summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmnml6
-rw-r--r--src/Interpreter/Interpreter.php460
-rw-r--r--src/Lexer/Lexer.php15
-rw-r--r--src/Parser/Parser.php149
-rw-r--r--src/standard.php35
-rw-r--r--test/const-array-complex.mnml2
-rw-r--r--test/const-array.mnml8
-rw-r--r--test/const-function-if.mnml5
-rw-r--r--test/const-function.mnml2
-rw-r--r--test/const-map.mnml7
-rw-r--r--test/const-simple.mnml2
-rw-r--r--test/if.mnml2
-rw-r--r--test/parentheses.mnml7
-rw-r--r--test/self/lexer.mnml22
-rw-r--r--test/test.mnml4
15 files changed, 695 insertions, 31 deletions
diff --git a/mnml b/mnml
index 33f7ca0..32937d5 100755
--- a/mnml
+++ b/mnml
@@ -4,6 +4,7 @@
require "vendor/autoload.php";
+use Mnml\Interpreter\Interpreter;
use Mnml\Lexer\Lexer;
use Mnml\Parser\Parser;
@@ -14,4 +15,7 @@ $tokens = $lexer->lex();
$parser = new Parser($tokens);
$nodes = $parser->parse();
-$parser->printTree();
+#$parser->printTree();
+
+$compiler = new Interpreter($nodes);
+$compiler->compile([array_slice($argv, 1)]);
diff --git a/src/Interpreter/Interpreter.php b/src/Interpreter/Interpreter.php
new file mode 100644
index 0000000..eed0004
--- /dev/null
+++ b/src/Interpreter/Interpreter.php
@@ -0,0 +1,460 @@
+<?php
+
+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;
+use Mnml\Parser\FunctionCall;
+use Mnml\Parser\FunctionCallParameter;
+use Mnml\Parser\FunctionDefinition;
+use Mnml\Parser\FunctionReturn;
+use Mnml\Parser\IdentifierNode;
+use Mnml\Parser\IfArm;
+use Mnml\Parser\IfNode;
+use Mnml\Parser\MapNode;
+use Mnml\Parser\Node;
+use Mnml\Parser\NumberNode;
+use Mnml\Parser\OperatorExpression;
+use Mnml\Parser\Parenthesis;
+use Mnml\Parser\Parser;
+use Mnml\Parser\PipeExpression;
+use Mnml\Parser\StringNode;
+use Mnml\Parser\VariableAssignment;
+
+class Interpreter
+{
+ private array $nodes;
+
+ /**
+ * @param Node[] $nodes
+ */
+ public function __construct(array $nodes)
+ {
+ $this->nodes = $nodes;
+ }
+
+ public function compile(array $parameters): Scope
+ {
+ $scope = new Scope();
+
+ // 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);
+ }
+
+ return $scope;
+ }
+
+ private function evaluate(Node $currentNode, Scope $scope): Node|null|bool
+ {
+ if ($currentNode instanceof ConstVariableDeclaration) {
+ $identifier = $currentNode->identifier;
+ $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);
+
+ return $this->evaluate($variable, $scope);
+ }
+
+ else if ($currentNode instanceof ArrayMapAccessNode) {
+ return $this->evaluateArrayOrMapAccess($currentNode, $scope);
+ }
+
+ else if ($currentNode instanceof Parenthesis) {
+ return $this->evaluate($currentNode->content, $scope);
+ }
+
+ else if ($currentNode instanceof OperatorExpression) {
+ $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 new ValueNode($left . $right);
+ }
+
+ return new ValueNode($left + $right);
+ }
+
+ else if ($currentNode->operator->value == "-") {
+ return new ValueNode($left - $right);
+ }
+
+ else if ($currentNode->operator->value == "*") {
+ return new ValueNode($left * $right);
+ }
+
+ else if ($currentNode->operator->value == "**") {
+ return new ValueNode(pow($left, $right));
+ }
+
+ else if ($currentNode->operator->value == "/") {
+ 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)->getValue();
+ $right = $this->evaluate($currentNode->right, $scope)->getValue();
+
+ if ($currentNode->operator->value == "<") {
+ return new ValueNode($left < $right);
+ }
+
+ else if ($currentNode->operator->value == ">") {
+ return new ValueNode($left > $right);
+ }
+
+ else if ($currentNode->operator->value == "<=") {
+ return new ValueNode($left <= $right);
+ }
+
+ else if ($currentNode->operator->value == ">=") {
+ return new ValueNode($left >= $right);
+ }
+
+ else if ($currentNode->operator->value == "==") {
+ return new ValueNode($left == $right);
+ }
+
+ else if ($currentNode->operator->value == "!=") {
+ return new ValueNode($left != $right);
+ }
+ }
+
+ else if ($currentNode instanceof IfNode) {
+ foreach ($currentNode->arms as $arm) {
+ /**@var IfArm $arm*/
+ $condition = true;
+ if ($arm->condition != null) {
+ $condition = $this->evaluate($arm->condition, $scope)->getValue();
+ }
+
+ if ($condition) {
+ return $this->evaluateIfArmBody($arm, $scope);
+ }
+ }
+
+ return null;
+ }
+
+ else if ($currentNode instanceof Condition) {
+ $left = $this->evaluate($currentNode->left, $scope)->getValue();
+ $right = $this->evaluate($currentNode->right, $scope)->getValue();
+
+ if ($currentNode->operator->value == "and") {
+ return new ValueNode($left and $right);
+ }
+
+ else if ($currentNode->operator->value == "or") {
+ return new ValueNode($left or $right);
+ }
+ }
+
+ else if ($currentNode instanceof FunctionDefinition) {
+ return $currentNode;
+ }
+
+ else if ($currentNode instanceof FunctionCall) {
+ $functionDefinition = $scope->get($currentNode->identifier);
+
+ if ($functionDefinition instanceof FunctionDefinition) {
+ return $this->evaluateFunction($currentNode->identifier, $functionDefinition, $currentNode->parameters, $scope);
+ }
+ else if ($functionDefinition instanceof Closure) {
+ return $this->evaluateClosure($currentNode->identifier, $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;
+ }
+ }
+
+ private function evaluateArrayOrMapAccess(ArrayMapAccessNode $arrayOrMapAccess, Scope $scope): Node
+ {
+ $arrayOrMap = $arrayOrMapAccess->arrayOrMap;
+ if ($arrayOrMap instanceof IdentifierNode) {
+ $arrayOrMap = $this->evaluate($arrayOrMap, $scope);
+ }
+
+ $accessee = $arrayOrMap;
+
+ foreach ($arrayOrMapAccess->items as $item) {
+ $evaluation = $this->evaluate($item, $scope)->getValue();
+
+ 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) {
+ $accessee = $this->evaluate($value->value, $scope);
+ break;
+ }
+ }
+ }
+ else if ($accessee instanceof ValueNode) {
+ $accessee = new ValueNode($accessee->getValue()[$evaluation]);
+ }
+ }
+
+ return $accessee;
+ }
+
+ /**
+ * @param FunctionCallParameter[] $parameters
+ */
+ 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, $this->evaluate($parameter->value, $outerScope));
+ }
+
+ foreach ($function->body as $currentNode) {
+ if ($currentNode instanceof FunctionReturn) {
+ return $this->evaluate($currentNode->returnValue, $scope);
+ }
+
+ else if ($currentNode instanceof IfNode) {
+ $returnValue = $this->evaluate($currentNode, $scope);
+
+ // return if "if" had a return inside
+ if ($returnValue) {
+ return $returnValue;
+ }
+ }
+
+ else {
+ $this->evaluate($currentNode, $scope);
+ }
+ }
+
+ echo sprintf("Function %s missing return value." . PHP_EOL, $identifier->token->value);
+ }
+
+ /**
+ * @param FunctionCallParameter[] $parameters
+ */
+ 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 new ValueNode(call_user_func_array($function, $evaluatedParameters));
+ }
+
+ private function evaluateIfArmBody(IfArm $arm, Scope $outerScope): mixed
+ {
+ $scope = new Scope($outerScope);
+
+ foreach ($arm->body as $currentNode) {
+ if ($currentNode instanceof FunctionReturn) {
+ return $this->evaluate($currentNode->returnValue, $scope);
+ }
+
+ else {
+ $this->evaluate($currentNode, $scope);
+ }
+ }
+
+ return null;
+ }
+}
+
+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;
+
+ if (isset($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)) {
+ echo sprintf("Undefined variable \"%s\" at %d:%d" . PHP_EOL, $key, $identifier->token->line, $identifier->token->column);
+
+ return null;
+ }
+
+ 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;
+ }
+}
+
+class ValueNode extends Node
+{
+ public function __construct(private mixed $value)
+ {}
+
+ public function getValue(): mixed
+ {
+ return $this->value;
+ }
+}
diff --git a/src/Lexer/Lexer.php b/src/Lexer/Lexer.php
index bb2a809..98ac5e2 100644
--- a/src/Lexer/Lexer.php
+++ b/src/Lexer/Lexer.php
@@ -27,7 +27,7 @@ class Lexer
"(", ")",
"[", "]",
"{", "}",
- "$", ".",
+ ".",
];
$lastPosition = -1;
@@ -238,6 +238,18 @@ class Lexer
}
}
+ // pipe placeholder
+ else if ($currentChar == "$") {
+ $output[] = new Token(
+ TokenType::PipePlaceholder,
+ $currentChar,
+ $currentChar,
+ $this->line,
+ $startColumn,
+ );
+ $this->advance(1);
+ }
+
// single char tokens
else if (in_array($currentChar, $singleCharTokens)) {
$output[] = new Token(
@@ -489,5 +501,6 @@ enum TokenType {
case Operator;
case Assign;
case Pipe;
+ case PipePlaceholder;
case EndOfFile;
}
diff --git a/src/Parser/Parser.php b/src/Parser/Parser.php
index 05d3726..b0b98f2 100644
--- a/src/Parser/Parser.php
+++ b/src/Parser/Parser.php
@@ -43,9 +43,17 @@ class Parser
$currentStatement = new CommentNode($currentToken);
$this->advance(1);
}
+
+ else if ($currentToken->type == TokenType::Identifier) {
+ if ($this->getNextToken()->literal == "=") {
+ $currentStatement = $this->parseVariableAssignment();
+ }
+ else {
+ $currentStatement = $this->parseFunctionCall();
+ }
+ }
else {
- #$currentStatement = $this->parseFunctionCall();
$error = sprintf("Unexpected %s at %d:%d" . PHP_EOL, $currentToken->value, $currentToken->line, $currentToken->column);
$this->addError($error);
@@ -182,7 +190,7 @@ class Parser
// skip const
$this->anticipateTokenAndSkip("const");
- $identifier = $this->getCurrentToken();
+ $identifier = new IdentifierNode($this->getCurrentToken());
$this->advance(1);
// skip :
@@ -190,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,
);
}
@@ -291,6 +321,11 @@ class Parser
$this->advance(1);
}
+ else if ($currentToken->type == TokenType::PipePlaceholder) {
+ $currentExpression = new IdentifierNode($currentToken);
+ $this->advance(1);
+ }
+
else if ($currentToken->literal == "(") {
if ($this->getNextToken(2)->literal == ":") {
$currentExpression = $this->parseFunctionDefinition();
@@ -324,6 +359,10 @@ class Parser
return new Condition($currentExpression, $nextToken, $this->parseExpression(shouldBeMap: $shouldBeMap));
}
+ else if ($nextToken->literal == "[") {
+ return $this->parseArrayOrMapAccess($currentExpression);
+ }
+
else if ($nextToken->literal == "=>") {
$this->advance(1);
@@ -419,6 +458,28 @@ class Parser
}
}
+ private function parseArrayOrMapAccess(Node $arrayOrMap): Node
+ {
+ $items = [];
+
+ while ($this->getCurrentToken()->literal == "[") {
+ // skip current [
+ $this->anticipateTokenAndSkip("[");
+
+ $item = $this->parseExpression();
+
+ // skip last ]
+ $this->anticipateTokenAndSkip("]");
+
+ $items[] = $item;
+ }
+
+ return new ArrayMapAccessNode(
+ $arrayOrMap,
+ $items,
+ );
+ }
+
private function parseNumber(): Node
{
$currentToken = $this->getCurrentToken();
@@ -470,7 +531,7 @@ class Parser
continue;
}
- $identifier = $this->getCurrentToken();
+ $identifier = new IdentifierNode($this->getCurrentToken());
$this->advance(1);
// skip :
@@ -530,9 +591,14 @@ class Parser
$this->advance(1);
}
- /*else if ($currentToken->type == TokenType::Identifier) {
- $body[] = $this->parseFunctionCall();
- }*/
+ else if ($currentToken->type == TokenType::Identifier) {
+ if ($this->getNextToken()->literal == "=") {
+ $body[] = $this->parseVariableAssignment();
+ }
+ else {
+ $body[] = $this->parseFunctionCall();
+ }
+ }
else {
$error = sprintf("Unexpected %s at %d:%d" . PHP_EOL, $currentToken->value, $currentToken->line, $currentToken->column);
@@ -558,7 +624,7 @@ class Parser
private function parseFunctionCall(): Node
{
- $identifier = $this->getCurrentToken();
+ $identifier = new IdentifierNode($this->getCurrentToken());
$this->advance(1);
// skip first (
@@ -590,7 +656,7 @@ class Parser
// if "=" then identifier is name
if ($this->getNextToken()->literal == "=") {
- $identifier = $this->getCurrentToken();
+ $identifier = new IdentifierNode($this->getCurrentToken());
$this->advance(2);
$value = $this->parseExpression();
@@ -696,8 +762,16 @@ class Tree extends Node
class ConstVariableDeclaration extends Node
{
public function __construct(
- public Token $identifier,
+ 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,
) {}
}
@@ -733,6 +807,11 @@ class ArrayNode extends Node
public function __construct(
public array $values,
) {}
+
+ public function getValue(): array
+ {
+ return $this->values;
+ }
}
class MapNode extends Node
@@ -743,7 +822,13 @@ class MapNode extends Node
public function __construct(
public array $values,
) {}
+
+ public function getValue(): array
+ {
+ return $this->values;
+ }
}
+
class MapItemNode extends Node
{
public function __construct(
@@ -752,6 +837,17 @@ class MapItemNode extends Node
) {}
}
+class ArrayMapAccessNode extends Node
+{
+ public function __construct(
+ public ArrayNode|IdentifierNode $arrayOrMap,
+ /**
+ * @param Node[] $items
+ */
+ public array $items,
+ ) {}
+}
+
class OperatorExpression extends Node
{
public function __construct(
@@ -792,6 +888,11 @@ class NumberNode extends Node
public Token $token,
public int|float $value,
) {}
+
+ public function getValue(): int|float
+ {
+ return $this->value;
+ }
}
class StringNode extends Node
@@ -799,6 +900,11 @@ class StringNode extends Node
public function __construct(
public Token $token,
) {}
+
+ public function getValue(): string
+ {
+ return $this->token->value;
+ }
}
class BoolNode extends Node
@@ -806,6 +912,11 @@ class BoolNode extends Node
public function __construct(
public Token $token,
) {}
+
+ public function getValue(): bool
+ {
+ return filter_var($this->token->value, FILTER_VALIDATE_BOOLEAN);
+ }
}
class CommentNode extends Node
@@ -822,7 +933,7 @@ class FunctionDefinition extends Node
* @param FunctionDefinitionParameter[] $parameters
*/
public array $parameters,
- public TypeDeclaration $returnType,
+ public TypeDeclaration|ArrayTypeDeclaration|MapTypeDeclaration $returnType,
/**
* @param Node[] $body
*/
@@ -833,8 +944,8 @@ class FunctionDefinition extends Node
class FunctionDefinitionParameter extends Node
{
public function __construct(
- public Token $identifier,
- public TypeDeclaration $type,
+ public Node $identifier,
+ public TypeDeclaration|ArrayTypeDeclaration|MapTypeDeclaration $type,
public ?Node $defaultValue = null,
) {}
}
@@ -849,7 +960,7 @@ class FunctionReturn extends Node
class FunctionCall extends Node
{
public function __construct(
- public Token $identifier,
+ public Node $identifier,
/**
* @param FunctionCallParameter[] $parameters
*/
@@ -860,7 +971,7 @@ class FunctionCall extends Node
class FunctionCallParameter extends Node
{
public function __construct(
- public ?Token $identifier,
+ public ?Node $identifier,
public Node|Token $value,
) {}
}
diff --git a/src/standard.php b/src/standard.php
new file mode 100644
index 0000000..6cc6be6
--- /dev/null
+++ b/src/standard.php
@@ -0,0 +1,35 @@
+<?php
+
+use Mnml\Lexer\Lexer;
+use Mnml\Parser\Parser;
+
+$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();
+};
+
+$print = function (string $string): void {
+ echo $string;
+};
+
+$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/const-array-complex.mnml b/test/const-array-complex.mnml
index 07e2286..00edaac 100644
--- a/test/const-array-complex.mnml
+++ b/test/const-array-complex.mnml
@@ -1 +1 @@
-const array: [[integer]] = [[1, 2], [3, 4], [5, 6, 7]]
+const array: [[integer]] = [[1, 2], [3, 4], [5, 6, 7_000_000]]
diff --git a/test/const-array.mnml b/test/const-array.mnml
index 03c7163..f79adbf 100644
--- a/test/const-array.mnml
+++ b/test/const-array.mnml
@@ -1,2 +1,8 @@
const array: [integer] = [1, 2, 3]
-const array2: [integer] = [3, 4, 5]
+const array2: [integer] = [3, [4, 8], 5]
+
+const main: function = (array3: [integer], array4: [integer]): integer {
+ return array4[1][1]
+}
+
+main(array3 = array, array4 = array2)
diff --git a/test/const-function-if.mnml b/test/const-function-if.mnml
index 11883cd..7b71b8c 100644
--- a/test/const-function-if.mnml
+++ b/test/const-function-if.mnml
@@ -1,6 +1,7 @@
const main: function = (input: string): string {
if (input == "hello") {
const bye: string = "bye"
+ return input + " " + bye
}
return input
@@ -9,5 +10,7 @@ const main: function = (input: string): string {
const main2: function = (input: string): string {
const bye: string = main(input = "bye")
- return main(input = "hello")
+ return input + main(input = "hello")
}
+
+main(input = "hello") => main2(input = $ + " ")
diff --git a/test/const-function.mnml b/test/const-function.mnml
index 9630dd8..5a82b68 100644
--- a/test/const-function.mnml
+++ b/test/const-function.mnml
@@ -5,4 +5,4 @@ const main: function = (input: string or integer, default: integer = 1): void {
return hello + " " + bye
}
-main(input = "hey", 2)
+main(input = "hey", default = 2)
diff --git a/test/const-map.mnml b/test/const-map.mnml
index b63b68b..6942bba 100644
--- a/test/const-map.mnml
+++ b/test/const-map.mnml
@@ -8,4 +8,11 @@ const map: [string][string or integer or bool] = [
},*/
"sixth" = 20_000,
"seventh" = 20.02,
+ "eight" = [8, 9, 0],
]
+
+const main: function = (input: [string][string or integer or bool]): string {
+ return input["eight"][2]
+}
+
+main(input = map)
diff --git a/test/const-simple.mnml b/test/const-simple.mnml
index 5da0a0f..f7f597d 100644
--- a/test/const-simple.mnml
+++ b/test/const-simple.mnml
@@ -1,2 +1,2 @@
const henshin: integer = 2
-const new: integer = henshin + 5 * 10
+const new: integer = (henshin * 5) + 10
diff --git a/test/if.mnml b/test/if.mnml
index 1d273b3..8227ad5 100644
--- a/test/if.mnml
+++ b/test/if.mnml
@@ -1,4 +1,4 @@
-if (1 == 2) {
+if ((1 == 2) or (2 == 3)) {
main1()
}
diff --git a/test/parentheses.mnml b/test/parentheses.mnml
index d18aee2..f321700 100644
--- a/test/parentheses.mnml
+++ b/test/parentheses.mnml
@@ -1,6 +1,7 @@
const main: function = (input: string): string {
if ((input == "hello") or input == "bye") {
- const bye: string = "bye"
+ const bye: string = "bye"
+ return bye
}
return input
@@ -13,5 +14,7 @@ const main2: function = (input: string): string {
const test2: string = ((main(input = "test") => main(input = $)) + "test2")
- return main(input = "hello") + bye
+ return input + main(input = "hello") + bye + " " + test2
}
+
+main(input = "test") => main2(input = $)
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($)
diff --git a/test/test.mnml b/test/test.mnml
index a14bdec..6ecb7d9 100644
--- a/test/test.mnml
+++ b/test/test.mnml
@@ -12,11 +12,11 @@ const main: function = (input: string or integer): void {
return hello + " " + bye
}
-main()/*
+main(input = "mlc")/*
* mlc
*/
-main("pipe1!") => print($)
+main(input = "pipe1!") => print($)
/*var mls: string = "alphabet
ende