|
@@ -1,5 +1,6 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
using NTERA.Core;
|
|
|
|
|
|
namespace NTERA.Interpreter
|
|
@@ -8,7 +9,7 @@ namespace NTERA.Interpreter
|
|
|
{
|
|
|
protected Lexer Lexer { get; set; }
|
|
|
private Token prevToken;
|
|
|
- private Token lastToken;
|
|
|
+ private Token CurrentToken => tokens.Current;
|
|
|
|
|
|
public VariableDictionary Variables { get; } = new VariableDictionary();
|
|
|
protected Dictionary<string, Marker> Loops { get; } = new Dictionary<string, Marker>();
|
|
@@ -43,13 +44,13 @@ namespace NTERA.Interpreter
|
|
|
if (pullNext)
|
|
|
GetNextToken();
|
|
|
|
|
|
- return lastToken == tok;
|
|
|
+ return CurrentToken == tok;
|
|
|
}
|
|
|
|
|
|
protected void AssertToken(Token tok, bool pullNext = true)
|
|
|
{
|
|
|
if (!TryAssertToken(tok, pullNext))
|
|
|
- Error("Expect " + tok + " got " + lastToken);
|
|
|
+ Error("Expect " + tok + " got " + CurrentToken);
|
|
|
}
|
|
|
|
|
|
private IEnumerator<Token> tokens;
|
|
@@ -68,23 +69,22 @@ namespace NTERA.Interpreter
|
|
|
|
|
|
protected Token GetNextToken()
|
|
|
{
|
|
|
- prevToken = lastToken;
|
|
|
+ prevToken = CurrentToken;
|
|
|
|
|
|
tokens.MoveNext();
|
|
|
- lastToken = tokens.Current;
|
|
|
|
|
|
- if (lastToken == Token.EOF && prevToken == Token.EOF)
|
|
|
+ if (CurrentToken == Token.EOF && prevToken == Token.EOF)
|
|
|
Error("Unexpected end of file");
|
|
|
|
|
|
- return lastToken;
|
|
|
+ return CurrentToken;
|
|
|
}
|
|
|
|
|
|
private void Line()
|
|
|
{
|
|
|
- while (lastToken == Token.NewLine)
|
|
|
+ while (CurrentToken == Token.NewLine)
|
|
|
GetNextToken();
|
|
|
|
|
|
- if (lastToken == Token.EOF)
|
|
|
+ if (CurrentToken == Token.EOF)
|
|
|
{
|
|
|
exit = true;
|
|
|
return;
|
|
@@ -93,56 +93,44 @@ namespace NTERA.Interpreter
|
|
|
lineMarker = Lexer.TokenMarker;
|
|
|
Statement();
|
|
|
|
|
|
- if (lastToken != Token.NewLine && lastToken != Token.EOF)
|
|
|
- Error("Expect new line got " + lastToken);
|
|
|
+ if (CurrentToken != Token.NewLine && CurrentToken != Token.EOF)
|
|
|
+ Error("Expect new line got " + CurrentToken);
|
|
|
}
|
|
|
|
|
|
private void Statement()
|
|
|
{
|
|
|
- while (true)
|
|
|
- {
|
|
|
- Token keyword = lastToken;
|
|
|
- GetNextToken();
|
|
|
-
|
|
|
- if (KeywordMethods.ContainsKey(keyword))
|
|
|
- KeywordMethods[keyword]();
|
|
|
- else
|
|
|
- {
|
|
|
- switch (keyword)
|
|
|
- {
|
|
|
- case Token.Input:
|
|
|
- Input();
|
|
|
- break;
|
|
|
-
|
|
|
- case Token.Function:
|
|
|
- case Token.Global:
|
|
|
- case Token.Dim:
|
|
|
- case Token.Const:
|
|
|
- while (GetNextToken() != Token.NewLine) { }
|
|
|
- break;
|
|
|
-
|
|
|
- case Token.Identifer:
|
|
|
- if (lastToken == Token.Equal)
|
|
|
- Let();
|
|
|
- else
|
|
|
- Error("Expected assignment got " + lastToken);
|
|
|
- break;
|
|
|
- case Token.EOF:
|
|
|
- exit = true;
|
|
|
- break;
|
|
|
- default:
|
|
|
- Error("Expect keyword got " + keyword);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
+ Token keyword = CurrentToken;
|
|
|
+ GetNextToken();
|
|
|
|
|
|
- if (lastToken == Token.Colon)
|
|
|
+ if (KeywordMethods.ContainsKey(keyword))
|
|
|
+ KeywordMethods[keyword]();
|
|
|
+ else
|
|
|
+ {
|
|
|
+ switch (keyword)
|
|
|
{
|
|
|
- GetNextToken();
|
|
|
- continue;
|
|
|
+ case Token.Input:
|
|
|
+ Input();
|
|
|
+ break;
|
|
|
+
|
|
|
+ case Token.Function:
|
|
|
+ case Token.Dim:
|
|
|
+ case Token.Const:
|
|
|
+ while (GetNextToken() != Token.NewLine) { }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case Token.Identifer:
|
|
|
+ if (CurrentToken != Token.Equal && CurrentToken != Token.Colon)
|
|
|
+ Error("Expected assignment got " + CurrentToken);
|
|
|
+
|
|
|
+ Let();
|
|
|
+ break;
|
|
|
+ case Token.EOF:
|
|
|
+ exit = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Error("Expect keyword got " + keyword);
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -185,10 +173,26 @@ namespace NTERA.Interpreter
|
|
|
};
|
|
|
|
|
|
private Value RealExpression()
|
|
|
+ {
|
|
|
+ return RealExpression(Lexer, tokens);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Value RealExpression(string input)
|
|
|
+ {
|
|
|
+ return RealExpression(new Lexer(input));
|
|
|
+ }
|
|
|
+
|
|
|
+ private Value RealExpression(Lexer lexer, IEnumerator<Token> enumerator = null)
|
|
|
{
|
|
|
Stack<Value> stack = new Stack<Value>();
|
|
|
Stack<Token> operators = new Stack<Token>();
|
|
|
|
|
|
+ if (enumerator == null)
|
|
|
+ {
|
|
|
+ enumerator = lexer.GetTokens().GetEnumerator();
|
|
|
+ enumerator.MoveNext();
|
|
|
+ }
|
|
|
+
|
|
|
void Operation(Token token)
|
|
|
{
|
|
|
Value b = stack.Pop();
|
|
@@ -200,27 +204,27 @@ namespace NTERA.Interpreter
|
|
|
int i = 0;
|
|
|
while (true)
|
|
|
{
|
|
|
- if (lastToken == Token.Value)
|
|
|
+ if (enumerator.Current == Token.Value)
|
|
|
{
|
|
|
- stack.Push(Lexer.Value);
|
|
|
+ stack.Push(lexer.Value);
|
|
|
}
|
|
|
- else if (lastToken == Token.Identifer)
|
|
|
+ else if (enumerator.Current == Token.Identifer)
|
|
|
{
|
|
|
- if (Variables.ContainsKey(Lexer.Identifer))
|
|
|
+ if (Variables.ContainsKey(lexer.Identifer))
|
|
|
{
|
|
|
- stack.Push(Variables[Lexer.Identifer]);
|
|
|
+ stack.Push(Variables[lexer.Identifer]);
|
|
|
}
|
|
|
- else if (FunctionDictionary.ContainsKey(Lexer.Identifer))
|
|
|
+ else if (FunctionDictionary.ContainsKey(lexer.Identifer))
|
|
|
{
|
|
|
- string name = Lexer.Identifer;
|
|
|
+ string name = lexer.Identifer;
|
|
|
List<Value> args = new List<Value>();
|
|
|
- GetNextToken();
|
|
|
- AssertToken(Token.LParen, false);
|
|
|
+ enumerator.MoveNext();
|
|
|
+ //AssertToken(Token.LParen, false);
|
|
|
|
|
|
- while (!TryAssertToken(Token.RParen))
|
|
|
+ while (enumerator.MoveNext() && enumerator.Current != Token.RParen)
|
|
|
{
|
|
|
- args.Add(RealExpression());
|
|
|
- if (lastToken != Token.Comma)
|
|
|
+ args.Add(RealExpression(lexer, enumerator));
|
|
|
+ if (enumerator.Current != Token.Comma)
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -228,27 +232,27 @@ namespace NTERA.Interpreter
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- Error("Undeclared variable " + Lexer.Identifer);
|
|
|
+ Error("Undeclared variable " + lexer.Identifer);
|
|
|
}
|
|
|
}
|
|
|
- else if (lastToken == Token.LParen)
|
|
|
+ else if (enumerator.Current == Token.LParen)
|
|
|
{
|
|
|
- GetNextToken();
|
|
|
- stack.Push(RealExpression());
|
|
|
- AssertToken(Token.RParen, false);
|
|
|
+ enumerator.MoveNext();
|
|
|
+ stack.Push(RealExpression(lexer, enumerator));
|
|
|
+ //AssertToken(Token.RParen, false);
|
|
|
}
|
|
|
- else if (lastToken.IsArithmetic())
|
|
|
+ else if (enumerator.Current.IsArithmetic())
|
|
|
{
|
|
|
- if (lastToken.IsUnary() && (i == 0 || prevToken == Token.LParen))
|
|
|
+ if (enumerator.Current.IsUnary() && (i == 0 || prevToken == Token.LParen))
|
|
|
{
|
|
|
stack.Push(0);
|
|
|
- operators.Push(lastToken);
|
|
|
+ operators.Push(enumerator.Current);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- while (operators.Count > 0 && OrderOfOps[lastToken] <= OrderOfOps[operators.Peek()])
|
|
|
+ while (operators.Count > 0 && OrderOfOps[enumerator.Current] <= OrderOfOps[operators.Peek()])
|
|
|
Operation(operators.Pop());
|
|
|
- operators.Push(lastToken);
|
|
|
+ operators.Push(enumerator.Current);
|
|
|
}
|
|
|
}
|
|
|
else
|
|
@@ -259,7 +263,7 @@ namespace NTERA.Interpreter
|
|
|
}
|
|
|
|
|
|
i++;
|
|
|
- GetNextToken();
|
|
|
+ enumerator.MoveNext();
|
|
|
}
|
|
|
|
|
|
while (operators.Count > 0)
|
|
@@ -270,10 +274,97 @@ namespace NTERA.Interpreter
|
|
|
|
|
|
private Value StringExpression()
|
|
|
{
|
|
|
- foreach (var t in Lexer.ReturnAsLine(Token.Unknown)) { }
|
|
|
+ Lexer.Type = LexerType.String;
|
|
|
+ GetNextToken();
|
|
|
+ var result = StringExpression(Lexer, tokens);
|
|
|
+ Lexer.Type = LexerType.Real;
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ private Value StringExpression(Lexer lexer, IEnumerator<Token> enumerator = null)
|
|
|
+ {
|
|
|
+ Stack<Value> stack = new Stack<Value>();
|
|
|
+ Stack<Token> operators = new Stack<Token>();
|
|
|
+
|
|
|
+ if (enumerator == null)
|
|
|
+ {
|
|
|
+ enumerator = lexer.GetTokens().GetEnumerator();
|
|
|
+ enumerator.MoveNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Operation(Token token)
|
|
|
+ {
|
|
|
+ Value b = stack.Pop();
|
|
|
+ Value a = stack.Pop();
|
|
|
+ Value result = a.Operate(b, token);
|
|
|
+ stack.Push(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ int i = 0;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (enumerator.Current == Token.Value)
|
|
|
+ {
|
|
|
+ stack.Push(lexer.Value.String);
|
|
|
+ }
|
|
|
+ else if (enumerator.Current == Token.Identifer)
|
|
|
+ {
|
|
|
+ if (Variables.ContainsKey(lexer.Identifer))
|
|
|
+ {
|
|
|
+ stack.Push(Variables[lexer.Identifer]);
|
|
|
+ }
|
|
|
+ else if (FunctionDictionary.ContainsKey(lexer.Identifer))
|
|
|
+ {
|
|
|
+ string name = lexer.Identifer;
|
|
|
+ List<Value> args = new List<Value>();
|
|
|
+ enumerator.MoveNext();
|
|
|
+ AssertToken(Token.LParen, false);
|
|
|
+
|
|
|
+ while (!TryAssertToken(Token.RParen))
|
|
|
+ {
|
|
|
+ args.Add(StringExpression(lexer, enumerator));
|
|
|
+ if (enumerator.Current != Token.Comma)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ stack.Push(FunctionDictionary[name](args));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (stack.Count > 0)
|
|
|
+ operators.Push(Token.Plus);
|
|
|
+
|
|
|
+ stack.Push(lexer.Identifer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (enumerator.Current == Token.LParen)
|
|
|
+ {
|
|
|
+ enumerator.MoveNext();
|
|
|
+ stack.Push(StringExpression(lexer, enumerator));
|
|
|
+ AssertToken(Token.RParen, false);
|
|
|
+ }
|
|
|
+ else if (enumerator.Current.IsStringOp())
|
|
|
+ {
|
|
|
+ while (operators.Count > 0 && OrderOfOps[enumerator.Current] <= OrderOfOps[operators.Peek()])
|
|
|
+ Operation(operators.Pop());
|
|
|
+ operators.Push(enumerator.Current);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (i == 0)
|
|
|
+ Error("Empty expression");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ i++;
|
|
|
+ enumerator.MoveNext();
|
|
|
+ }
|
|
|
+
|
|
|
+ while (operators.Count > 0)
|
|
|
+ Operation(operators.Pop());
|
|
|
|
|
|
- lastToken = Token.NewLine;
|
|
|
- return Lexer.Value;
|
|
|
+ return stack.Aggregate((a, b) => b.String + a.String);
|
|
|
}
|
|
|
}
|
|
|
}
|