diff options
-rwxr-xr-x | mnml | 6 | ||||
-rw-r--r-- | src/Interpreter/Interpreter.php | 460 | ||||
-rw-r--r-- | src/Lexer/Lexer.php | 15 | ||||
-rw-r--r-- | src/Parser/Parser.php | 149 | ||||
-rw-r--r-- | src/standard.php | 35 | ||||
-rw-r--r-- | test/const-array-complex.mnml | 2 | ||||
-rw-r--r-- | test/const-array.mnml | 8 | ||||
-rw-r--r-- | test/const-function-if.mnml | 5 | ||||
-rw-r--r-- | test/const-function.mnml | 2 | ||||
-rw-r--r-- | test/const-map.mnml | 7 | ||||
-rw-r--r-- | test/const-simple.mnml | 2 | ||||
-rw-r--r-- | test/if.mnml | 2 | ||||
-rw-r--r-- | test/parentheses.mnml | 7 | ||||
-rw-r--r-- | test/self/lexer.mnml | 22 | ||||
-rw-r--r-- | test/test.mnml | 4 |
15 files changed, 695 insertions, 31 deletions
@@ -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 |