diff options
author | Daniel Weipert <git@mail.dweipert.de> | 2025-02-06 13:59:50 +0100 |
---|---|---|
committer | Daniel Weipert <git@mail.dweipert.de> | 2025-02-06 13:59:50 +0100 |
commit | 54b4040a8e46c4104e228264fa57b44d17e245c9 (patch) | |
tree | 938f077f1343ee018476bd1cf68924f9181143f1 /src/Interpreter | |
parent | 7667162a20ebdedac14de88260df018b961548d4 (diff) |
all current tests passing
Diffstat (limited to 'src/Interpreter')
-rw-r--r-- | src/Interpreter/Interpreter.php | 226 |
1 files changed, 141 insertions, 85 deletions
diff --git a/src/Interpreter/Interpreter.php b/src/Interpreter/Interpreter.php index 7d18551..e8b3c82 100644 --- a/src/Interpreter/Interpreter.php +++ b/src/Interpreter/Interpreter.php @@ -2,11 +2,14 @@ namespace Mnml\Interpreter; +use Closure; use Mnml\Lexer\Lexer; use Mnml\Lexer\Token; use Mnml\Lexer\TokenType; use Mnml\Parser\ArrayMapAccessNode; use Mnml\Parser\ArrayNode; +use Mnml\Parser\BoolNode; +use Mnml\Parser\CommentNode; use Mnml\Parser\CompareExpression; use Mnml\Parser\Condition; use Mnml\Parser\ConstVariableDeclaration; @@ -23,6 +26,7 @@ use Mnml\Parser\NumberNode; use Mnml\Parser\OperatorExpression; use Mnml\Parser\Parenthesis; use Mnml\Parser\Parser; +use Mnml\Parser\PipeExpression; use Mnml\Parser\StringNode; class Interpreter @@ -40,49 +44,28 @@ class Interpreter public function compile(array $parameters): Scope { $scope = new Scope(); - - $import = function (string $library) { - $input = file_get_contents(dirname(__FILE__) . "/" . $library); - $lexer = new Lexer($input); - $tokens = $lexer->lex(); - $parser = new Parser($tokens); - $nodes = $parser->parse(); - }; - $scope->add(new IdentifierNode(new Token(TokenType::Identifier, "import", "import", 0, 0)), $import); + // add standard library + $standardLibrary = include dirname(__DIR__) . "/standard.php"; + foreach ($standardLibrary as $identifier => $function) { + $scope->addExternal($identifier, $function); + } + foreach ($this->nodes as $currentNode) { $value = $this->evaluate($currentNode, $scope); - - if ($currentNode instanceof FunctionCall) { - var_dump($value); - } } return $scope; } - private function evaluate(Node $currentNode, Scope $scope): mixed + private function evaluate(Node $currentNode, Scope $scope): Node|null|bool { if ($currentNode instanceof ConstVariableDeclaration) { $identifier = $currentNode->identifier; - - if ($currentNode->expression instanceof NumberNode or $currentNode->expression instanceof StringNode) { - $value = $currentNode->expression; - } else { - $value = $this->evaluate($currentNode->expression, $scope); - } - + $value = $this->evaluate($currentNode->expression, $scope); $scope->add($identifier, $value); - return true; - } - - else if ($currentNode instanceof NumberNode) { - return $currentNode->value; - } - - else if ($currentNode instanceof StringNode) { - return $currentNode->token->value; + return $value; } else if ($currentNode instanceof IdentifierNode) { @@ -91,14 +74,6 @@ class Interpreter return $this->evaluate($variable, $scope); } - else if ($currentNode instanceof ArrayNode) { - return $currentNode; - } - - else if ($currentNode instanceof MapNode) { - return $currentNode; - } - else if ($currentNode instanceof ArrayMapAccessNode) { return $this->evaluateArrayOrMapAccess($currentNode, $scope); } @@ -108,67 +83,76 @@ class Interpreter } else if ($currentNode instanceof OperatorExpression) { - $left = $this->evaluate($currentNode->left, $scope); - $right = $this->evaluate($currentNode->right, $scope); + $left = $this->evaluate($currentNode->left, $scope)->getValue(); + $right = $this->evaluate($currentNode->right, $scope)->getValue(); if ($currentNode->operator->value == "+") { if (is_string($left) and is_string($right)) { - return $left . $right; + return new ValueNode($left . $right); } - return $left + $right; + return new ValueNode($left + $right); } else if ($currentNode->operator->value == "-") { - return $left - $right; + return new ValueNode($left - $right); } else if ($currentNode->operator->value == "*") { - return $left * $right; + return new ValueNode($left * $right); } else if ($currentNode->operator->value == "**") { - return pow($left, $right); + return new ValueNode(pow($left, $right)); } else if ($currentNode->operator->value == "/") { - return $left / $right; + return new ValueNode($left / $right); + } + + else if ($currentNode->operator->value == "%") { + return new ValueNode($left % $right); } } else if ($currentNode instanceof CompareExpression) { - $left = $this->evaluate($currentNode->left, $scope); - $right = $this->evaluate($currentNode->right, $scope); - + $left = $this->evaluate($currentNode->left, $scope)->getValue(); + $right = $this->evaluate($currentNode->right, $scope)->getValue(); + if ($currentNode->operator->value == "<") { - return $left < $right; + return new ValueNode($left < $right); } else if ($currentNode->operator->value == ">") { - return $left > $right; + return new ValueNode($left > $right); } else if ($currentNode->operator->value == "<=") { - return $left <= $right; + return new ValueNode($left <= $right); } else if ($currentNode->operator->value == ">=") { - return $left >= $right; + return new ValueNode($left >= $right); } else if ($currentNode->operator->value == "==") { - return $left == $right; + return new ValueNode($left == $right); } else if ($currentNode->operator->value == "!=") { - return $left != $right; + return new ValueNode($left != $right); } } else if ($currentNode instanceof IfNode) { foreach ($currentNode->arms as $arm) { /**@var IfArm $arm*/ - if ($this->evaluate($arm->condition, $scope)) { + $condition = true; + if ($arm->condition != null) { + $condition = $this->evaluate($arm->condition, $scope)->getValue(); + } + + if ($condition) { return $this->evaluateIfArmBody($arm, $scope); } } @@ -177,15 +161,15 @@ class Interpreter } else if ($currentNode instanceof Condition) { - $left = $this->evaluate($currentNode->left, $scope); - $right = $this->evaluate($currentNode->right, $scope); + $left = $this->evaluate($currentNode->left, $scope)->getValue(); + $right = $this->evaluate($currentNode->right, $scope)->getValue(); - if ($currentNode->operator == "and") { - return $left and $right; + if ($currentNode->operator->value == "and") { + return new ValueNode($left and $right); } - else if ($currentNode->operator == "or") { - return $left or $right; + else if ($currentNode->operator->value == "or") { + return new ValueNode($left or $right); } } @@ -196,15 +180,62 @@ class Interpreter else if ($currentNode instanceof FunctionCall) { $functionDefinition = $scope->get($currentNode->identifier); - return $this->evaluateFunction($functionDefinition, $currentNode->parameters, $scope); + if ($functionDefinition instanceof FunctionDefinition) { + return $this->evaluateFunction($functionDefinition, $currentNode->parameters, $scope); + } + else if ($functionDefinition instanceof Closure) { + return $this->evaluateClosure($functionDefinition, $currentNode->parameters, $scope); + } + + return $currentNode; + } + + else if ($currentNode instanceof PipeExpression) { + $left = $this->evaluate($currentNode->left, $scope); + + $pipeScope = new Scope($scope); + $pipeScope->add(new IdentifierNode(new Token(TokenType::Identifier, "$", "$", 0, 0)), $left); + + $right = $this->evaluate($currentNode->right, $pipeScope); + + return $right; + } + + else if ($currentNode instanceof NumberNode) { + return $currentNode; + } + + else if ($currentNode instanceof StringNode) { + return $currentNode; + } + + else if ($currentNode instanceof BoolNode) { + return $currentNode; + } + + else if ($currentNode instanceof ArrayNode) { + return $currentNode; + } + + else if ($currentNode instanceof MapNode) { + return $currentNode; } + else if ($currentNode instanceof ValueNode) { + return $currentNode; + } + + else if ($currentNode instanceof CommentNode) { + return $currentNode; + } + else if (is_callable($currentNode)) { return true; } else { echo sprintf("Compiler Error: Unhandled Node type %s." . PHP_EOL, get_class($currentNode)); + return false; } } @@ -215,34 +246,26 @@ class Interpreter $arrayOrMap = $this->evaluate($arrayOrMap, $scope); } - if ($arrayOrMap instanceof ArrayNode) { - $value = $arrayOrMap->values; - - foreach ($arrayOrMapAccess->items as $item) { - $evaluation = $this->evaluate($item, $scope); - $value = $value[$evaluation]; - } - - return $value; - } - - else if ($arrayOrMap instanceof MapNode) { - $values = $arrayOrMap->values; + $accessee = $arrayOrMap; + + foreach ($arrayOrMapAccess->items as $item) { + $evaluation = $this->evaluate($item, $scope)->getValue(); - foreach ($arrayOrMapAccess->items as $item) { - $evaluation = $this->evaluate($item, $scope); - - foreach ($values as $value) { - $key = $this->evaluate($value->key, $scope); + if ($accessee instanceof ArrayNode) { + $accessee = $accessee->values[$evaluation]; + } + else if ($accessee instanceof MapNode) { + foreach ($accessee->values as $value) { + $key = $this->evaluate($value->key, $scope)->getValue(); if ($key == $evaluation) { - $values = $this->evaluate($value->value, $scope); - # TODO: array inside map => create ArrayOrMapAccessItem with $left and $right + $accessee = $this->evaluate($value->value, $scope); + break; } } } - - return $values; } + + return $accessee; } /** @@ -279,6 +302,20 @@ class Interpreter echo sprintf("Function %s missing return value." . PHP_EOL); } + /** + * @param FunctionCallParameter[] $parameters + */ + private function evaluateClosure(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); + } + private function evaluateIfArmBody(IfArm $arm, Scope $outerScope): mixed { $scope = new Scope($outerScope); @@ -301,16 +338,19 @@ class Scope { private ?Scope $outerScope; public array $values; + public function __construct(?Scope $outerScope = null) { $this->outerScope = $outerScope; } + public function add(IdentifierNode $identifier, mixed $value): void { $key = $identifier->token->value; $this->values[$key] = $value; } + public function get(IdentifierNode $identifier): mixed { $key = $identifier->token->value; @@ -327,4 +367,20 @@ class Scope return $this->outerScope->get($identifier); } + + public function addExternal(string $identifier, mixed $value): void + { + $this->values[$identifier] = $value; + } +} + +class ValueNode extends Node +{ + public function __construct(private mixed $value) + {} + + public function getValue(): mixed + { + return $this->value; + } } |