diff options
author | Daniel Weipert <git@mail.dweipert.de> | 2025-02-25 13:24:02 +0100 |
---|---|---|
committer | Daniel Weipert <git@mail.dweipert.de> | 2025-02-25 13:24:02 +0100 |
commit | 65b36f1b321a71578cd8b568b4a04ef27c6b714a (patch) | |
tree | 6b1f69dbecff1b7e08e5efcd2787769a91671023 | |
parent | 54b4040a8e46c4104e228264fa57b44d17e245c9 (diff) |
-rw-r--r-- | src/Interpreter/Interpreter.php | 98 | ||||
-rw-r--r-- | src/Parser/Parser.php | 66 | ||||
-rw-r--r-- | src/standard.php | 21 | ||||
-rw-r--r-- | test/self/lexer.mnml | 22 |
4 files changed, 185 insertions, 22 deletions
diff --git a/src/Interpreter/Interpreter.php b/src/Interpreter/Interpreter.php index e8b3c82..eed0004 100644 --- a/src/Interpreter/Interpreter.php +++ b/src/Interpreter/Interpreter.php @@ -28,6 +28,7 @@ use Mnml\Parser\Parenthesis; use Mnml\Parser\Parser; use Mnml\Parser\PipeExpression; use Mnml\Parser\StringNode; +use Mnml\Parser\VariableAssignment; class Interpreter { @@ -62,12 +63,46 @@ class Interpreter { if ($currentNode instanceof ConstVariableDeclaration) { $identifier = $currentNode->identifier; - $value = $this->evaluate($currentNode->expression, $scope); - $scope->add($identifier, $value); + $value = new ValueNode(null); + + /*if ($scope->has($identifier)) { + echo sprintf("%s already declared in current scope." . PHP_EOL, $identifier->token->value); + }*/ # TODO: problems with recursive functions. clear scope somehow? + + #else { + if (! is_null($currentNode->expression)) { + $value = $this->evaluate($currentNode->expression, $scope); + } + + $scope->add($identifier, $value); + #} return $value; } + if ($currentNode instanceof VariableAssignment) { + $identifier = $currentNode->identifier; + $value = new ValueNode(null); + + if ($scope->has($identifier)) { + $curentValue = $scope->get($identifier)->getValue(); + + if (is_null($curentValue)) { + $value = $this->evaluate($currentNode->expression, $scope); + $scope->set($identifier, $value); + } + else { + echo sprintf("%s already assigned in current scope." . PHP_EOL, $identifier->token->value); + } + } + + else { + echo sprintf("%s is not declared in current scope." . PHP_EOL, $identifier->token->value); + } + + return $value; + } + else if ($currentNode instanceof IdentifierNode) { $variable = $scope->get($currentNode); @@ -181,10 +216,10 @@ class Interpreter $functionDefinition = $scope->get($currentNode->identifier); if ($functionDefinition instanceof FunctionDefinition) { - return $this->evaluateFunction($functionDefinition, $currentNode->parameters, $scope); + return $this->evaluateFunction($currentNode->identifier, $functionDefinition, $currentNode->parameters, $scope); } else if ($functionDefinition instanceof Closure) { - return $this->evaluateClosure($functionDefinition, $currentNode->parameters, $scope); + return $this->evaluateClosure($currentNode->identifier, $functionDefinition, $currentNode->parameters, $scope); } return $currentNode; @@ -263,6 +298,9 @@ class Interpreter } } } + else if ($accessee instanceof ValueNode) { + $accessee = new ValueNode($accessee->getValue()[$evaluation]); + } } return $accessee; @@ -271,15 +309,15 @@ class Interpreter /** * @param FunctionCallParameter[] $parameters */ - private function evaluateFunction(FunctionDefinition $function, array $parameters, Scope $outerScope): mixed + private function evaluateFunction(IdentifierNode $identifier, FunctionDefinition $function, array $parameters, Scope $outerScope): mixed { $scope = new Scope($outerScope); foreach ($parameters as $parameter) { /**@var FunctionCallParameter $parameter*/ - $scope->add($parameter->identifier, $parameter->value); + $scope->add($parameter->identifier, $this->evaluate($parameter->value, $outerScope)); } - + foreach ($function->body as $currentNode) { if ($currentNode instanceof FunctionReturn) { return $this->evaluate($currentNode->returnValue, $scope); @@ -299,21 +337,21 @@ class Interpreter } } - echo sprintf("Function %s missing return value." . PHP_EOL); + echo sprintf("Function %s missing return value." . PHP_EOL, $identifier->token->value); } /** * @param FunctionCallParameter[] $parameters */ - private function evaluateClosure(Closure $function, array $parameters, Scope $scope): mixed + private function evaluateClosure(IdentifierNode $identifier, Closure $function, array $parameters, Scope $scope): mixed { $evaluatedParameters = []; foreach ($parameters as $parameter) { /**@var FunctionCallParameter $parameter*/ $evaluatedParameters[] = $this->evaluate($parameter->value, $scope)->getValue(); } - - return call_user_func_array($function, $evaluatedParameters); + + return new ValueNode(call_user_func_array($function, $evaluatedParameters)); } private function evaluateIfArmBody(IfArm $arm, Scope $outerScope): mixed @@ -356,7 +394,11 @@ class Scope $key = $identifier->token->value; if (isset($this->values[$key])) { - return $this->values[$key]; + $value = $this->values[$key]; + if ($value instanceof IdentifierNode and $value->token->value == $key) {} + else { + return $this->values[$key]; + } } if (! isset($this->outerScope)) { @@ -368,6 +410,38 @@ class Scope return $this->outerScope->get($identifier); } + public function set(IdentifierNode $identifier, mixed $value): void + { + $key = $identifier->token->value; + + if (isset($this->values[$key])) { + $this->values[$key] = $value; + } + + else if (isset($this->outerScope)){ + $this->outerScope->set($identifier, $value); + } + + else { + echo sprintf("Can't set nonexistent variable %s." . PHP_EOL, $identifier->token->value); + } + } + + public function has(IdentifierNode $identifier): bool + { + $has = isset($this->values[$identifier->token->value]); + + if ($has) { + return $has; + } + + if (isset($this->outerScope)) { + return $this->outerScope->has($identifier); + } + + return false; + } + public function addExternal(string $identifier, mixed $value): void { $this->values[$identifier] = $value; diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php index a3a6839..b0b98f2 100644 --- a/src/Parser/Parser.php +++ b/src/Parser/Parser.php @@ -43,9 +43,14 @@ class Parser $currentStatement = new CommentNode($currentToken); $this->advance(1); } - + else if ($currentToken->type == TokenType::Identifier) { - $currentStatement = $this->parseFunctionCall(); + if ($this->getNextToken()->literal == "=") { + $currentStatement = $this->parseVariableAssignment(); + } + else { + $currentStatement = $this->parseFunctionCall(); + } } else { @@ -193,17 +198,39 @@ class Parser $type = $this->parseType(); + $expression = null; + if ($this->getCurrentToken()->literal == "=") { + // skip = + $this->anticipateTokenAndSkip("="); + + $expression = $this->parseExpression( + shouldBeMap: $type instanceof MapTypeDeclaration, + shouldBeFunction: $type instanceof TypeDeclaration and $type->left->literal == "function", + ); + } + + return new ConstVariableDeclaration( + $identifier, + $type, + $expression, + ); + } + + private function parseVariableAssignment(): Node + { + $identifier = new IdentifierNode($this->getCurrentToken()); + $this->advance(1); + // skip = $this->anticipateTokenAndSkip("="); - + $expression = $this->parseExpression( - shouldBeMap: $type instanceof MapTypeDeclaration, - shouldBeFunction: $type instanceof TypeDeclaration and $type->left->literal == "function", + #shouldBeMap: $type instanceof MapTypeDeclaration, + #shouldBeFunction: $type instanceof TypeDeclaration and $type->left->literal == "function", ); - return new ConstVariableDeclaration( + return new VariableAssignment( $identifier, - $type, $expression, ); } @@ -565,7 +592,12 @@ class Parser } else if ($currentToken->type == TokenType::Identifier) { - $body[] = $this->parseFunctionCall(); + if ($this->getNextToken()->literal == "=") { + $body[] = $this->parseVariableAssignment(); + } + else { + $body[] = $this->parseFunctionCall(); + } } else { @@ -732,6 +764,14 @@ class ConstVariableDeclaration extends Node public function __construct( public Node $identifier, public Node|Token $type, + public Node|Token|null $expression, + ) {} +} + +class VariableAssignment extends Node +{ + public function __construct( + public Node $identifier, public Node|Token $expression, ) {} } @@ -767,6 +807,11 @@ class ArrayNode extends Node public function __construct( public array $values, ) {} + + public function getValue(): array + { + return $this->values; + } } class MapNode extends Node @@ -777,6 +822,11 @@ class MapNode extends Node public function __construct( public array $values, ) {} + + public function getValue(): array + { + return $this->values; + } } class MapItemNode extends Node diff --git a/src/standard.php b/src/standard.php index 351c9e8..6cc6be6 100644 --- a/src/standard.php +++ b/src/standard.php @@ -11,8 +11,25 @@ $import = function (string $library) { $nodes = $parser->parse(); }; -$print = function (string $string) { +$print = function (string $string): void { echo $string; }; -return compact("import", "print"); +$dump = function (mixed $value): void { + var_dump($value); +}; + +$strlen = function (string $string) { + return strlen($string); +}; + +$get_char = function (string $input, int $position): string { + return $input[$position]; +}; + +$array_append = function (array $array, mixed $value): array { + $array[] = $value; + return $array; +}; + +return compact("import", "print", "dump", "strlen", "get_char", "array_append"); diff --git a/test/self/lexer.mnml b/test/self/lexer.mnml new file mode 100644 index 0000000..8468f66 --- /dev/null +++ b/test/self/lexer.mnml @@ -0,0 +1,22 @@ +const main: function = (input: string): array { + return lex(input = input, position = 0, output = []) +} + +const lex: function = (input: string, position: integer, output: array): array { + if (position == (strlen(input))) { + return output + } + + const current_char: string = get_char(input, position) + + const appended_output: array + if (current_char != " ") { + appended_output = array_append(output, current_char) + } else { + appended_output = output + } + + return lex(input = input, position = (position + 1), output = appended_output) +} + +main(input = "a b c d e f g") => dump($) |