Compare commits

...

10 Commits

Author SHA1 Message Date
51698afe89 add readme 2025-10-06 16:51:08 -06:00
6b3300744d add jinja to examples 2025-10-06 16:49:08 -06:00
dd5cf14b70 treat jinja as comments in tf 2025-10-06 16:36:21 -06:00
169afdafda treat jinja as comments in hcl 2025-10-06 16:25:16 -06:00
257abf0c72 add tf example 2025-10-06 16:21:00 -06:00
Michael Hoffmann
fad991865f changelog: bump to 1.2.0
Signed-off-by: Michael Hoffmann <mhoffmann@cloudflare.com>
2025-06-16 15:47:02 +03:00
ObserverOfTime
cb0e322d6f ci: publish to pypi 2025-06-15 22:15:34 +03:00
ObserverOfTime
09d38ff794 fix(node): set access to public 2025-06-15 22:15:34 +03:00
ObserverOfTime
dffd961034 feat: enable go & python bindings 2025-06-15 22:15:34 +03:00
Michael Hoffmann
28e327cd3f fix: precedence of unary operators and expressions
Signed-off-by: Michael Hoffmann <mhoffm@posteo.de>
2025-06-09 21:40:11 +02:00
35 changed files with 11911 additions and 11148 deletions

View File

@@ -38,18 +38,11 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up tree-sitter - name: Set up tree-sitter
uses: tree-sitter/setup-action/cli@v1 uses: tree-sitter/setup-action/cli@v1
- name: Regenerate with ABI 14
# TODO: remove when node & swift support ABI 15
run: |-
tree-sitter generate --abi=14
cd dialects/terraform
tree-sitter generate --abi=14
- name: Run parser and binding tests - name: Run parser and binding tests
uses: tree-sitter/parser-test-action@v2 uses: tree-sitter/parser-test-action@v2
with: with:
test-node: true test-node: true
test-rust: ${{runner.os == 'Linux'}} test-rust: ${{runner.os == 'Linux'}}
test-swift: ${{runner.os == 'macOS'}}
- name: Parse sample files - name: Parse sample files
uses: tree-sitter/parse-action@v4 uses: tree-sitter/parse-action@v4
id: parse-files id: parse-files

View File

@@ -20,9 +20,19 @@ jobs:
attestations: true attestations: true
npm: npm:
uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main uses: tree-sitter/workflows/.github/workflows/package-npm.yml@main
with:
generate: true
abi-version: 14
secrets: secrets:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
crates: crates:
uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main uses: tree-sitter/workflows/.github/workflows/package-crates.yml@main
secrets: secrets:
CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}} CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_TOKEN}}
pypi:
uses: tree-sitter/workflows/.github/workflows/package-pypi.yml@main
with:
generate: true
abi-version: 14
secrets:
PYPI_API_TOKEN: ${{secrets.PYPI_TOKEN}}

View File

@@ -1,5 +1,17 @@
# Changelog # Changelog
## 1.2.0 - 2025-06-15
feat:
* namespaced identitifers
fix:
* precedence of unary operators and expressions
housekeeping:
* regenerate with tree-sitter-cli 0.25.6
* add bindings
## 1.1.0 - 2023-07-25 ## 1.1.0 - 2023-07-25
feature feature

2
CMakeLists.txt generated
View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
project(tree-sitter-hcl project(tree-sitter-hcl
VERSION "1.1.0" VERSION "1.2.0"
DESCRIPTION "HCL and terraform grammar for tree-sitter" DESCRIPTION "HCL and terraform grammar for tree-sitter"
HOMEPAGE_URL "https://github.com/tree-sitter-grammars/tree-sitter-hcl" HOMEPAGE_URL "https://github.com/tree-sitter-grammars/tree-sitter-hcl"
LANGUAGES C) LANGUAGES C)

2
Cargo.toml generated
View File

@@ -1,7 +1,7 @@
[package] [package]
name = "tree-sitter-hcl" name = "tree-sitter-hcl"
description = "HCL and terraform grammar for tree-sitter" description = "HCL and terraform grammar for tree-sitter"
version = "1.1.0" version = "1.2.0"
license = "Apache-2.0" license = "Apache-2.0"
readme = "README.md" readme = "README.md"
keywords = ["incremental", "parsing", "tree-sitter", "hcl"] keywords = ["incremental", "parsing", "tree-sitter", "hcl"]

2
Makefile generated
View File

@@ -4,7 +4,7 @@ endif
LANGUAGE_NAME := tree-sitter-hcl LANGUAGE_NAME := tree-sitter-hcl
HOMEPAGE_URL := https://github.com/tree-sitter-grammars/tree-sitter-hcl HOMEPAGE_URL := https://github.com/tree-sitter-grammars/tree-sitter-hcl
VERSION := 1.1.0 VERSION := 1.2.0
# repository # repository
SRC_DIR := src SRC_DIR := src

8
Package.resolved generated
View File

@@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/tree-sitter/swift-tree-sitter", "repositoryURL": "https://github.com/tree-sitter/swift-tree-sitter",
"state": { "state": {
"branch": null, "branch": null,
"revision": "36aa61d1b531f744f35229f010efba9c6d6cbbdd", "revision": "08ef81eb8620617b55b08868126707ad72bf754f",
"version": "0.9.0" "version": "0.25.0"
} }
}, },
{ {
@@ -15,8 +15,8 @@
"repositoryURL": "https://github.com/tree-sitter/tree-sitter", "repositoryURL": "https://github.com/tree-sitter/tree-sitter",
"state": { "state": {
"branch": null, "branch": null,
"revision": "d97db6d63507eb62c536bcb2c4ac7d70c8ec665e", "revision" : "460118b4c82318b083b4d527c9c750426730f9c0",
"version": "0.23.2" "version" : "0.25.5"
} }
} }
] ]

3
Package.swift generated
View File

@@ -4,11 +4,12 @@ import PackageDescription
let package = Package( let package = Package(
name: "TreeSitterHCL", name: "TreeSitterHCL",
platforms: [.macOS(.v10_13), .iOS(.v11)],
products: [ products: [
.library(name: "TreeSitterHCL", targets: ["TreeSitterHCL"]), .library(name: "TreeSitterHCL", targets: ["TreeSitterHCL"]),
], ],
dependencies: [ dependencies: [
.package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.9.0"), .package(name: "SwiftTreeSitter", url: "https://github.com/tree-sitter/swift-tree-sitter", from: "0.25.0"),
], ],
targets: [ targets: [
.target( .target(

View File

@@ -1,39 +1,40 @@
# tree-sitter-hcl HCL and Terraform parsers that treat `{%` as comment lines for... reasons. iykyk 🤷
tree-sitter grammar for the [HCL](https://github.com/hashicorp/hcl/blob/main/hclsyntax/spec.md) language # Neovim usage with the `nvim-treesitter` plugin
## Try It Out ```lua
local configs = require("nvim-treesitter.configs")
local parser_config = require("nvim-treesitter.parsers").get_parser_configs()
Try the parser in the [playground](https://tree-sitter-grammars.github.io/tree-sitter-hcl/) local jhcl_repo = "https://code.buildbetter.boats/jenterkin/tree-sitter-jhcl"
## Example parser_config.hcl = {
install_info = {
url = jhcl_repo,
files = { "src/parser.c", "src/scanner.c" },
}
}
Highlighting `example/example.hcl`: parser_config.terraform = {
install_info = {
url = jhcl_repo,
files = { "src/parser.c", "src/scanner.c" },
location = "dialects/terraform",
}
}
![Highlighting Example](https://i.imgur.com/yUJ0ybK.png) configs.setup({
-- ...
## Developing -- We overwrote the default `nvim-treesitter` configs for `hcl` and
-- `terraform` above, so we can include them in `ensure_installed` normally
ensure_installed = {
-- ...
"hcl",
"terraform",
-- ...
},
It is recommended to use `nix` to fulfill all development dependencies. To activate the development environment simply run `nix-shell` in the project root. -- ...
})
## Running Tests
To run tests simply run `nix-shell --run 'tree-sitter test'`.
## Compliance
The directory `example/real_world_stuff` contains a corpus of hcl files that I found with the github query `language:HCL` for users `coreos`, `hashicorp`, `oracle` and `terraform-community-modules`.
```bash
tree-sitter parse --quiet --stat example/real_world_stuff/*/*
Total parses: 1892; successful parses: 1892; failed parses: 0; success percentage: 100.00%
``` ```
## Fuzzing
See the [fuzzing repo for this parser](https://github.com/MichaHoffmann/tree-sitter-hcl-fuzz)
## Attributions
Pages were copied from https://github.com/m-novikov/tree-sitter-sql

15
bindings/go/binding.go generated Normal file
View File

@@ -0,0 +1,15 @@
package tree_sitter_hcl
// #cgo CFLAGS: -std=c11 -fPIC
// #include "../../src/parser.c"
// #if __has_include("../../src/scanner.c")
// #include "../../src/scanner.c"
// #endif
import "C"
import "unsafe"
// Get the tree-sitter Language for this grammar.
func Language() unsafe.Pointer {
return unsafe.Pointer(C.tree_sitter_hcl())
}

15
bindings/go/binding_test.go generated Normal file
View File

@@ -0,0 +1,15 @@
package tree_sitter_hcl_test
import (
"testing"
tree_sitter "github.com/tree-sitter/go-tree-sitter"
tree_sitter_hcl "github.com/tree-sitter-grammars/tree-sitter-hcl/bindings/go"
)
func TestCanLoadGrammar(t *testing.T) {
language := tree_sitter.NewLanguage(tree_sitter_hcl.Language())
if language == nil {
t.Errorf("Error loading HCL grammar")
}
}

12
bindings/python/tests/test_binding.py generated Normal file
View File

@@ -0,0 +1,12 @@
from unittest import TestCase
import tree_sitter
import tree_sitter_hcl
class TestLanguage(TestCase):
def test_can_load_grammar(self):
try:
tree_sitter.Language(tree_sitter_hcl.language())
except Exception:
self.fail("Error loading HCL grammar")

View File

@@ -0,0 +1,5 @@
"""HCL and terraform grammar for tree-sitter"""
from ._binding import language
__all__ = ["language"]

View File

@@ -0,0 +1 @@
def language() -> object: ...

View File

@@ -0,0 +1,35 @@
#include <Python.h>
typedef struct TSLanguage TSLanguage;
TSLanguage *tree_sitter_hcl(void);
static PyObject* _binding_language(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) {
return PyCapsule_New(tree_sitter_hcl(), "tree_sitter.Language", NULL);
}
static struct PyModuleDef_Slot slots[] = {
#ifdef Py_GIL_DISABLED
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
#endif
{0, NULL}
};
static PyMethodDef methods[] = {
{"language", _binding_language, METH_NOARGS,
"Get the tree-sitter language for this grammar."},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "_binding",
.m_doc = NULL,
.m_size = 0,
.m_methods = methods,
.m_slots = slots,
};
PyMODINIT_FUNC PyInit__binding(void) {
return PyModuleDef_Init(&module);
}

View File

View File

@@ -124,7 +124,8 @@
"members": [ "members": [
{ {
"type": "PATTERN", "type": "PATTERN",
"value": "\\p{ID_Start}" "value": "\\p{ID_Start}",
"flags": "u"
}, },
{ {
"type": "STRING", "type": "STRING",
@@ -139,7 +140,8 @@
"members": [ "members": [
{ {
"type": "PATTERN", "type": "PATTERN",
"value": "\\p{ID_Continue}" "value": "\\p{ID_Continue}",
"flags": "u"
}, },
{ {
"type": "STRING", "type": "STRING",
@@ -204,43 +206,55 @@
"name": "operation" "name": "operation"
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "index" },
} {
] "type": "SYMBOL",
"name": "index"
}
]
}
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "get_attr" },
} {
] "type": "SYMBOL",
"name": "get_attr"
}
]
}
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "splat" },
} {
] "type": "SYMBOL",
"name": "splat"
}
]
}
}, },
{ {
"type": "SEQ", "type": "SEQ",
@@ -1681,6 +1695,19 @@
"content": { "content": {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "{%"
},
{
"type": "PATTERN",
"value": ".*"
}
]
},
{ {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [

File diff suppressed because it is too large Load Diff

View File

@@ -129,7 +129,7 @@ static unsigned serialize(Scanner *scanner, char *buf) {
memcpy(&buf[size], &(scanner->context_stack.len), sizeof(uint32_t)); memcpy(&buf[size], &(scanner->context_stack.len), sizeof(uint32_t));
size += sizeof(uint32_t); size += sizeof(uint32_t);
for (int i = 0; i < scanner->context_stack.len; i++) { for (uint32_t i = 0; i < scanner->context_stack.len; i++) {
Context *context = &scanner->context_stack.data[i]; Context *context = &scanner->context_stack.data[i];
if (size + sizeof(ContextType) + sizeof(uint32_t) + context->heredoc_identifier.len >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) { if (size + sizeof(ContextType) + sizeof(uint32_t) + context->heredoc_identifier.len >= TREE_SITTER_SERIALIZATION_BUFFER_SIZE) {
return 0; return 0;
@@ -413,7 +413,7 @@ bool tree_sitter_terraform_external_scanner_scan(void *payload, TSLexer *lexer,
void tree_sitter_terraform_external_scanner_destroy(void *payload) { void tree_sitter_terraform_external_scanner_destroy(void *payload) {
Scanner *scanner = (Scanner *)payload; Scanner *scanner = (Scanner *)payload;
for (int i = 0; i < scanner->context_stack.len; i++) { for (uint32_t i = 0; i < scanner->context_stack.len; i++) {
STRING_FREE(scanner->context_stack.data[i].heredoc_identifier); STRING_FREE(scanner->context_stack.data[i].heredoc_identifier);
} }
VEC_FREE(scanner->context_stack); VEC_FREE(scanner->context_stack);

View File

@@ -1,3 +1,4 @@
{% test %}
resource "example" "literals" { resource "example" "literals" {
attr1 = "val1" attr1 = "val1"
tupl1 = [ 1, 2, 3.4, "foo" ] tupl1 = [ 1, 2, 3.4, "foo" ]

96
example/example.tf Normal file
View File

@@ -0,0 +1,96 @@
{% test %}
resource "example" "literals" {
attr1 = "val1"
tupl1 = [ 1, 2, 3.4, "foo" ]
tupl2 = []
obj1 = { foo = "bar", baz = quz }
null1 = null
bool1 = true
bool2 = false
esc1 = "\" \t \UFF11FF22 \uFFFF \n"
esc2 = "$${} %%{}"
num1 = 2
num2 = 2.112
num3 = 2.112e-12
num4 = 2.112e+12
num5 = 2.112E+12
num6 = 2.112E-12
num7 = 0x21FF
}
resource "example" "comments" {
// comment
# comment
/*
comment
*/
}
resource "example" "splat_expressions" {
splat1 = foo.*.bar.baz[0]
splat2 = foo[*].bar.baz[0]
}
resource "example" "for_expressions" {
for1 = { for i, v in ["a", "a", "b"] : v => i... }
for2 = [ for k, v in x : "${k}-${v}" ]
for3 = { for k, v in x: k => v }
for4 = [ for v in x : v ]
for5 = { for v in x : v => v }
for6 = [ for v in x : v if v < 3 ]
}
resource "example" "function_expressions" {
func1 = is_number("123")
func2 = multiline(
arg1,
arg2,
arg3...
)
func3 = withobject({
"foo" : 2,
"bar" : baz,
key : val,
fizz : buzz,
})
}
resource "example" "binary_expressions" {
cond1 = (1 == 2) ? 1 : "foobar"
bin1 = ((1+2)%3)*4
}
resource "example" "template_expressions" {
tpl1 = "prefix-${var.bar}"
tpl2 = "prefix-${func("bar")}"
tpl3 = "prefix-${func("nested-${var.bar}")}"
tpl4 = <<EOF
%{ for a in f(b) ~}
${func("foo${ a }")}
%{ endfor ~}
EOF
tpl5 = <<-EOF
%{~if cond~}
"foo"
%{~else~}
4
%{~endif~}
EOF
tpl6 = <<-EOF
%{ for a in f(b) ~}
%{~if a~} "true" %{~else~} "false" %{~endif~}
%{ endfor ~}
EOF
}
resource "example" "nested_blocks" {
nested_block "first" {
attr1 = "foo"
nested_block "second" {
attr1 = "bar"
}
}
}

7
go.mod generated Normal file
View File

@@ -0,0 +1,7 @@
module github.com/tree-sitter-grammars/tree-sitter-hcl
go 1.22
require github.com/tree-sitter/go-tree-sitter v0.24.0
require github.com/mattn/go-pointer v0.0.1 // indirect

36
go.sum generated Normal file
View File

@@ -0,0 +1,36 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tree-sitter/go-tree-sitter v0.24.0 h1:kRZb6aBNfcI/u0Qh8XEt3zjNVnmxTisDBN+kXK0xRYQ=
github.com/tree-sitter/go-tree-sitter v0.24.0/go.mod h1:x681iFVoLMEwOSIHA1chaLkXlroXEN7WY+VHGFaoDbk=
github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb h1:A8425heRM8mylnv4H58FPUiH+aYivyitre0PzxrfmWs=
github.com/tree-sitter/tree-sitter-c v0.21.5-0.20240818205408-927da1f210eb/go.mod h1:dOF6gtQiF9UwNh995T5OphYmtIypkjsp3ap7r9AN/iA=
github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148 h1:AfFPZwtwGN01BW1jDdqBVqscTwetvMpydqYZz57RSlc=
github.com/tree-sitter/tree-sitter-cpp v0.22.4-0.20240818224355-b1a4e2b25148/go.mod h1:Bh6U3viD57rFXRYIQ+kmiYtr+1Bx0AceypDLJJSyi9s=
github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33 h1:TwqSV3qLp3tKSqirGLRHnjFk9Tc2oy57LIl+FQ4GjI4=
github.com/tree-sitter/tree-sitter-embedded-template v0.21.1-0.20240819044651-ffbf64942c33/go.mod h1:CvCKCt3v04Ufos1zZnNCelBDeCGRpPucaN8QczoUsN4=
github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012 h1:Xvxck3tE5FW7F7bTS97iNM2ADMyCMJztVqn5HYKdJGo=
github.com/tree-sitter/tree-sitter-go v0.21.3-0.20240818010209-8c0f0e7a6012/go.mod h1:T40D0O1cPvUU/+AmiXVXy1cncYQT6wem4Z0g4SfAYvY=
github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0 h1:c46K6uh5Dz00zJeU9BfjXdb8I+E4RkUdfnWJpQADXFo=
github.com/tree-sitter/tree-sitter-html v0.20.5-0.20240818004741-d11201a263d0/go.mod h1:hcNt/kOJHcIcuMvouE7LJcYdeFUFbVpBJ6d4wmOA+tU=
github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495 h1:jrt4qbJVEFs4H93/ITxygHc6u0TGqAkkate7TQ4wFSA=
github.com/tree-sitter/tree-sitter-java v0.21.1-0.20240824015150-576d8097e495/go.mod h1:oyaR7fLnRV0hT9z6qwE9GkaeTom/hTDwK3H2idcOJFc=
github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5 h1:om4X9AVg3asL8gxNJDcz4e/Wp+VpQj1PY3uJXKr6EOg=
github.com/tree-sitter/tree-sitter-javascript v0.21.5-0.20240818005344-15887341e5b5/go.mod h1:nNqgPoV/h9uYWk6kYEFdEAhNVOacpfpRW5SFmdaP4tU=
github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5 h1:pfV3G3k7NCKqKk8THBmyuh2zA33lgYHS3GVrzRR8ry4=
github.com/tree-sitter/tree-sitter-json v0.21.1-0.20240818005659-bdd69eb8c8a5/go.mod h1:GbMKRjLfk0H+PI7nLi1Sx5lHf5wCpLz9al8tQYSxpEk=
github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1 h1:ZXZMDwE+IhUtGug4Brv6NjJWUU3rfkZBKpemf6RY8/g=
github.com/tree-sitter/tree-sitter-php v0.22.9-0.20240819002312-a552625b56c1/go.mod h1:UKCLuYnJ312Mei+3cyTmGOHzn0YAnaPRECgJmHtzrqs=
github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb h1:EXEM82lFM7JjJb6qiKZXkpIDaCcbV2obNn82ghwj9lw=
github.com/tree-sitter/tree-sitter-python v0.21.1-0.20240818005537-55a9b8a4fbfb/go.mod h1:lXCF1nGG5Dr4J3BTS0ObN4xJCCICiSu/b+Xe/VqMV7g=
github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d h1:fcYCvoXdcP1uRQYXqJHRy6Hec+uKScQdKVtMwK9JeCI=
github.com/tree-sitter/tree-sitter-ruby v0.21.1-0.20240818211811-7dbc1e2d0e2d/go.mod h1:T1nShQ4v5AJtozZ8YyAS4uzUtDAJj/iv4YfwXSbUHzg=
github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447 h1:o9alBu1J/WjrcTKEthYtXmdkDc5OVXD+PqlvnEZ0Lzc=
github.com/tree-sitter/tree-sitter-rust v0.21.3-0.20240818005432-2b43eafe6447/go.mod h1:1Oh95COkkTn6Ezp0vcMbvfhRP5gLeqqljR0BYnBzWvc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -4,6 +4,8 @@
/** @param {string} dialect */ /** @param {string} dialect */
module.exports = function make_grammar(dialect) { module.exports = function make_grammar(dialect) {
const PREC = { const PREC = {
// unary negation should not pull expressions apart
expr: 8,
unary: 7, unary: 7,
binary_mult: 6, binary_mult: 6,
binary_add: 5, binary_add: 5,
@@ -75,9 +77,9 @@ module.exports = function make_grammar(dialect) {
$.function_call, $.function_call,
$.for_expr, $.for_expr,
$.operation, $.operation,
seq($._expr_term, $.index), prec.right(PREC.expr, seq($._expr_term, $.index)),
seq($._expr_term, $.get_attr), prec.right(PREC.expr, seq($._expr_term, $.get_attr)),
seq($._expr_term, $.splat), prec.right(PREC.expr, seq($._expr_term, $.splat)),
seq("(", $.expression, ")"), seq("(", $.expression, ")"),
), ),
@@ -347,6 +349,7 @@ module.exports = function make_grammar(dialect) {
comment: ($) => comment: ($) =>
token( token(
choice( choice(
seq("{%", /.*/),
seq("#", /.*/), seq("#", /.*/),
seq("//", /.*/), seq("//", /.*/),
seq("/*", /[^*]*\*+([^/*][^*]*\*+)*/, "/"), seq("/*", /[^*]*\*+([^/*][^*]*\*+)*/, "/"),

20
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "@tree-sitter-grammars/tree-sitter-hcl", "name": "@tree-sitter-grammars/tree-sitter-hcl",
"version": "1.1.0", "version": "1.2.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@tree-sitter-grammars/tree-sitter-hcl", "name": "@tree-sitter-grammars/tree-sitter-hcl",
"version": "1.1.0", "version": "1.2.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -15,10 +15,10 @@
}, },
"devDependencies": { "devDependencies": {
"prebuildify": "^6.0.1", "prebuildify": "^6.0.1",
"tree-sitter-cli": "^0.25.3" "tree-sitter-cli": "^0.25.6"
}, },
"peerDependencies": { "peerDependencies": {
"tree-sitter": "^0.22.4" "tree-sitter": "^0.25.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"tree-sitter": { "tree-sitter": {
@@ -338,9 +338,9 @@
} }
}, },
"node_modules/tree-sitter": { "node_modules/tree-sitter": {
"version": "0.22.4", "version": "0.25.0",
"resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.22.4.tgz", "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.25.0.tgz",
"integrity": "sha512-usbHZP9/oxNsUY65MQUsduGRqDHQOou1cagUSwjhoSYAmSahjQDAVsh9s+SlZkn8X8+O1FULRGwHu7AFP3kjzg==", "integrity": "sha512-PGZZzFW63eElZJDe/b/R/LbsjDDYJa5UEjLZJB59RQsMX+fo0j54fqBPn1MGKav/QNa0JR0zBiVaikYDWCj5KQ==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
@@ -351,9 +351,9 @@
} }
}, },
"node_modules/tree-sitter-cli": { "node_modules/tree-sitter-cli": {
"version": "0.25.3", "version": "0.25.6",
"resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.25.3.tgz", "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.25.6.tgz",
"integrity": "sha512-Bk6ZUXG+cKnwZpfR/te4NDrKld90p6350eqWlbLwSpV9/8vmL/x8LCw+3k7quY9oMDaYoMXHMvokXJbkM5A7bA==", "integrity": "sha512-UhkXRkMPtBgE4OatZtYVtDsT3HFUliqAJcs49XQaZv8d2sbeTzEhpJVpMaCqBR3HGhb1WpyoodaFXQaMuOLPEg==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",

9
package.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "@tree-sitter-grammars/tree-sitter-hcl", "name": "@tree-sitter-grammars/tree-sitter-hcl",
"version": "1.1.0", "version": "1.2.0",
"description": "HCL and terraform grammar for tree-sitter", "description": "HCL and terraform grammar for tree-sitter",
"repository": "https://github.com/tree-sitter-grammars/tree-sitter-hcl", "repository": "https://github.com/tree-sitter-grammars/tree-sitter-hcl",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -30,10 +30,10 @@
}, },
"devDependencies": { "devDependencies": {
"prebuildify": "^6.0.1", "prebuildify": "^6.0.1",
"tree-sitter-cli": "^0.25.3" "tree-sitter-cli": "^0.25.6"
}, },
"peerDependencies": { "peerDependencies": {
"tree-sitter": "^0.22.4" "tree-sitter": "^0.25.0"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"tree-sitter": { "tree-sitter": {
@@ -46,5 +46,8 @@
"prestart": "tree-sitter build --wasm", "prestart": "tree-sitter build --wasm",
"start": "tree-sitter playground", "start": "tree-sitter playground",
"test": "node --test bindings/node/*_test.js" "test": "node --test bindings/node/*_test.js"
},
"publishConfig": {
"access": "public"
} }
} }

28
pyproject.toml generated Normal file
View File

@@ -0,0 +1,28 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "tree-sitter-hcl"
description = "HCL and terraform grammar for tree-sitter"
version = "1.2.0"
keywords = ["incremental", "parsing", "tree-sitter", "hcl"]
classifiers = [
"Intended Audience :: Developers",
"Topic :: Software Development :: Compilers",
"Topic :: Text Processing :: Linguistic",
"Typing :: Typed",
]
requires-python = ">=3.10"
license.text = "Apache-2.0"
readme = "README.md"
[project.urls]
Homepage = "https://github.com/tree-sitter-grammars/tree-sitter-hcl"
[project.optional-dependencies]
core = ["tree-sitter~=0.24"]
[tool.cibuildwheel]
build = "cp310-*"
build-frontend = "build"

77
setup.py generated Normal file
View File

@@ -0,0 +1,77 @@
from os import path
from platform import system
from sysconfig import get_config_var
from setuptools import Extension, find_packages, setup
from setuptools.command.build import build
from setuptools.command.egg_info import egg_info
from wheel.bdist_wheel import bdist_wheel
sources = [
"bindings/python/tree_sitter_hcl/binding.c",
"src/parser.c",
]
if path.exists("src/scanner.c"):
sources.append("src/scanner.c")
macros: list[tuple[str, str | None]] = [
("PY_SSIZE_T_CLEAN", None),
("TREE_SITTER_HIDE_SYMBOLS", None),
]
if limited_api := not get_config_var("Py_GIL_DISABLED"):
macros.append(("Py_LIMITED_API", "0x030A0000"))
if system() != "Windows":
cflags = ["-std=c11", "-fvisibility=hidden"]
else:
cflags = ["/std:c11", "/utf-8"]
class Build(build):
def run(self):
if path.isdir("queries"):
dest = path.join(self.build_lib, "tree_sitter_hcl", "queries")
self.copy_tree("queries", dest)
super().run()
class BdistWheel(bdist_wheel):
def get_tag(self):
python, abi, platform = super().get_tag()
if python.startswith("cp"):
python, abi = "cp310", "abi3"
return python, abi, platform
class EggInfo(egg_info):
def find_sources(self):
super().find_sources()
self.filelist.recursive_include("queries", "*.scm")
self.filelist.include("src/tree_sitter/*.h")
setup(
packages=find_packages("bindings/python"),
package_dir={"": "bindings/python"},
package_data={
"tree_sitter_hcl": ["*.pyi", "py.typed"],
"tree_sitter_hcl.queries": ["*.scm"],
},
ext_package="tree_sitter_hcl",
ext_modules=[
Extension(
name="_binding",
sources=sources,
extra_compile_args=cflags,
define_macros=macros,
include_dirs=["src"],
py_limited_api=limited_api,
)
],
cmdclass={
"build": Build,
"bdist_wheel": BdistWheel,
"egg_info": EggInfo,
},
zip_safe=False
)

View File

@@ -7,8 +7,11 @@ pkgs.mkShell {
gdb gdb
valgrind valgrind
nodejs nodejs
tree-sitter
emscripten emscripten
]; ];
shellHook = ''
npm install
export PATH="$(git rev-parse --show-toplevel)/node_modules/.bin:$PATH"
'';
} }

97
src/grammar.json generated
View File

@@ -124,7 +124,8 @@
"members": [ "members": [
{ {
"type": "PATTERN", "type": "PATTERN",
"value": "\\p{ID_Start}" "value": "\\p{ID_Start}",
"flags": "u"
}, },
{ {
"type": "STRING", "type": "STRING",
@@ -139,7 +140,8 @@
"members": [ "members": [
{ {
"type": "PATTERN", "type": "PATTERN",
"value": "\\p{ID_Continue}" "value": "\\p{ID_Continue}",
"flags": "u"
}, },
{ {
"type": "STRING", "type": "STRING",
@@ -204,43 +206,55 @@
"name": "operation" "name": "operation"
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "index" },
} {
] "type": "SYMBOL",
"name": "index"
}
]
}
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "get_attr" },
} {
] "type": "SYMBOL",
"name": "get_attr"
}
]
}
}, },
{ {
"type": "SEQ", "type": "PREC_RIGHT",
"members": [ "value": 8,
{ "content": {
"type": "SYMBOL", "type": "SEQ",
"name": "_expr_term" "members": [
}, {
{ "type": "SYMBOL",
"type": "SYMBOL", "name": "_expr_term"
"name": "splat" },
} {
] "type": "SYMBOL",
"name": "splat"
}
]
}
}, },
{ {
"type": "SEQ", "type": "SEQ",
@@ -1681,6 +1695,19 @@
"content": { "content": {
"type": "CHOICE", "type": "CHOICE",
"members": [ "members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "{%"
},
{
"type": "PATTERN",
"value": ".*"
}
]
},
{ {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [

11124
src/parser.c generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
======
Unary operation on variable with splat expression
=====
foo = !var.bar
------
(config_file
(body
(attribute
(identifier)
(expression
(operation
(unary_operation
(variable_expr
(identifier))
(get_attr
(identifier))))))))
=====
Attribute as conditional expression
=====
foo = local.bar == local.baz
-----
(config_file
(body
(attribute
(identifier)
(expression
(operation
(binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(variable_expr
(identifier))
(get_attr
(identifier))))))))

View File

@@ -81,3 +81,48 @@ foo = a != b && c == d
(identifier)) (identifier))
(variable_expr (variable_expr
(identifier)))))))))) (identifier))))))))))
================================================================================
precedence in binary operators 2
================================================================================
foo = a.foo != "" && b.foo == "" ? 1 : 0
--------------------------------------------------------------------------------
(config_file
(body
(attribute
(identifier)
(expression
(conditional
(expression
(operation
(binary_operation
(operation
(binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(literal_value
(string_lit
(quoted_template_start)
(quoted_template_end)))))
(operation
(binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(literal_value
(string_lit
(quoted_template_start)
(quoted_template_end))))))))
(expression
(literal_value
(numeric_lit)))
(expression
(literal_value
(numeric_lit))))))))

View File

@@ -390,24 +390,24 @@ resource "azurerm_storage_blob" "proxy_cert" {
(binary_operation (binary_operation
(operation (operation
(binary_operation (binary_operation
(operation
(binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(literal_value
(string_lit
(quoted_template_start)
(quoted_template_end)))))
(variable_expr (variable_expr
(identifier)))) (identifier))
(get_attr (get_attr
(identifier)) (identifier))
(literal_value (literal_value
(string_lit (string_lit
(quoted_template_start) (quoted_template_start)
(quoted_template_end)))))) (quoted_template_end)))))
(operation
(binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(literal_value
(string_lit
(quoted_template_start)
(quoted_template_end))))))))
(expression (expression
(literal_value (literal_value
(numeric_lit))) (numeric_lit)))

View File

@@ -21,20 +21,11 @@
} }
], ],
"metadata": { "metadata": {
"version": "1.1.0", "version": "1.2.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"description": "HCL and terraform grammar for tree-sitter", "description": "HCL and terraform grammar for tree-sitter",
"links": { "links": {
"repository": "https://github.com/tree-sitter-grammars/tree-sitter-hcl" "repository": "https://github.com/tree-sitter-grammars/tree-sitter-hcl"
} }
},
"bindings": {
"c": true,
"go": false,
"node": true,
"python": false,
"rust": true,
"swift": true,
"zig": false
} }
} }