1
0
Fork 0
mirror of https://github.com/pnx/tree-sitter-dotenv synced 2026-10-14 12:06:44 +02:00

fix: parse variable references in unquoted strings

This commit is contained in:
Henrik Hautakoski 2026-09-26 13:04:39 +02:00
parent a16f203ba0
commit 90f212b3ea
5 changed files with 1171 additions and 582 deletions

View file

@ -45,10 +45,12 @@ module.exports = grammar({
comment: _ => /\#[^\n]*/, comment: _ => /\#[^\n]*/,
identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/, identifier: _ => /[A-Za-z_][A-Za-z0-9_]*/,
variable: $ => seq('$', choice( variable: $ => choice(
$.identifier, seq('$', $.identifier),
seq('{', $.identifier, '}') $._braced_variable,
)), ),
_braced_variable: $ => seq('$', '{', $.identifier, '}'),
_value: $ => choice( _value: $ => choice(
$.string, $.string,
@ -66,7 +68,35 @@ module.exports = grammar({
$._unquoted_string, $._unquoted_string,
), ),
_unquoted_string: $ => alias(/[^\#\s\"\'\$]+(?:[ \t]+[^\#\s\"\'\$]+)+/, $.string_content), _unquoted_string: $ => choice(
alias($._unquoted_content, $.string_content),
seq(
choice(
alias($.value, $.string_content),
alias($.boolean, $.string_content),
alias($.decimal, $.string_content),
alias($.hexadecimal, $.string_content),
alias($.float, $.string_content),
alias($._unquoted_content, $.string_content),
),
alias($._braced_variable, $.variable),
optional(choice(
alias($.value, $.string_content),
alias($._unquoted_content, $.string_content),
)),
),
),
_unquoted_content: _ => token(choice(
/[^\#\s\"\'\$]+(?:[ \t]+[^\#\s\"\'\$]+)+/,
seq(
/[^\#\s\"\'\$]+(?:[ \t]+[^\#\s\"\'\$]+)*/,
repeat1(seq(
/\$[A-Za-z_][A-Za-z0-9_]*/,
optional(/(?:[ \t]*[^\#\s\"\'\$]+)+/),
)),
),
)),
_literal_string: $ => seq( _literal_string: $ => seq(
"'", "'",

222
src/grammar.json generated
View file

@ -143,6 +143,28 @@
"value": "[A-Za-z_][A-Za-z0-9_]*" "value": "[A-Za-z_][A-Za-z0-9_]*"
}, },
"variable": { "variable": {
"type": "CHOICE",
"members": [
{
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "$"
},
{
"type": "SYMBOL",
"name": "identifier"
}
]
},
{
"type": "SYMBOL",
"name": "_braced_variable"
}
]
},
"_braced_variable": {
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@ -150,30 +172,16 @@
"value": "$" "value": "$"
}, },
{ {
"type": "CHOICE", "type": "STRING",
"members": [ "value": "{"
{ },
"type": "SYMBOL", {
"name": "identifier" "type": "SYMBOL",
}, "name": "identifier"
{ },
"type": "SEQ", {
"members": [ "type": "STRING",
{ "value": "}"
"type": "STRING",
"value": "{"
},
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "STRING",
"value": "}"
}
]
}
]
} }
] ]
}, },
@ -220,13 +228,167 @@
] ]
}, },
"_unquoted_string": { "_unquoted_string": {
"type": "ALIAS", "type": "CHOICE",
"members": [
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_unquoted_content"
},
"named": true,
"value": "string_content"
},
{
"type": "SEQ",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "value"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "boolean"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "decimal"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "hexadecimal"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "float"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_unquoted_content"
},
"named": true,
"value": "string_content"
}
]
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_braced_variable"
},
"named": true,
"value": "variable"
},
{
"type": "CHOICE",
"members": [
{
"type": "CHOICE",
"members": [
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "value"
},
"named": true,
"value": "string_content"
},
{
"type": "ALIAS",
"content": {
"type": "SYMBOL",
"name": "_unquoted_content"
},
"named": true,
"value": "string_content"
}
]
},
{
"type": "BLANK"
}
]
}
]
}
]
},
"_unquoted_content": {
"type": "TOKEN",
"content": { "content": {
"type": "PATTERN", "type": "CHOICE",
"value": "[^\\#\\s\\\"\\'\\$]+(?:[ \\t]+[^\\#\\s\\\"\\'\\$]+)+" "members": [
}, {
"named": true, "type": "PATTERN",
"value": "string_content" "value": "[^\\#\\s\\\"\\'\\$]+(?:[ \\t]+[^\\#\\s\\\"\\'\\$]+)+"
},
{
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "[^\\#\\s\\\"\\'\\$]+(?:[ \\t]+[^\\#\\s\\\"\\'\\$]+)*"
},
{
"type": "REPEAT1",
"content": {
"type": "SEQ",
"members": [
{
"type": "PATTERN",
"value": "\\$[A-Za-z_][A-Za-z0-9_]*"
},
{
"type": "CHOICE",
"members": [
{
"type": "PATTERN",
"value": "(?:[ \\t]*[^\\#\\s\\\"\\'\\$]+)+"
},
{
"type": "BLANK"
}
]
}
]
}
}
]
}
]
}
}, },
"_literal_string": { "_literal_string": {
"type": "SEQ", "type": "SEQ",

1248
src/parser.c generated

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,161 @@
================================================================================
Boolean prefixes become string content
================================================================================
PLAIN_TRUE=true
TRUE_PREFIX=true${APP_NAME}
PLAIN_FALSE=false
FALSE_PREFIX=false${APP_NAME}
--------------------------------------------------------------------------------
(document
(assignment
(identifier)
(boolean))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(boolean))
(assignment
(identifier)
(string
(string_content)
(variable (identifier)))))
================================================================================
Numeric prefixes become string content
================================================================================
PLAIN_INTEGER=123
INTEGER_PREFIX=123${APP_NAME}
PLAIN_NEGATIVE=-123
NEGATIVE_PREFIX=-123${APP_NAME}
PLAIN_HEX=0xFF
HEX_PREFIX=0xFF${APP_NAME}
PLAIN_FLOAT=1.5
FLOAT_PREFIX=1.5${APP_NAME}
PLAIN_NEGATIVE_FLOAT=-1.5
NEGATIVE_FLOAT_PREFIX=-1.5${APP_NAME}
--------------------------------------------------------------------------------
(document
(assignment
(identifier)
(number (integer (decimal))))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(number (integer (decimal))))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(number (integer (hexadecimal))))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(number (float)))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(number (float)))
(assignment
(identifier)
(string
(string_content)
(variable (identifier)))))
================================================================================
Scalar prefixes with suffixes and comments
================================================================================
BOOL=true${APP_NAME}-suffix # comment
NUMBER=123${APP_NAME} suffix words
--------------------------------------------------------------------------------
(document
(assignment
(identifier)
(string
(string_content)
(variable (identifier))
(string_content)))
(comment)
(assignment
(identifier)
(string
(string_content)
(variable (identifier))
(string_content))))
================================================================================
Bare boolean-prefixed references are literal strings
================================================================================
BOOL=true$APP_NAME
BOOL=false$APP_NAME
--------------------------------------------------------------------------------
(document
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content))))
================================================================================
Bare decimal-prefixed references are literal strings
================================================================================
NUMBER=123$APP_NAME
NUMBER=-123$APP_NAME
--------------------------------------------------------------------------------
(document
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content))))
================================================================================
Bare hexadecimal-prefixed references are literal strings
================================================================================
HEX=0xFF$APP_NAME
--------------------------------------------------------------------------------
(document
(assignment (identifier) (string (string_content))))
================================================================================
Bare float-prefixed references are literal strings
================================================================================
FLOAT=1.5$APP_NAME
FLOAT=-1.5$APP_NAME
--------------------------------------------------------------------------------
(document
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content))))

View file

@ -42,3 +42,85 @@ MULTIPLE = "variable is: ${VARIABLE}, and $ANOTHER !!!"
(variable (variable
(identifier)) (identifier))
(string_content)))) (string_content))))
================================================================================
Unquoted braced variables with prefixes
================================================================================
BASIC = prefix${VARIABLE}
VITE_APP_NAME=xxx${APP_NAME}
SUFFIX = prefix${VARIABLE}-suffix
MULTIWORD = prefix words${VARIABLE} suffix words # comment
--------------------------------------------------------------------------------
(document
(assignment
key: (identifier)
value: (string
(string_content)
(variable
(identifier))))
(assignment
key: (identifier)
value: (string
(string_content)
(variable
(identifier))))
(assignment
key: (identifier)
value: (string
(string_content)
(variable
(identifier))
(string_content)))
(assignment
key: (identifier)
value: (string
(string_content)
(variable
(identifier))
(string_content)))
(comment))
================================================================================
Unquoted bare references with prefixes are literal strings
================================================================================
VITE_APP_NAME=xxx$APP_NAME
NAME=XXX$APP_NAME
SUFFIX=xxx$APP_NAME-suffix
MULTIPLE=xxx$APP_NAME$OTHER
MULTIWORD=prefix words$APP_NAME suffix words # comment
--------------------------------------------------------------------------------
(document
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content)))
(assignment (identifier) (string (string_content)))
(comment))
================================================================================
Literal bare references alongside braced interpolation
================================================================================
PREFIX=xxx$LITERAL${APP_NAME}
SUFFIX=xxx${APP_NAME}suffix$LITERAL
--------------------------------------------------------------------------------
(document
(assignment
(identifier)
(string
(string_content)
(variable (identifier))))
(assignment
(identifier)
(string
(string_content)
(variable (identifier))
(string_content))))