use dynamic precedence to fix 'is' and allow not not

This commit is contained in:
danhanson committed 2026-09-11 04:21:08 -05:00
1 parent 9701fab04a
commit b239bf6509
5 files changed
+40132 -39901

No files matched your search

+5 -1
View File
@@ -79,7 +79,11 @@ Consequences worth knowing before editing a rule:
fail at generate time; it shows up as `MISSING` nodes in a passing-looking parse. fail at generate time; it shows up as `MISSING` nodes in a passing-looking parse.
- **`conflicts` entries are the normal fix, not precedence.** With explicit separators the lookahead at a - **`conflicts` entries are the normal fix, not precedence.** With explicit separators the lookahead at a
part boundary is `SEP` instead of the meaningful token, so LR(1) loses its discriminator. That is why part boundary is `SEP` instead of the meaningful token, so LR(1) loses its discriminator. That is why
`predicate` and `[$.library, $.load]` are listed there. `predicate`, `binaryOperator` and `[$.library, $.load]` are listed there. Where two readings both stay
valid to the end — `is not` as one operator or as `is` and a prefix `not` — the conflict needs a
`prec.dynamic` alongside it to settle which wins; a static `prec` resolves the shift/reduce while the
table is built and discards the other parse outright, which is how a bare `{% if x is y %}` came to be
an error.
### A tag group is a sequence of clauses ### A tag group is a sequence of clauses
+5 -4
View File
@@ -359,6 +359,7 @@ const django = grammar({
[$.timezone_clause], [$.timezone_clause],
[$.with_clause], [$.with_clause],
[$.predicate], [$.predicate],
[$.binaryOperator],
[$.library, $.load], [$.library, $.load],
[$._translate_option], [$._translate_option],
[$._filtered_value_spaced], [$._filtered_value_spaced],
@@ -426,18 +427,18 @@ const django = grammar({
"<=", "<=",
">=", ">=",
"in", "in",
prec(1, joined("not", "in")), prec.dynamic(1, joined("not", "in")),
"is", "is",
prec(1, joined("is", "not")), prec.dynamic(1, joined("is", "not")),
), ),
predicate: ($) => predicate: ($) =>
seq( seq(
optional(seq("not", SEP)), repeat(seq("not", SEP)),
$.filtered_value, $.filtered_value,
repeat( repeat(
seq( seq(
part($.binaryOperator), part($.binaryOperator),
optional(part("not")), repeat(part("not")),
part($.filtered_value), part($.filtered_value),
), ),
), ),
+9 -16
View File
@@ -493,7 +493,7 @@
"value": "in" "value": "in"
}, },
{ {
"type": "PREC", "type": "PREC_DYNAMIC",
"value": 1, "value": 1,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
@@ -518,7 +518,7 @@
"value": "is" "value": "is"
}, },
{ {
"type": "PREC", "type": "PREC_DYNAMIC",
"value": 1, "value": 1,
"content": { "content": {
"type": "SEQ", "type": "SEQ",
@@ -544,9 +544,8 @@
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
"type": "CHOICE", "type": "REPEAT",
"members": [ "content": {
{
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -558,11 +557,7 @@
"value": "[ \\t\\r\\n]+" "value": "[ \\t\\r\\n]+"
} }
] ]
},
{
"type": "BLANK"
} }
]
}, },
{ {
"type": "SYMBOL", "type": "SYMBOL",
@@ -587,9 +582,8 @@
] ]
}, },
{ {
"type": "CHOICE", "type": "REPEAT",
"members": [ "content": {
{
"type": "SEQ", "type": "SEQ",
"members": [ "members": [
{ {
@@ -601,11 +595,7 @@
"value": "not" "value": "not"
} }
] ]
},
{
"type": "BLANK"
} }
]
}, },
{ {
"type": "SEQ", "type": "SEQ",
@@ -8598,6 +8588,9 @@
[ [
"predicate" "predicate"
], ],
[
"binaryOperator"
],
[ [
"library", "library",
"load" "load"
+39967 -39880
View File
File diff suppressed because it is too large. Load diff
+146
View File
@@ -182,3 +182,149 @@ If Is Not None
(template (template
(content)))) (content))))
(content)) (content))
==================
If Is
==================
{% if x is y %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier))))
(binaryOperator)
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))
==================
If Is Not
==================
{% if x is not y %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier))))
(binaryOperator)
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))
==================
If Is Not Not
==================
{% if x is not not y %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier))))
(binaryOperator)
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))
==================
If With A Repeated Not
==================
{% if not not x %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))
==================
If Not Before A Name Beginning With In
==================
{% if not in_stock %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))
==================
If Is Before A Name Beginning With Not
==================
{% if x is nothing %}a{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier))))
(binaryOperator)
(filtered_value
(value
(variable_attribute
(identifier)))))
(template
(content))))
(content))