fix: precedence of unary operators and expressions

Signed-off-by: Michael Hoffmann <mhoffm@posteo.de>
This commit is contained in:
Michael Hoffmann
2025-03-30 13:33:58 +02:00
parent 009def4ae3
commit 28e327cd3f
15 changed files with 7189 additions and 6812 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

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

@@ -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,6 +206,9 @@
"name": "operation" "name": "operation"
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -215,8 +220,12 @@
"name": "index" "name": "index"
} }
] ]
}
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -228,8 +237,12 @@
"name": "get_attr" "name": "get_attr"
} }
] ]
}
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -241,6 +254,7 @@
"name": "splat" "name": "splat"
} }
] ]
}
}, },
{ {
"type": "SEQ", "type": "SEQ",

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

@@ -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, ")"),
), ),

16
package-lock.json generated
View File

@@ -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",

4
package.json generated
View File

@@ -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": {

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"
'';
} }

18
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,6 +206,9 @@
"name": "operation" "name": "operation"
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -215,8 +220,12 @@
"name": "index" "name": "index"
} }
] ]
}
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -228,8 +237,12 @@
"name": "get_attr" "name": "get_attr"
} }
] ]
}
}, },
{ {
"type": "PREC_RIGHT",
"value": 8,
"content": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -241,6 +254,7 @@
"name": "splat" "name": "splat"
} }
] ]
}
}, },
{ {
"type": "SEQ", "type": "SEQ",

6827
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,6 +390,14 @@ resource "azurerm_storage_blob" "proxy_cert" {
(binary_operation (binary_operation
(operation (operation
(binary_operation (binary_operation
(variable_expr
(identifier))
(get_attr
(identifier))
(literal_value
(string_lit
(quoted_template_start)
(quoted_template_end)))))
(operation (operation
(binary_operation (binary_operation
(variable_expr (variable_expr
@@ -399,15 +407,7 @@ resource "azurerm_storage_blob" "proxy_cert" {
(literal_value (literal_value
(string_lit (string_lit
(quoted_template_start) (quoted_template_start)
(quoted_template_end))))) (quoted_template_end))))))))
(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)))