import { describe, expect, it } from "vitest"; import { tokenize } from "../src/lexer"; import { parse } from "../src/parser"; import { Environment, Interpreter } from "../src/runtime"; const TEST_STRINGS = { // Text nodes NO_TEMPLATE: `Hello world!`, TEXT_NODES: `0{{ 'A' }}1{{ 'B' }}{{ 'C' }}2{{ 'D' }}3`, // Logical operators LOGICAL_AND: `{{ true and true }}{{ true and false }}{{ false and true }}{{ false and false }}`, LOGICAL_OR: `{{ true or true }}{{ true or false }}{{ false or true }}{{ false or false }}`, LOGICAL_NOT: `{{ not true }}{{ not false }}`, LOGICAL_NOT_NOT: `{{ not not true }}{{ not not false }}`, LOGICAL_AND_OR: `{{ true and true or false }}{{ true and false or true }}{{ false and true or true }}{{ false and false or true }}{{ false and false or false }}`, LOGICAL_AND_NOT: `{{ true and not true }}{{ true and not false }}{{ false and not true }}{{ false and not false }}`, LOGICAL_OR_NOT: `{{ true or not true }}{{ true or not false }}{{ false or not true }}{{ false or not false }}`, LOGICAL_COMBINED: `{{ 1 == 2 and 2 == 2 }}{{ 1 == 2 or 2 == 2}}`, // If statements IF_ONLY: `{% if 1 == 1 %}{{ 'A' }}{% endif %}{{ 'B' }}`, IF_ELSE_ONLY: `{% if 1 == 2 %}{{ 'A' }}{% else %}{{ 'B' }}{% endif %}{{ 'C' }}`, IF_ELIF_ELSE: `{% if 1 == 2 %}{{ 'A' }}{{ 'B' }}{{ 'C' }}{% elif 1 == 2 %}{{ 'D' }}{% elif 1 == 3 %}{{ 'E' }}{{ 'F' }}{% else %}{{ 'G' }}{{ 'H' }}{{ 'I' }}{% endif %}{{ 'J' }}`, NESTED_STATEMENTS: `{% set a = 0 %}{% set b = 0 %}{% set c = 0 %}{% set d = 0 %}{% if 1 == 1 %}{% set a = 2 %}{% set b = 3 %}{% elif 1 == 2 %}{% set c = 4 %}{% else %}{% set d = 5 %}{% endif %}{{ a }}{{ b }}{{ c }}{{ d }}`, // For loops FOR_LOOP: `{% for message in messages %}{{ message['content'] }}{% endfor %}`, FOR_LOOP_UNPACKING: `|{% for x, y in [ [1, 2], [3, 4] ] %}|{{ x + ' ' + y }}|{% endfor %}|`, // Set variables VARIABLES: `{% set x = 'Hello' %}{% set y = 'World' %}{{ x + ' ' + y }}`, // Numbers NUMBERS: `|{{ 5 }}|{{ -5 }}|{{ add(3, -1) }}|{{ (3 - 1) + (a - 5) - (a + 5)}}|`, // Binary expressions BINOP_EXPR: `{{ 1 % 2 }}{{ 1 < 2 }}{{ 1 > 2 }}{{ 1 >= 2 }}{{ 2 <= 2 }}{{ 2 == 2 }}{{ 2 != 3 }}{{ 2 + 3 }}`, // Strings STRINGS: `{{ 'Bye' }}{{ bos_token + '[INST] ' }}`, STRINGS_1: `|{{ "test" }}|{{ "a" + 'b' + "c" }}|{{ '"' + "'" }}|{{ '\\'' }}|{{ "\\"" }}|`, STRINGS_2: `|{{ "" | length }}|{{ "a" | length }}|{{ '' | length }}|{{ 'a' | length }}|`, // Function calls FUNCTIONS: `{{ func() }}{{ func(apple) }}{{ func(x, 'test', 2, false) }}`, // Object properties PROPERTIES: `{{ obj.x + obj.y }}{{ obj['x'] + obj.y }}`, // Object methods OBJ_METHODS: `{{ obj.x(x, y) }}{{ ' ' + obj.x() + ' ' }}{{ obj.z[x](x, y) }}`, STRING_METHODS: `{{ ' A '.strip() }}{% set x = ' B ' %}{{ x.strip() }}{% set y = ' aBcD ' %}{{ y.upper() }}{{ y.lower() }}`, STRING_METHODS_2: `{{ 'test test'.title() }}`, // String indexing and slicing STRING_SLICING: `|{{ x[0] }}|{{ x[:] }}|{{ x[:3] }}|{{ x[1:4] }}|{{ x[1:-1] }}|{{ x[1::2] }}|{{ x[5::-1] }}|`, // Array indexing and slicing ARRAY_SLICING: `|{{ strings[0] }}|{% for s in strings[:] %}{{ s }}{% endfor %}|{% for s in strings[:3] %}{{ s }}{% endfor %}|{% for s in strings[1:4] %}{{ s }}{% endfor %}|{% for s in strings[1:-1] %}{{ s }}{% endfor %}|{% for s in strings[1::2] %}{{ s }}{% endfor %}|{% for s in strings[5::-1] %}{{ s }}{% endfor %}|`, // Membership operators MEMBERSHIP: `|{{ 0 in arr }}|{{ 1 in arr }}|{{ true in arr }}|{{ false in arr }}|{{ 'a' in arr }}|{{ 'b' in arr }}|`, MEMBERSHIP_NEGATION_1: `|{{ not 0 in arr }}|{{ not 1 in arr }}|{{ not true in arr }}|{{ not false in arr }}|{{ not 'a' in arr }}|{{ not 'b' in arr }}|`, MEMBERSHIP_NEGATION_2: `|{{ 0 not in arr }}|{{ 1 not in arr }}|{{ true not in arr }}|{{ false not in arr }}|{{ 'a' not in arr }}|{{ 'b' not in arr }}|`, // Escaped characters ESCAPED_CHARS: `{{ '\\n' }}{{ '\\t' }}{{ '\\'' }}{{ '\\"' }}{{ '\\\\' }}{{ '|\\n|\\t|\\'|\\"|\\\\|' }}`, // Substring inclusion SUBSTRING_INCLUSION: `|{{ '' in 'abc' }}|{{ 'a' in 'abc' }}|{{ 'd' in 'abc' }}|{{ 'ab' in 'abc' }}|{{ 'ac' in 'abc' }}|{{ 'abc' in 'abc' }}|{{ 'abcd' in 'abc' }}|`, // Filter operator FILTER_OPERATOR: `{{ arr | length }}{{ 1 + arr | length }}{{ 2 + arr | sort | length }}{{ (arr | sort)[0] }}`, FILTER_OPERATOR_2: `|{{ 'abc' | length }}|{{ 'aBcD' | upper }}|{{ 'aBcD' | lower }}|{{ 'test test' | capitalize}}|{{ 'test test' | title }}|{{ ' a b ' | trim }}|{{ ' A B ' | trim | lower | length }}|`, FILTER_OPERATOR_3: `|{{ -1 | abs }}|{{ 1 | abs }}|`, FILTER_OPERATOR_4: `{{ items | selectattr('key') | length }}`, FILTER_OPERATOR_5: `{{ messages | selectattr('role', 'equalto', 'system') | length }}`, FILTER_OPERATOR_6: `|{{ obj | length }}|{{ (obj | items)[1:] | length }}|`, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: `|{{ 1 and 2 }}|{{ 1 and 0 }}|{{ 0 and 1 }}|{{ 0 and 0 }}|{{ 1 or 2 }}|{{ 1 or 0 }}|{{ 0 or 1 }}|{{ 0 or 0 }}|{{ not 1 }}|{{ not 0 }}|`, BOOLEAN_STRINGS: `|{{ 'a' and 'b' }}|{{ 'a' and '' }}|{{ '' and 'a' }}|{{ '' and '' }}|{{ 'a' or 'b' }}|{{ 'a' or '' }}|{{ '' or 'a' }}|{{ '' or '' }}|{{ not 'a' }}|{{ not '' }}|`, BOOLEAN_MIXED: `|{{ true and 1 }}|{{ true and 0 }}|{{ false and 1 }}|{{ false and 0 }}|{{ true or 1 }}|{{ true or 0 }}|{{ false or 1 }}|{{ false or 0 }}|`, BOOLEAN_MIXED_2: `|{{ true and '' }}|{{ true and 'a' }}|{{ false or '' }}|{{ false or 'a' }}|{{ '' and true }}|{{ 'a' and true }}|{{ '' or false }}|{{ 'a' or false }}|`, BOOLEAN_MIXED_IF: `{% if '' %}{{ 'A' }}{% endif %}{% if 'a' %}{{ 'B' }}{% endif %}{% if true and '' %}{{ 'C' }}{% endif %}{% if true and 'a' %}{{ 'D' }}{% endif %}`, // Tests (is operator) IS_OPERATOR: `|{{ unknown_var is defined }}|{{ unknown_var is not defined }}|{{ known_var is defined }}|{{ known_var is not defined }}|`, IS_OPERATOR_2: `|{{ true is true }}|{{ true is not true }}|{{ true is false }}|{{ true is not false }}|{{ true is boolean }}|{{ 1 is boolean }}|`, IS_OPERATOR_3: `|{{ 1 is odd }}|{{ 2 is odd }}|{{ 1 is even }}|{{ 2 is even }}|{{ 2 is number }}|{{ '2' is number }}|{{ 2 is integer }}|{{ '2' is integer }}|`, IS_OPERATOR_4: `|{{ func is callable }}|{{ 2 is callable }}|{{ 1 is iterable }}|{{ 'hello' is iterable }}|`, IS_OPERATOR_5: `|{{ 'a' is lower }}|{{ 'A' is lower }}|{{ 'a' is upper }}|{{ 'A' is upper }}|`, // Short-circuit evaluation SHORT_CIRCUIT: `{{ false and raise_exception('This should not be printed') }}`, SHORT_CIRCUIT_1: `{{ true or raise_exception('This should not be printed') }}`, // Namespaces NAMESPACE: `{% set ns = namespace() %}{% set ns.foo = 'bar' %}{{ ns.foo }}`, NAMESPACE_1: `{% set ns = namespace(default=false) %}{{ ns.default }}`, NAMESPACE_2: `{% set ns = namespace(default=false, number=1+1) %}|{{ ns.default }}|{{ ns.number }}|`, // Object operators OBJECT_OPERATORS: `|{{ 'known' in obj }}|{{ 'known' not in obj }}|{{ 'unknown' in obj }}|{{ 'unknown' not in obj }}|`, OBJECT_OPERATORS_1: `|{{ obj.get('known') }}|{{ obj.get('unknown') is none }}|{{ obj.get('unknown') is defined }}|`, OBJECT_OPERATORS_2: `|{% for x, y in obj.items() %}|{{ x + ' ' + y }}|{% endfor %}|`, // Scope SCOPE: `{% set ns = namespace(found=false) %}{% for num in nums %}{% if num == 1 %}{{ 'found=' }}{% set ns.found = true %}{% endif %}{% endfor %}{{ ns.found }}`, SCOPE_1: `{% set found = false %}{% for num in nums %}{% if num == 1 %}{{ 'found=' }}{% set found = true %}{% endif %}{% endfor %}{{ found }}`, // Undefined UNDEFINED_VARIABLES: `{{ undefined_variable }}`, UNDEFINED_ACCESS: `{{ object.undefined_attribute }}`, // Ternary operator TERNARY_OPERATOR: `|{{ 'a' if true else 'b' }}|{{ 'a' if false else 'b' }}|{{ 'a' if 1 + 1 == 2 else 'b' }}|{{ 'a' if 1 + 1 == 3 or 1 * 2 == 3 else 'b' }}|`, // Array literals ARRAY_LITERALS: `{{ [1, true, 'hello', [1, 2, 3, 4], var] | length }}`, // Tuple literals TUPLE_LITERALS: `{{ (1, (1, 2)) | length }}`, // Object literals OBJECT_LITERALS: `{{ { 'key': 'value', key: 'value2', "key3": [1, {'foo': 'bar'} ] }['key'] }}`, // Array operators ARRAY_OPERATORS: `{{ ([1, 2, 3] + [4, 5, 6]) | length }}`, }; const TEST_PARSED = { // Text nodes NO_TEMPLATE: [{ value: "Hello world!", type: "Text" }], TEXT_NODES: [ { value: "0", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "1", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "B", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "C", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "2", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "D", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "3", type: "Text" }, ], // Logical operators LOGICAL_AND: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_OR: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_NOT: [ { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_NOT_NOT: [ { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_AND_OR: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_AND_NOT: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_OR_NOT: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, ], LOGICAL_COMBINED: [ { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "and", type: "And" }, { value: "2", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "2", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, ], // If statements IF_ONLY: [ { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "B", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, ], IF_ELSE_ONLY: [ { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "else", type: "Else" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "B", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "C", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, ], IF_ELIF_ELSE: [ { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "B", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "C", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "elif", type: "ElseIf" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "D", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "elif", type: "ElseIf" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "3", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "E", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "F", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "else", type: "Else" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "G", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "H", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "I", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "J", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, ], NESTED_STATEMENTS: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "a", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "0", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "b", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "0", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "c", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "0", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "d", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "0", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "a", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "2", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "b", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "3", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "elif", type: "ElseIf" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "c", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "4", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "else", type: "Else" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "d", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "5", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "b", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "c", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "d", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // For loops FOR_LOOP: [ { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "message", type: "Identifier" }, { value: "in", type: "In" }, { value: "messages", type: "Identifier" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "message", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "content", type: "StringLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, ], FOR_LOOP_UNPACKING: [ { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "x", type: "Identifier" }, { value: ",", type: "Comma" }, { value: "y", type: "Identifier" }, { value: "in", type: "In" }, { value: "[", type: "OpenSquareBracket" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "2", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: ",", type: "Comma" }, { value: "[", type: "OpenSquareBracket" }, { value: "3", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "4", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: " ", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "y", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, ], // Set variables VARIABLES: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "x", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "Hello", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "y", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "World", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: " ", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "y", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Numbers NUMBERS: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "5", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "-5", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "add", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "3", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "-1", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "(", type: "OpenParen" }, { value: "3", type: "NumericLiteral" }, { value: "-", type: "AdditiveBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "(", type: "OpenParen" }, { value: "a", type: "Identifier" }, { value: "-", type: "AdditiveBinaryOperator" }, { value: "5", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: "-", type: "AdditiveBinaryOperator" }, { value: "(", type: "OpenParen" }, { value: "a", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "5", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Binary expressions BINOP_EXPR: [ { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "%", type: "MultiplicativeBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "<", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: ">", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: ">=", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "<=", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "!=", type: "ComparisonBinaryOperator" }, { value: "3", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "3", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, ], // Strings STRINGS: [ { value: "{{", type: "OpenExpression" }, { value: "Bye", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "bos_token", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "[INST] ", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, ], STRINGS_1: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "test", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "b", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "c", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: '"', type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "'", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "'", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: '"', type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], STRINGS_2: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Function calls FUNCTIONS: [ { value: "{{", type: "OpenExpression" }, { value: "func", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "func", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "apple", type: "Identifier" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "func", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "x", type: "Identifier" }, { value: ",", type: "Comma" }, { value: "test", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "2", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "false", type: "BooleanLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], // Object properties PROPERTIES: [ { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "x", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "y", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "x", type: "StringLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "y", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Object methods OBJ_METHODS: [ { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "x", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "x", type: "Identifier" }, { value: ",", type: "Comma" }, { value: "y", type: "Identifier" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: " ", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "x", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: " ", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "z", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "x", type: "Identifier" }, { value: "]", type: "CloseSquareBracket" }, { value: "(", type: "OpenParen" }, { value: "x", type: "Identifier" }, { value: ",", type: "Comma" }, { value: "y", type: "Identifier" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], // String methods STRING_METHODS: [ { value: "{{", type: "OpenExpression" }, { value: " A ", type: "StringLiteral" }, { value: ".", type: "Dot" }, { value: "strip", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "x", type: "Identifier" }, { value: "=", type: "Equals" }, { value: " B ", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "strip", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "y", type: "Identifier" }, { value: "=", type: "Equals" }, { value: " aBcD ", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "y", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "upper", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "y", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "lower", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], STRING_METHODS_2: [ { value: "{{", type: "OpenExpression" }, { value: "test test", type: "StringLiteral" }, { value: ".", type: "Dot" }, { value: "title", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], // String indexing and slicing STRING_SLICING: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "0", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: ":", type: "Colon" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: ":", type: "Colon" }, { value: "3", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: "4", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: "-1", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: ":", type: "Colon" }, { value: "2", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "5", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: ":", type: "Colon" }, { value: "-1", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Array indexing and slicing ARRAY_SLICING: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "0", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: ":", type: "Colon" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: ":", type: "Colon" }, { value: "3", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: "4", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: "-1", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: ":", type: "Colon" }, { value: "2", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "s", type: "Identifier" }, { value: "in", type: "In" }, { value: "strings", type: "Identifier" }, { value: "[", type: "OpenSquareBracket" }, { value: "5", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: ":", type: "Colon" }, { value: "-1", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "s", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, ], // Membership operators MEMBERSHIP: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "b", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], MEMBERSHIP_NEGATION_1: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "0", type: "NumericLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "a", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "b", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], MEMBERSHIP_NEGATION_2: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "b", type: "StringLiteral" }, { value: "not in", type: "NotIn" }, { value: "arr", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Escaped characters ESCAPED_CHARS: [ { value: "{{", type: "OpenExpression" }, { value: "\n", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "\t", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "'", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: '"', type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "\\", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: `|\n|\t|'|"|\\|`, type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, ], // Substring inclusion SUBSTRING_INCLUSION: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "d", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "ab", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "ac", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "abc", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "abcd", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "abc", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Filter operator FILTER_OPERATOR: [ { value: "{{", type: "OpenExpression" }, { value: "arr", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "arr", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "arr", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "sort", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "{{", type: "OpenExpression" }, { value: "(", type: "OpenParen" }, { value: "arr", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "sort", type: "Identifier" }, { value: ")", type: "CloseParen" }, { value: "[", type: "OpenSquareBracket" }, { value: "0", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, ], FILTER_OPERATOR_2: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "abc", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "aBcD", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "upper", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "aBcD", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "lower", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "test test", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "capitalize", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "test test", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "title", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: " a b ", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "trim", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: " A B ", type: "StringLiteral" }, { value: "|", type: "Pipe" }, { value: "trim", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "lower", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], FILTER_OPERATOR_3: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "-1", type: "NumericLiteral" }, { value: "|", type: "Pipe" }, { value: "abs", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "|", type: "Pipe" }, { value: "abs", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], FILTER_OPERATOR_4: [ { value: "{{", type: "OpenExpression" }, { value: "items", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "selectattr", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "key", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], FILTER_OPERATOR_5: [ { value: "{{", type: "OpenExpression" }, { value: "messages", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "selectattr", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "role", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "equalto", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "system", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], FILTER_OPERATOR_6: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "(", type: "OpenParen" }, { value: "obj", type: "Identifier" }, { value: "|", type: "Pipe" }, { value: "items", type: "Identifier" }, { value: ")", type: "CloseParen" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ":", type: "Colon" }, { value: "]", type: "CloseSquareBracket" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Logical operators between non-Booleans BOOLEAN_NUMERICAL: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "and", type: "And" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "and", type: "And" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "and", type: "And" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "and", type: "And" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "2", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "0", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], BOOLEAN_STRINGS: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "a", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "a", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "a", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "not", type: "UnaryOperator" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], BOOLEAN_MIXED: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "1", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "0", type: "NumericLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], BOOLEAN_MIXED_2: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "a", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "a", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "and", type: "And" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "or", type: "Or" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], BOOLEAN_MIXED_IF: [ { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "a", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "B", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "C", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "true", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "a", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "D", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, ], // Tests (is operator) IS_OPERATOR: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "unknown_var", type: "Identifier" }, { value: "is", type: "Is" }, { value: "defined", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "unknown_var", type: "Identifier" }, { value: "is", type: "Is" }, { value: "not", type: "UnaryOperator" }, { value: "defined", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "known_var", type: "Identifier" }, { value: "is", type: "Is" }, { value: "defined", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "known_var", type: "Identifier" }, { value: "is", type: "Is" }, { value: "not", type: "UnaryOperator" }, { value: "defined", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], IS_OPERATOR_2: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "is", type: "Is" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "is", type: "Is" }, { value: "not", type: "UnaryOperator" }, { value: "true", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "is", type: "Is" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "is", type: "Is" }, { value: "not", type: "UnaryOperator" }, { value: "false", type: "BooleanLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "is", type: "Is" }, { value: "boolean", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "boolean", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], IS_OPERATOR_3: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "odd", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "odd", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "even", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "even", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "number", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "number", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "integer", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "integer", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], IS_OPERATOR_4: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "func", type: "Identifier" }, { value: "is", type: "Is" }, { value: "callable", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "2", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "callable", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "1", type: "NumericLiteral" }, { value: "is", type: "Is" }, { value: "iterable", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "hello", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "iterable", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], IS_OPERATOR_5: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "lower", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "lower", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "upper", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "A", type: "StringLiteral" }, { value: "is", type: "Is" }, { value: "upper", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Short-circuit evaluation SHORT_CIRCUIT: [ { value: "{{", type: "OpenExpression" }, { value: "false", type: "BooleanLiteral" }, { value: "and", type: "And" }, { value: "raise_exception", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "This should not be printed", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], SHORT_CIRCUIT_1: [ { value: "{{", type: "OpenExpression" }, { value: "true", type: "BooleanLiteral" }, { value: "or", type: "Or" }, { value: "raise_exception", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "This should not be printed", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, ], // Namespaces NAMESPACE: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "namespace", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "foo", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "bar", type: "StringLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "foo", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], NAMESPACE_1: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "namespace", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "default", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "false", type: "BooleanLiteral" }, { value: ")", type: "CloseParen" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "default", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], NAMESPACE_2: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "namespace", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "default", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "false", type: "BooleanLiteral" }, { value: ",", type: "Comma" }, { value: "number", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "1", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "default", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "number", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Object operators OBJECT_OPERATORS: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "known", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "obj", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "known", type: "StringLiteral" }, { value: "not in", type: "NotIn" }, { value: "obj", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "unknown", type: "StringLiteral" }, { value: "in", type: "In" }, { value: "obj", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "unknown", type: "StringLiteral" }, { value: "not in", type: "NotIn" }, { value: "obj", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], OBJECT_OPERATORS_1: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "get", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "known", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "get", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "unknown", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "is", type: "Is" }, { value: "none", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "get", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "unknown", type: "StringLiteral" }, { value: ")", type: "CloseParen" }, { value: "is", type: "Is" }, { value: "defined", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], OBJECT_OPERATORS_2: [ { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "x", type: "Identifier" }, { value: ",", type: "Comma" }, { value: "y", type: "Identifier" }, { value: "in", type: "In" }, { value: "obj", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "items", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: ")", type: "CloseParen" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "x", type: "Identifier" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: " ", type: "StringLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "y", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "|", type: "Text" }, ], // Scope SCOPE: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "namespace", type: "Identifier" }, { value: "(", type: "OpenParen" }, { value: "found", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "false", type: "BooleanLiteral" }, { value: ")", type: "CloseParen" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "num", type: "Identifier" }, { value: "in", type: "In" }, { value: "nums", type: "Identifier" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "num", type: "Identifier" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "found=", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "found", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "true", type: "BooleanLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "ns", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "found", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], SCOPE_1: [ { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "found", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "false", type: "BooleanLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "for", type: "For" }, { value: "num", type: "Identifier" }, { value: "in", type: "In" }, { value: "nums", type: "Identifier" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "if", type: "If" }, { value: "num", type: "Identifier" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "found=", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "{%", type: "OpenStatement" }, { value: "set", type: "Set" }, { value: "found", type: "Identifier" }, { value: "=", type: "Equals" }, { value: "true", type: "BooleanLiteral" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "endif", type: "EndIf" }, { value: "%}", type: "CloseStatement" }, { value: "{%", type: "OpenStatement" }, { value: "endfor", type: "EndFor" }, { value: "%}", type: "CloseStatement" }, { value: "{{", type: "OpenExpression" }, { value: "found", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Undefined UNDEFINED_VARIABLES: [ { value: "{{", type: "OpenExpression" }, { value: "undefined_variable", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], UNDEFINED_ACCESS: [ { value: "{{", type: "OpenExpression" }, { value: "object", type: "Identifier" }, { value: ".", type: "Dot" }, { value: "undefined_attribute", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Ternary operator TERNARY_OPERATOR: [ { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "if", type: "If" }, { value: "true", type: "BooleanLiteral" }, { value: "else", type: "Else" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "if", type: "If" }, { value: "false", type: "BooleanLiteral" }, { value: "else", type: "Else" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "else", type: "Else" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, { value: "{{", type: "OpenExpression" }, { value: "a", type: "StringLiteral" }, { value: "if", type: "If" }, { value: "1", type: "NumericLiteral" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "1", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "3", type: "NumericLiteral" }, { value: "or", type: "Or" }, { value: "1", type: "NumericLiteral" }, { value: "*", type: "MultiplicativeBinaryOperator" }, { value: "2", type: "NumericLiteral" }, { value: "==", type: "ComparisonBinaryOperator" }, { value: "3", type: "NumericLiteral" }, { value: "else", type: "Else" }, { value: "b", type: "StringLiteral" }, { value: "}}", type: "CloseExpression" }, { value: "|", type: "Text" }, ], // Array literals ARRAY_LITERALS: [ { value: "{{", type: "OpenExpression" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "true", type: "BooleanLiteral" }, { value: ",", type: "Comma" }, { value: "hello", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "2", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "3", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "4", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: ",", type: "Comma" }, { value: "var", type: "Identifier" }, { value: "]", type: "CloseSquareBracket" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Tuple literals TUPLE_LITERALS: [ { value: "{{", type: "OpenExpression" }, { value: "(", type: "OpenParen" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "(", type: "OpenParen" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "2", type: "NumericLiteral" }, { value: ")", type: "CloseParen" }, { value: ")", type: "CloseParen" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], // Object literals OBJECT_LITERALS: [ { value: "{{", type: "OpenExpression" }, { value: "{", type: "OpenCurlyBracket" }, { value: "key", type: "StringLiteral" }, { value: ":", type: "Colon" }, { value: "value", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "key", type: "Identifier" }, { value: ":", type: "Colon" }, { value: "value2", type: "StringLiteral" }, { value: ",", type: "Comma" }, { value: "key3", type: "StringLiteral" }, { value: ":", type: "Colon" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "{", type: "OpenCurlyBracket" }, { value: "foo", type: "StringLiteral" }, { value: ":", type: "Colon" }, { value: "bar", type: "StringLiteral" }, { value: "}", type: "CloseCurlyBracket" }, { value: "]", type: "CloseSquareBracket" }, { value: "}", type: "CloseCurlyBracket" }, { value: "[", type: "OpenSquareBracket" }, { value: "key", type: "StringLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "}}", type: "CloseExpression" }, ], // Array operators ARRAY_OPERATORS: [ { value: "{{", type: "OpenExpression" }, { value: "(", type: "OpenParen" }, { value: "[", type: "OpenSquareBracket" }, { value: "1", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "2", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "3", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: "+", type: "AdditiveBinaryOperator" }, { value: "[", type: "OpenSquareBracket" }, { value: "4", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "5", type: "NumericLiteral" }, { value: ",", type: "Comma" }, { value: "6", type: "NumericLiteral" }, { value: "]", type: "CloseSquareBracket" }, { value: ")", type: "CloseParen" }, { value: "|", type: "Pipe" }, { value: "length", type: "Identifier" }, { value: "}}", type: "CloseExpression" }, ], }; const TEST_CONTEXT = { // Text nodes NO_TEMPLATE: {}, TEXT_NODES: {}, // Logical operators LOGICAL_AND: {}, LOGICAL_OR: {}, LOGICAL_NOT: {}, LOGICAL_NOT_NOT: {}, LOGICAL_AND_OR: {}, LOGICAL_AND_NOT: {}, LOGICAL_OR_NOT: {}, LOGICAL_COMBINED: {}, // If statements IF_ONLY: {}, IF_ELSE_ONLY: {}, IF_ELIF_ELSE: {}, NESTED_STATEMENTS: {}, // For loops FOR_LOOP: { messages: [ { role: "user", content: "A" }, { role: "assistant", content: "B" }, { role: "user", content: "C" }, ], }, FOR_LOOP_UNPACKING: {}, // Set variables VARIABLES: {}, // Numbers NUMBERS: { a: 0, add: (x, y) => x + y, }, // Binary expressions BINOP_EXPR: {}, // Strings STRINGS: { bos_token: "", }, STRINGS_1: {}, STRINGS_2: {}, // Function calls FUNCTIONS: { x: 10, apple: "apple", func: (...args) => args.length, }, // Object properties PROPERTIES: { obj: { x: 10, y: 20 }, }, // Object methods OBJ_METHODS: { x: "A", y: "B", obj: { x: (...args) => args.join(""), z: { A: (...args) => args.join("_"), }, }, }, // String methods STRING_METHODS: {}, STRING_METHODS_2: {}, // String indexing and slicing STRING_SLICING: { x: "0123456789", }, // Array indexing and slicing ARRAY_SLICING: { strings: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], }, // Membership operators MEMBERSHIP: { arr: [0, true, "a"], }, MEMBERSHIP_NEGATION_1: { arr: [0, true, "a"], }, MEMBERSHIP_NEGATION_2: { arr: [0, true, "a"], }, // Escaped characters ESCAPED_CHARS: {}, // Substring inclusion SUBSTRING_INCLUSION: {}, // Filter operator FILTER_OPERATOR: { arr: [3, 2, 1], }, FILTER_OPERATOR_2: {}, FILTER_OPERATOR_3: {}, FILTER_OPERATOR_4: { items: [{ key: "a" }, { key: 0 }, { key: 1 }, {}, { key: false }], }, FILTER_OPERATOR_5: { messages: [{ role: "system" }, { role: "user" }, { role: "assistant" }], }, FILTER_OPERATOR_6: { obj: { a: 1, b: 2, c: 3 }, }, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: {}, BOOLEAN_STRINGS: {}, BOOLEAN_MIXED: {}, BOOLEAN_MIXED_2: {}, BOOLEAN_MIXED_IF: {}, // Tests (is operator) IS_OPERATOR: { known_var: "Hello World", }, IS_OPERATOR_2: {}, IS_OPERATOR_3: {}, IS_OPERATOR_4: { func: () => {}, }, IS_OPERATOR_5: {}, // Short-circuit evaluation SHORT_CIRCUIT: {}, SHORT_CIRCUIT_1: {}, // Namespaces NAMESPACE: {}, NAMESPACE_1: {}, NAMESPACE_2: {}, // Object operators OBJECT_OPERATORS: { obj: { known: true, }, }, OBJECT_OPERATORS_1: { obj: { known: true, }, }, OBJECT_OPERATORS_2: { obj: { a: 1, b: 2, c: 3 }, }, // Scope SCOPE: { nums: [1, 2, 3] }, SCOPE_1: { nums: [1, 2, 3] }, // Undefined UNDEFINED_VARIABLES: {}, UNDEFINED_ACCESS: { object: {} }, // Ternary operator TERNARY_OPERATOR: {}, // Array literals ARRAY_LITERALS: { var: true }, // Tuple literals TUPLE_LITERALS: {}, // Object literals OBJECT_LITERALS: { key: "key2", }, // Array operators ARRAY_OPERATORS: {}, }; const EXPECTED_OUTPUTS = { // Text nodes NO_TEMPLATE: `Hello world!`, TEXT_NODES: `0A1BC2D3`, // Logical operators LOGICAL_AND: `truefalsefalsefalse`, LOGICAL_OR: `truetruetruefalse`, LOGICAL_NOT: `falsetrue`, LOGICAL_NOT_NOT: `truefalse`, LOGICAL_AND_OR: `truetruetruetruefalse`, LOGICAL_AND_NOT: `falsetruefalsefalse`, LOGICAL_OR_NOT: `truetruefalsetrue`, LOGICAL_COMBINED: `falsetrue`, // If statements IF_ONLY: "AB", IF_ELSE_ONLY: "BC", IF_ELIF_ELSE: "GHIJ", NESTED_STATEMENTS: "2300", // For loops FOR_LOOP: "ABC", FOR_LOOP_UNPACKING: "||1 2||3 4||", // Set variables VARIABLES: "Hello World", // Numbers NUMBERS: "|5|-5|2|-8|", // Binary expressions BINOP_EXPR: "1truefalsefalsetruetruetrue5", // Strings STRINGS: "Bye[INST] ", STRINGS_1: `|test|abc|"'|'|"|`, STRINGS_2: `|0|1|0|1|`, // Function calls FUNCTIONS: "014", // Object properties PROPERTIES: "3030", // Object methods OBJ_METHODS: "AB A_B", STRING_METHODS: "AB ABCD abcd ", STRING_METHODS_2: "Test Test", // String indexing and slicing STRING_SLICING: "|0|0123456789|012|123|12345678|13579|543210|", // Array indexing and slicing ARRAY_SLICING: "|0|0123456789|012|123|12345678|13579|543210|", // Membership operators MEMBERSHIP: "|true|false|true|false|true|false|", MEMBERSHIP_NEGATION_1: "|false|true|false|true|false|true|", MEMBERSHIP_NEGATION_2: "|false|true|false|true|false|true|", // Escaped characters ESCAPED_CHARS: `\n\t'"\\|\n|\t|'|"|\\|`, // Substring inclusion SUBSTRING_INCLUSION: `|true|true|false|true|false|true|false|`, // Filter operator FILTER_OPERATOR: `3451`, FILTER_OPERATOR_2: `|3|ABCD|abcd|Test test|Test Test|a b|4|`, FILTER_OPERATOR_3: `|1|1|`, FILTER_OPERATOR_4: `2`, FILTER_OPERATOR_5: `1`, FILTER_OPERATOR_6: `|3|2|`, // Logical operators between non-Booleans BOOLEAN_NUMERICAL: `|2|0|0|0|1|1|1|0|false|true|`, BOOLEAN_STRINGS: `|b||||a|a|a||false|true|`, BOOLEAN_MIXED: `|1|0|false|false|true|true|1|0|`, BOOLEAN_MIXED_2: `||a||a||true|false|a|`, BOOLEAN_MIXED_IF: `BD`, // Tests (is operator) IS_OPERATOR: `|false|true|true|false|`, IS_OPERATOR_2: `|true|false|false|true|true|false|`, IS_OPERATOR_3: `|true|false|false|true|true|false|true|false|`, IS_OPERATOR_4: `|true|false|false|true|`, IS_OPERATOR_5: `|true|false|false|true|`, // Short-circuit evaluation SHORT_CIRCUIT: `false`, SHORT_CIRCUIT_1: `true`, // Namespaces NAMESPACE: `bar`, NAMESPACE_1: `false`, NAMESPACE_2: `|false|2|`, // Object operators OBJECT_OPERATORS: `|true|false|false|true|`, OBJECT_OPERATORS_1: `|true|true|true|`, OBJECT_OPERATORS_2: `||a 1||b 2||c 3||`, // Scope SCOPE: `found=true`, SCOPE_1: `found=false`, // Undefined UNDEFINED_VARIABLES: ``, UNDEFINED_ACCESS: ``, // Ternary operator TERNARY_OPERATOR: `|a|b|a|b|`, // Array literals ARRAY_LITERALS: `5`, // Tuple literals TUPLE_LITERALS: `2`, // Object literals OBJECT_LITERALS: `value`, // Array operators ARRAY_OPERATORS: `6`, }; describe("Templates", () => { describe("Lexing", () => { it("should tokenize an input string", () => { for (const [name, text] of Object.entries(TEST_STRINGS)) { const tokens = tokenize(text); if (!TEST_PARSED[name]) { throw new Error(`Test case "${name}" not found`); } if (tokens.length !== TEST_PARSED[name].length) { console.log(tokens); } // console.log(tokens); expect(tokens).toMatchObject(TEST_PARSED[name]); } }); // TODO add failure cases }); describe("Parsing and intepretation", () => { const AST_CACHE = new Map(); it("should generate an AST", () => { // NOTE: In this test case, we just check that no error occurs for (const [name, text] of Object.entries(TEST_PARSED)) { const ast = parse(text); AST_CACHE.set(name, ast); } }); it("should interpret an AST", () => { for (const [name, ast] of AST_CACHE.entries()) { if (TEST_CONTEXT[name] === undefined || EXPECTED_OUTPUTS[name] === undefined) { console.warn(`Skipping test case "${name}" due to missing context or expected output`); continue; } const env = new Environment(); // Declare global variables env.set("false", false); env.set("true", true); // Add user-defined variables for (const [key, value] of Object.entries(TEST_CONTEXT[name])) { env.set(key, value); } const interpreter = new Interpreter(env); const result = interpreter.run(ast); expect(result.value).toEqual(EXPECTED_OUTPUTS[name]); } }); }); }); describe("Error checking", () => { describe("Lexing errors", () => { it("Missing closing curly brace", () => { const text = "{{ variable"; expect(() => tokenize(text)).toThrowError(); }); it("Unclosed string literal", () => { const text = `{{ 'unclosed string }}`; expect(() => tokenize(text)).toThrowError(); }); it("Unexpected character", () => { const text = "{{ invalid ! invalid }}"; expect(() => tokenize(text)).toThrowError(); }); it("Invalid quote character", () => { const text = "{{ \u2018text\u2019 }}"; expect(() => tokenize(text)).toThrowError(); }); }); describe("Parsing errors", () => { it("Unclosed statement", () => { const text = "{{ variable }}{{"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Unclosed expression", () => { const text = "{% if condition %}\n Content"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Unmatched control structure", () => { const text = "{% if condition %}\n Content\n{% endif %}\n{% endfor %}"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Missing variable in for loop", () => { const text = "{% for %}\n Content\n{% endfor %}"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Unclosed parentheses in expression", () => { const text = "{{ (variable + 1 }}"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Invalid variable name", () => { const text = "{{ 1variable }}"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); it("Invalid control structure usage", () => { const text = "{% if %}Content{% endif %}"; const tokens = tokenize(text); expect(() => parse(tokens)).toThrowError(); }); }); describe("Runtime errors", () => { it("Undefined function call", () => { const env = new Environment(); const interpreter = new Interpreter(env); const tokens = tokenize("{{ undefined_function() }}"); const ast = parse(tokens); expect(() => interpreter.run(ast)).toThrowError(); }); it("Incorrect function call", () => { const env = new Environment(); env.set("true", true); const interpreter = new Interpreter(env); const tokens = tokenize("{{ true() }}"); const ast = parse(tokens); expect(() => interpreter.run(ast)).toThrowError(); }); it("Looping over non-iterable", () => { const env = new Environment(); const interpreter = new Interpreter(env); env.set("non_iterable", 10); const tokens = tokenize("{% for item in non_iterable %}{{ item }}{% endfor %}"); const ast = parse(tokens); expect(() => interpreter.run(ast)).toThrowError(); }); it("Invalid variable assignment", () => { const env = new Environment(); const interpreter = new Interpreter(env); const tokens = tokenize("{% set 42 = variable %}"); const ast = parse(tokens); expect(() => interpreter.run(ast)).toThrowError(); }); }); });