add True, False, None

This commit is contained in:
danhanson committed 2026-09-10 15:00:39 -05:00
1 parent c0a2e0099c
commit 913ca69e6c
9 files changed
+40372 -39523

No files matched your search

+6
View File
@@ -277,6 +277,12 @@ documentation or memory. Both checks are worth repeating whenever tags, filters
- Django has no number token: `[\w.]+` is one lexeme and `int()`/`float()` decides whether it is a literal, - Django has no number token: `[\w.]+` is one lexeme and `int()`/`float()` decides whether it is a literal,
so `1a`, `1e`, `0x1f`, `1.2.3`, `1__0` and `1.` are variable _lookups_ there and errors here. so `1a`, `1e`, `0x1f`, `1.2.3`, `1__0` and `1.` are variable _lookups_ there and errors here.
- Django has no boolean or `None` token either. `True`, `False` and `None` are lookups, which resolve
because `BaseContext._reset_dicts` seeds every context with `{"True": True, "False": False, "None": None}`.
They are `boolean` and `none` literals here, so that a tool reading `queries/locals.scm` does not count
them as references to something the template never defines. Shadowing still parses — `{% with True=1 %}`
binds an `identifier`, because the keyword is not valid where a binding name is — but `{{ True.x }}` is
an error here and an ordinary two-part lookup in Django, which renders `string_if_invalid`.
- A `with` binding written twice (`{% with a=1 a=2 %}`, and the same in `{% include %}` and - A `with` binding written twice (`{% with a=1 a=2 %}`, and the same in `{% include %}` and
`{% blocktranslate %}`) is an error here and not in Django, where `token_kwargs` builds a dict and the `{% blocktranslate %}`) is an error here and not in Django, where `token_kwargs` builds a dict and the
later value wins. It has no use, so it is treated as the mistake it almost certainly is. later value wins. It has no use, so it is treated as the mistake it almost certainly is.
+4 -1
View File
@@ -342,7 +342,8 @@ const django = grammar({
seq($.value, optional(seq("|", $.filter_expression))), seq($.value, optional(seq("|", $.filter_expression))),
filter_expression: ($) => seq($.filter, repeat(seq("|", $.filter))), filter_expression: ($) => seq($.filter, repeat(seq("|", $.filter))),
value: ($) => choice($.literal, $.variable_attribute), value: ($) => choice($.literal, $.variable_attribute),
literal: ($) => choice($.number, $.string, $.translated_string), literal: ($) =>
choice($.number, $.string, $.translated_string, $.boolean, $.none),
// digits, with single underscores allowed between them, as Python's // digits, with single underscores allowed between them, as Python's
// int() and float() accept ("1_000") and Django's Variable relies on // int() and float() accept ("1_000") and Django's Variable relies on
number: ($) => number: ($) =>
@@ -354,6 +355,8 @@ const django = grammar({
* string: "_( 'a' )" is a syntax error there, not a translated literal. * string: "_( 'a' )" is a syntax error there, not a translated literal.
*/ */
translated_string: ($) => seq("_(", $.string, ")"), translated_string: ($) => seq("_(", $.string, ")"),
boolean: ($) => choice("True", "False"),
none: ($) => "None",
attribute: ($) => /[a-zA-Z0-9][a-zA-Z0-9_]*(?:\.[a-zA-Z0-9][a-zA-Z0-9_]*)*/, attribute: ($) => /[a-zA-Z0-9][a-zA-Z0-9_]*(?:\.[a-zA-Z0-9][a-zA-Z0-9_]*)*/,
variable_attribute: ($) => variable_attribute: ($) =>
seq($.identifier, optional(seq(".", $.attribute))), seq($.identifier, optional(seq(".", $.attribute))),
+2
View File
@@ -41,6 +41,8 @@ variable: (identifier) @variable.parameter
(number) @number (number) @number
(string) @string (string) @string
(boolean) @constant.builtin
(none) @constant.builtin
(library (library
(identifier) @module) (identifier) @module)
+25
View File
@@ -122,6 +122,14 @@
{ {
"type": "SYMBOL", "type": "SYMBOL",
"name": "translated_string" "name": "translated_string"
},
{
"type": "SYMBOL",
"name": "boolean"
},
{
"type": "SYMBOL",
"name": "none"
} }
] ]
}, },
@@ -150,6 +158,23 @@
} }
] ]
}, },
"boolean": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "True"
},
{
"type": "STRING",
"value": "False"
}
]
},
"none": {
"type": "STRING",
"value": "None"
},
"attribute": { "attribute": {
"type": "PATTERN", "type": "PATTERN",
"value": "[a-zA-Z0-9][a-zA-Z0-9_]*(?:\\.[a-zA-Z0-9][a-zA-Z0-9_]*)*" "value": "[a-zA-Z0-9][a-zA-Z0-9_]*(?:\\.[a-zA-Z0-9][a-zA-Z0-9_]*)*"
+25
View File
@@ -396,6 +396,11 @@
] ]
} }
}, },
{
"type": "boolean",
"named": true,
"fields": {}
},
{ {
"type": "cache_clause", "type": "cache_clause",
"named": true, "named": true,
@@ -1699,6 +1704,14 @@
"multiple": false, "multiple": false,
"required": true, "required": true,
"types": [ "types": [
{
"type": "boolean",
"named": true
},
{
"type": "none",
"named": true
},
{ {
"type": "number", "type": "number",
"named": true "named": true
@@ -2650,6 +2663,14 @@
"type": ">=", "type": ">=",
"named": false "named": false
}, },
{
"type": "False",
"named": false
},
{
"type": "True",
"named": false
},
{ {
"type": "_(", "type": "_(",
"named": false "named": false
@@ -3074,6 +3095,10 @@
"type": "media", "type": "media",
"named": false "named": false
}, },
{
"type": "none",
"named": true
},
{ {
"type": "noop", "type": "noop",
"named": false "named": false
+40163 -39522
View File
File diff suppressed because it is too large. Load diff
+26
View File
@@ -156,3 +156,29 @@ If Operator Adjacent To Value
{% if 1and 2 %}yes{% endif %} {% if 1and 2 %}yes{% endif %}
--- ---
==================
If Is Not None
==================
{% if status is not None %}yes{% endif %}
---
(template
(content)
(if_group
(if_clause
(predicate
(filtered_value
(value
(variable_attribute
(identifier))))
(binaryOperator)
(filtered_value
(value
(literal
(none)))))
(template
(content))))
(content))
+100
View File
@@ -361,3 +361,103 @@ Template Variable With Spaced Filter
(filter_expression (filter_expression
(filter)))) (filter))))
(content)) (content))
==================
Template Variable True
==================
{{ True }}
---
(template
(content)
(template_variable
(filtered_value
(value
(literal
(boolean)))))
(content))
==================
Template Variable False
==================
{{ False }}
---
(template
(content)
(template_variable
(filtered_value
(value
(literal
(boolean)))))
(content))
==================
Template Variable None
==================
{{ None }}
---
(template
(content)
(template_variable
(filtered_value
(value
(literal
(none)))))
(content))
==================
Template Variable Boolean As Filter Argument
==================
{{ value|default:True }}
---
(template
(content)
(template_variable
(filtered_value
(value
(variable_attribute
(identifier)))
(filter_expression
(filter
argument: (value
(literal
(boolean)))))))
(content))
==================
Template Variable Name Beginning With A Literal
==================
{{ Truest }}
---
(template
(content)
(template_variable
(filtered_value
(value
(variable_attribute
(identifier)))))
(content))
==================
Template Variable Attribute Of A Literal
:error
==================
{{ None.x }}
---
+21
View File
@@ -111,3 +111,24 @@ With Repeating A Binding Of The Tag Before It
(template (template
(content)))) (content))))
(content)) (content))
==================
With A Binding Named After A Literal
==================
{% with True=1 %}a{% endwith %}
---
(template
(content)
(with_group
(with_clause
variable: (identifier)
(filtered_value
(value
(literal
(number))))
(template
(content))))
(content))