summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Interpreter/Interpreter.php98
-rw-r--r--src/Parser/Parser.php66
-rw-r--r--src/standard.php21
-rw-r--r--test/self/lexer.mnml22
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($)