summaryrefslogtreecommitdiff
path: root/src/Interpreter
diff options
context:
space:
mode:
authorDaniel Weipert <git@mail.dweipert.de>2025-02-06 13:59:50 +0100
committerDaniel Weipert <git@mail.dweipert.de>2025-02-06 13:59:50 +0100
commit54b4040a8e46c4104e228264fa57b44d17e245c9 (patch)
tree938f077f1343ee018476bd1cf68924f9181143f1 /src/Interpreter
parent7667162a20ebdedac14de88260df018b961548d4 (diff)
all current tests passing
Diffstat (limited to 'src/Interpreter')
-rw-r--r--src/Interpreter/Interpreter.php226
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;
+ }
}