diff options
| -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($)  | 
