diff options
-rw-r--r-- | src/Parser/Parser.php | 167 | ||||
-rw-r--r-- | test/const-function-if.mnml | 8 | ||||
-rw-r--r-- | test/parentheses.mnml | 17 |
3 files changed, 129 insertions, 63 deletions
diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index 41c8a14..05d3726 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -43,27 +43,29 @@ class Parser $currentStatement = new CommentNode($currentToken); $this->advance(1); } - + else { - $currentStatement = $this->parseFunctionCall(); + #$currentStatement = $this->parseFunctionCall(); + $error = sprintf("Unexpected %s at %d:%d" . PHP_EOL, $currentToken->value, $currentToken->line, $currentToken->column); + $this->addError($error); + + exit; } $nextToken = $this->getCurrentToken(); - + if ($nextToken->literal == "=>") { $this->advance(1); - + $currentStatement = new PipeExpression($currentStatement, $this->parseExpression(shouldBeFunction: true)); } - + // unknown token if ($this->position == $lastPosition) { $error = sprintf("Unknown token %s at position %d,%d" . PHP_EOL, $currentToken, $currentToken->line, $currentToken->column); - $this->errors[] = $error; + $this->addError($error); $this->advance(1); - - echo $error; } $this->nodes[] = $currentStatement; @@ -87,7 +89,7 @@ class Parser echo sprintf(str_repeat("> ", $level) . "%s: %s" . PHP_EOL, $propertyKey, (new \ReflectionClass($propertyValue))->getShortName()); $this->printNodeRecursive($propertyValue, $level + 1); } - + else if (is_array($propertyValue)) { $length = count($propertyValue); @@ -122,15 +124,15 @@ class Parser return $this->tokens[$this->position]; } - private function getNextToken(): ?Token + private function getNextToken(int $steps = 1): ?Token { - return $this->tokens[$this->position + 1] ?? null; + return $this->tokens[$this->position + $steps] ?? null; } private function advance(int $steps): void { $lastToken = $this->getCurrentToken(); - + if ($this->position + $steps > count($this->tokens)) { if (! empty($this->errors)) { exit; @@ -141,7 +143,7 @@ class Parser "Parser Implementation Error: Can't advance. Position out of bounds." ); } - + $this->position += $steps; } @@ -161,15 +163,20 @@ class Parser if ($currentToken->value != $value) { $error = sprintf("Expected %s but got %s instead at %d:%d" . PHP_EOL, $value, $currentToken->value, $currentToken->line, $currentToken->column); - $this->errors[] = $error; - - echo $error; + $this->addError($error); } - + $this->advance(1); } } + private function addError(string $error): void + { + $this->errors[] = $error; + + echo $error; + } + private function parseConst(): Node { // skip const @@ -185,7 +192,7 @@ class Parser // skip = $this->anticipateTokenAndSkip("="); - + $expression = $this->parseExpression( shouldBeMap: $type instanceof MapTypeDeclaration, shouldBeFunction: $type instanceof TypeDeclaration and $type->left->literal == "function", @@ -199,7 +206,7 @@ class Parser } private function parseType(): Node - { + { $currentToken = $this->getCurrentToken(); $nextToken = $this->getNextToken(); @@ -209,13 +216,13 @@ class Parser else if (in_array($nextToken->literal, ["and", "or"])) { $this->advance(2); - + return new TypeDeclaration($currentToken, $nextToken, $this->parseType()); } else { $this->advance(1); - + return new TypeDeclaration($currentToken); } } @@ -224,7 +231,7 @@ class Parser { // skip first [ $this->anticipateTokenAndSkip("["); - + $key = $this->parseType(); $currentToken = $this->getCurrentToken(); @@ -233,7 +240,7 @@ class Parser if ($currentToken->literal == "]" && $nextToken->literal == "[") { // skip to first type $this->advance(2); - + $value = $this->parseType(); // skip last ] @@ -252,14 +259,27 @@ class Parser { $currentToken = $this->getCurrentToken(); $currentExpression = $currentToken; - + if ($currentToken->literal == "[") { $currentExpression = $this->parseArrayOrMap($shouldBeMap); } else if ($currentToken->type == TokenType::Identifier) { - $currentExpression = new IdentifierNode($currentToken); - $this->advance(1); + if (in_array($currentToken->literal, ["true", "false"])) { + $currentExpression = new BoolNode($currentToken); + $this->advance(1); + } + else { + $currentExpression = new IdentifierNode($currentToken); + $this->advance(1); + + if ($this->getCurrentToken()->literal == "(") { + // step back to parse function call fully + $this->stepBack(1); + + $currentExpression = $this->parseFunctionCall(); + } + } } else if ($currentToken->type == TokenType::Number) { @@ -272,11 +292,11 @@ class Parser } else if ($currentToken->literal == "(") { - #if ($shouldBeFunction) { + if ($this->getNextToken(2)->literal == ":") { $currentExpression = $this->parseFunctionDefinition(); - #} - - # TODO: parse normal () + } else { + $currentExpression = $this->parseParenthesis(); + } } else { @@ -288,7 +308,7 @@ class Parser if (in_array($nextToken->literal, ["+", "-", "*", "**", "/"])) { $this->advance(1); - + return new OperatorExpression($currentExpression, $nextToken, $this->parseExpression(shouldBeMap: $shouldBeMap)); } @@ -297,7 +317,7 @@ class Parser return new CompareExpression($currentExpression, $nextToken, $this->parseExpression(shouldBeMap: $shouldBeMap)); } - + else if (in_array($nextToken->literal, ["and", "or"])) { $this->advance(1); @@ -306,17 +326,10 @@ class Parser else if ($nextToken->literal == "=>") { $this->advance(1); - + return new PipeExpression($currentExpression, $this->parseExpression(shouldBeFunction: true)); } - else if ($currentToken->type == TokenType::Identifier and $nextToken->literal == "(") { - // step back to parse function call fully - $this->stepBack(1); - - return $this->parseFunctionCall(); - } - else { return $currentExpression; } @@ -334,7 +347,7 @@ class Parser $tokenShouldBeComma = false; while ($this->getCurrentToken()->literal != "]") { $currentToken = $this->getCurrentToken(); - + // skip , if ($tokenShouldBeComma) { if ($currentToken->literal == ",") { @@ -358,7 +371,7 @@ class Parser break; } } - + // nested array or map if ($currentToken->literal == "[") { $values[] = $this->parseArrayOrMap(); @@ -379,7 +392,7 @@ class Parser // skip last ] $this->advance(1); - + if ((count($values) > 0 and $values[0] instanceof MapItemNode) or $shouldBeMap) { return new MapNode($values); } else { @@ -394,7 +407,7 @@ class Parser // is map item if ($this->getCurrentToken()->literal == "=") { $this->advance(1); - + $value = $this->parseExpression(); return new MapItemNode($key, $value); @@ -409,7 +422,7 @@ class Parser private function parseNumber(): Node { $currentToken = $this->getCurrentToken(); - + $value = $currentToken->value; if (str_contains($value, ".")) { $value = floatval($value); @@ -449,14 +462,14 @@ class Parser } $parameters = []; - + while ($this->getCurrentToken()->literal != ")") { // skip , if ($this->getCurrentToken()->literal == ",") { $this->advance(1); continue; } - + $identifier = $this->getCurrentToken(); $this->advance(1); @@ -464,7 +477,7 @@ class Parser $this->anticipateTokenAndSkip(":"); $type = $this->parseType(); - + // skip potential , instead default value if ($this->getCurrentToken()->literal == ",") { $this->advance(1); @@ -496,12 +509,12 @@ class Parser $this->anticipateTokenAndSkip("{"); $body = []; - + while ($this->getCurrentToken()->literal != "}") { $currentToken = $this->getCurrentToken(); - + if ($currentToken->literal == "const") { - $body[] = $this->parseConst(); + $body[] = $this->parseConst(); } else if ($currentToken->literal == "return") { @@ -512,14 +525,20 @@ class Parser $body[] = $this->parseIf(); } - // skip comments else if ($currentToken->type == TokenType::Comment) { + $body[] = new CommentNode($currentToken); $this->advance(1); - continue; } - else { + /*else if ($currentToken->type == TokenType::Identifier) { $body[] = $this->parseFunctionCall(); + }*/ + + else { + $error = sprintf("Unexpected %s at %d:%d" . PHP_EOL, $currentToken->value, $currentToken->line, $currentToken->column); + $this->addError($error); + + exit; } } @@ -562,19 +581,18 @@ class Parser $parameters = []; - while ($this->getCurrentToken()->literal != ")") { // skip , if ($this->getCurrentToken()->literal == ",") { $this->advance(1); continue; } - + // if "=" then identifier is name if ($this->getNextToken()->literal == "=") { $identifier = $this->getCurrentToken(); $this->advance(2); - + $value = $this->parseExpression(); $parameters[] = new FunctionCallParameter( @@ -616,7 +634,7 @@ class Parser private function parseIfArm(): Node { $isElseBlock = false; - + if ($this->getCurrentToken()->literal == "if") { $this->advance(1); } @@ -641,12 +659,25 @@ class Parser } $body = $this->parseFunctionBody(); - + return new IfArm( $condition, $body, ); } + + private function parseParenthesis(): Node + { + // skip first ( + $this->anticipateTokenAndSkip("("); + + $content = $this->parseExpression(); + + // skip last ) + $this->anticipateTokenAndSkip(")"); + + return new Parenthesis($content); + } } class Node @@ -677,7 +708,7 @@ class TypeDeclaration extends Node public Token $left, public ?Token $operator = null, public Token|TypeDeclaration|null $right = null, - ) {} + ) {} } class ArrayTypeDeclaration extends Node @@ -727,7 +758,7 @@ class OperatorExpression extends Node public Token|Node $left, public Token $operator, public Token|Node $right, - ) {} + ) {} } class CompareExpression extends Node @@ -770,6 +801,13 @@ class StringNode extends Node ) {} } +class BoolNode extends Node +{ + public function __construct( + public Token $token, + ) {} +} + class CommentNode extends Node { public function __construct( @@ -852,3 +890,10 @@ class PipeExpression extends Node public Token|Node $right, ) {} } + +class Parenthesis extends Node +{ + public function __construct( + public Node $content, + ) {} +} diff --git a/test/const-function-if.mnml b/test/const-function-if.mnml index 48fc4fb..11883cd 100644 --- a/test/const-function-if.mnml +++ b/test/const-function-if.mnml @@ -1,4 +1,4 @@ -const main: function = (input: string): void { +const main: function = (input: string): string { if (input == "hello") { const bye: string = "bye" } @@ -6,4 +6,8 @@ const main: function = (input: string): void { return input } -main(input = "hello") +const main2: function = (input: string): string { + const bye: string = main(input = "bye") + + return main(input = "hello") +} diff --git a/test/parentheses.mnml b/test/parentheses.mnml new file mode 100644 index 0000000..d18aee2 --- /dev/null +++ b/test/parentheses.mnml @@ -0,0 +1,17 @@ +const main: function = (input: string): string { + if ((input == "hello") or input == "bye") { + const bye: string = "bye" + } + + return input +} + +const main2: function = (input: string): string { + const bye: string = main(input = "bye") + + const test: bool = (5 > 6) and ((7 < 8) or (true != false)) + + const test2: string = ((main(input = "test") => main(input = $)) + "test2") + + return main(input = "hello") + bye +} |