Skip to content

Commit 5b6d22f

Browse files
feat: add support for null literals
1 parent b154a77 commit 5b6d22f

7 files changed

Lines changed: 33 additions & 4 deletions

File tree

src/AST/NullLiteral.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Codewithkyrian\Jinja\AST;
6+
7+
class NullLiteral extends Literal
8+
{
9+
public string $type = "NullLiteral";
10+
11+
public function __construct()
12+
{
13+
parent::__construct(null);
14+
}
15+
}

src/Core/Environment.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public function __construct(?Environment $parent = null)
7070
},
7171
'false' => fn(RuntimeValue $operand) => $operand->type === "BooleanValue" && !$operand->value,
7272
'true' => fn(RuntimeValue $operand) => $operand->type === "BooleanValue" && $operand->value,
73+
'null' => fn(RuntimeValue $operand) => $operand->type === "NullValue",
7374
'string' => fn(RuntimeValue $operand) => $operand->type === "StringValue",
7475
'number' => fn(RuntimeValue $operand) => $operand->type === "NumericValue",
7576
'integer' => function (RuntimeValue $operand) {

src/Core/Interpreter.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ function evaluate(?Statement $statement, Environment $environment): RuntimeValue
8282
case "BooleanLiteral":
8383
return new BooleanValue($statement->value);
8484

85+
case "NullLiteral":
86+
return new NullValue();
87+
8588
case "ArrayLiteral":
8689
$values = array_map(function ($x) use ($environment) {
8790
return $this->evaluate($x, $environment);

src/Core/Lexer.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,8 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool
121121
// Consume (and ignore) all whitespace inside Jinja statements or expressions
122122
$consumeWhile(fn($char) => preg_match('/\s/', $char));
123123

124-
// if ($cursorPosition >= $srcLength) break; // End of input check
125-
126124
$char = $src[$cursorPosition];
127125

128-
129126
// Check for unary operators
130127
if ($char === "-" || $char === "+") {
131128
$lastTokenType = end($tokens)->type;
@@ -136,6 +133,7 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool
136133
case TokenType::Identifier:
137134
case TokenType::NumericLiteral:
138135
case TokenType::BooleanLiteral:
136+
case TokenType::NullLiteral:
139137
case TokenType::StringLiteral:
140138
case TokenType::CloseParen:
141139
case TokenType::CloseSquareBracket:

src/Core/Parser.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Codewithkyrian\Jinja\AST\KeywordArgumentExpression;
1616
use Codewithkyrian\Jinja\AST\Macro;
1717
use Codewithkyrian\Jinja\AST\MemberExpression;
18+
use Codewithkyrian\Jinja\AST\NullLiteral;
1819
use Codewithkyrian\Jinja\AST\NumericLiteral;
1920
use Codewithkyrian\Jinja\AST\ObjectLiteral;
2021
use Codewithkyrian\Jinja\AST\Program;
@@ -541,16 +542,20 @@ private function parseMultiplicativeExpression(): Statement
541542
private function parseTestExpression(): Statement
542543
{
543544
$operand = $this->parseFilterExpression();
545+
544546
while ($this->is(TokenType::Is)) {
545547
$this->current++; // consume is
546548
$negate = $this->is(TokenType::Not);
549+
547550
if ($negate) {
548551
$this->current++; // consume not
549552
}
553+
550554
$filter = $this->parsePrimaryExpression();
551555
if ($filter instanceof BooleanLiteral) {
552-
// PHP version: treating boolean literals as identifiers might require manual handling
553556
$filter = new Identifier((string)$filter->value);
557+
} elseif ($filter instanceof NullLiteral) {
558+
$filter = new Identifier("none");
554559
}
555560
if (!($filter instanceof Identifier)) {
556561
throw new SyntaxError("Expected identifier for the test");
@@ -597,6 +602,10 @@ private function parsePrimaryExpression(): Statement
597602
$this->current++;
598603
return new BooleanLiteral(strtolower($token->value) === "true");
599604

605+
case TokenType::NullLiteral:
606+
$this->current++;
607+
return new NullLiteral();
608+
600609
case TokenType::Identifier:
601610
$this->current++;
602611
return new Identifier($token->value);

src/Core/Token.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ class Token
2828
// Literals
2929
'true' => TokenType::BooleanLiteral,
3030
'false' => TokenType::BooleanLiteral,
31+
'none' => TokenType::NullLiteral,
3132
'True' => TokenType::BooleanLiteral,
3233
'False' => TokenType::BooleanLiteral,
34+
'None' => TokenType::NullLiteral,
3335
];
3436

3537
public const ORDERED_MAPPING_TABLE = [

src/Core/TokenType.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ enum TokenType: string
1414
case Text = "Text";
1515
case NumericLiteral = "NumericLiteral"; // e.g., 123
1616
case BooleanLiteral = "BooleanLiteral"; // true or false
17+
case NullLiteral = "NullLiteral"; // null
1718
case StringLiteral = "StringLiteral"; // 'string'
1819
case Identifier = "Identifier"; // Variables, functions, etc.
1920
case Equals = "Equals"; // =

0 commit comments

Comments
 (0)