|
@@ -0,0 +1,263 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using NTERA.Interpreter.Functions;
|
|
|
+
|
|
|
+namespace NTERA.Interpreter
|
|
|
+{
|
|
|
+ public partial class Interpreter
|
|
|
+ {
|
|
|
+ public bool HasPrint { get; set; } = true;
|
|
|
+ public bool HasInput { get; set; } = true;
|
|
|
+
|
|
|
+ private Lexer lex;
|
|
|
+ private Token prevToken;
|
|
|
+ private Token lastToken;
|
|
|
+
|
|
|
+ private Dictionary<string, Value> vars;
|
|
|
+ private Dictionary<string, Marker> loops;
|
|
|
+
|
|
|
+ public delegate Value BasicFunction(Interpreter interpreter, List<Value> args);
|
|
|
+ private Dictionary<string, BasicFunction> funcs;
|
|
|
+
|
|
|
+ private int ifcounter;
|
|
|
+
|
|
|
+ private Marker lineMarker;
|
|
|
+
|
|
|
+ private bool exit;
|
|
|
+
|
|
|
+ public Interpreter(string input)
|
|
|
+ {
|
|
|
+ lex = new Lexer(input);
|
|
|
+ vars = new Dictionary<string, Value>();
|
|
|
+ loops = new Dictionary<string, Marker>();
|
|
|
+ funcs = new Dictionary<string, BasicFunction>();
|
|
|
+ ifcounter = 0;
|
|
|
+ BuiltInFunctions.InstallAll(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ public Value GetVar(string name)
|
|
|
+ {
|
|
|
+ if (!vars.ContainsKey(name))
|
|
|
+ throw new Exception("Variable with name " + name + " does not exist.");
|
|
|
+ return vars[name];
|
|
|
+ }
|
|
|
+
|
|
|
+ public void SetVar(string name, Value val)
|
|
|
+ {
|
|
|
+ if (!vars.ContainsKey(name)) vars.Add(name, val);
|
|
|
+ else vars[name] = val;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void AddFunction(string name, BasicFunction function)
|
|
|
+ {
|
|
|
+ if (!funcs.ContainsKey(name)) funcs.Add(name, function);
|
|
|
+ else funcs[name] = function;
|
|
|
+ }
|
|
|
+
|
|
|
+ void Error(string text)
|
|
|
+ {
|
|
|
+ throw new Exception(text + " at line: " + lineMarker.Line);
|
|
|
+ }
|
|
|
+
|
|
|
+ void Match(Token tok)
|
|
|
+ {
|
|
|
+ if (lastToken != tok)
|
|
|
+ Error("Expect " + tok + " got " + lastToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Exec()
|
|
|
+ {
|
|
|
+ exit = false;
|
|
|
+ GetNextToken();
|
|
|
+ while (!exit) Line();
|
|
|
+ }
|
|
|
+
|
|
|
+ Token GetNextToken()
|
|
|
+ {
|
|
|
+ prevToken = lastToken;
|
|
|
+ lastToken = lex.GetToken();
|
|
|
+
|
|
|
+ if (lastToken == Token.EOF && prevToken == Token.EOF)
|
|
|
+ Error("Unexpected end of file");
|
|
|
+
|
|
|
+ return lastToken;
|
|
|
+ }
|
|
|
+
|
|
|
+ void Line()
|
|
|
+ {
|
|
|
+ while (lastToken == Token.NewLine) GetNextToken();
|
|
|
+
|
|
|
+ if (lastToken == Token.EOF)
|
|
|
+ {
|
|
|
+ exit = true;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ lineMarker = lex.TokenMarker;
|
|
|
+ Statment();
|
|
|
+
|
|
|
+ if (lastToken != Token.NewLine && lastToken != Token.EOF)
|
|
|
+ Error("Expect new line got " + lastToken);
|
|
|
+ }
|
|
|
+
|
|
|
+ void Statment()
|
|
|
+ {
|
|
|
+ Token keyword = lastToken;
|
|
|
+ GetNextToken();
|
|
|
+ switch (keyword)
|
|
|
+ {
|
|
|
+ case Token.Print: Print(); break;
|
|
|
+ case Token.Input: Input(); break;
|
|
|
+ case Token.If: If(); break;
|
|
|
+ case Token.Else: Else(); break;
|
|
|
+ case Token.EndIf: break;
|
|
|
+ case Token.For: For(); break;
|
|
|
+ case Token.Next: Next(); break;
|
|
|
+ case Token.Let: Let(); break;
|
|
|
+ case Token.End: End(); break;
|
|
|
+ case Token.Identifer:
|
|
|
+ if (lastToken == Token.Equal) Let();
|
|
|
+ else goto default;
|
|
|
+ break;
|
|
|
+ case Token.EOF:
|
|
|
+ exit = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Error("Expect keyword got " + keyword);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (lastToken == Token.Colon)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ Statment();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void Print()
|
|
|
+ {
|
|
|
+ if (!HasPrint)
|
|
|
+ Error("Print command not allowed");
|
|
|
+
|
|
|
+ Console.WriteLine(Expr().ToString());
|
|
|
+ }
|
|
|
+
|
|
|
+ void Input()
|
|
|
+ {
|
|
|
+ if (!HasInput)
|
|
|
+ Error("Input command not allowed");
|
|
|
+
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ Match(Token.Identifer);
|
|
|
+
|
|
|
+ if (!vars.ContainsKey(lex.Identifer)) vars.Add(lex.Identifer, new Value());
|
|
|
+
|
|
|
+ string input = Console.ReadLine();
|
|
|
+ double d;
|
|
|
+ if (double.TryParse(input, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d))
|
|
|
+ vars[lex.Identifer] = new Value(d);
|
|
|
+ else
|
|
|
+ vars[lex.Identifer] = new Value(input);
|
|
|
+
|
|
|
+ GetNextToken();
|
|
|
+ if (lastToken != Token.Comma) break;
|
|
|
+ GetNextToken();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Value Expr()
|
|
|
+ {
|
|
|
+ Dictionary<Token, int> prec = new Dictionary<Token, int>()
|
|
|
+ {
|
|
|
+ { Token.Or, 0 }, { Token.And, 0 },
|
|
|
+ { Token.Equal, 1 }, { Token.NotEqual, 1 },
|
|
|
+ { Token.Less, 1 }, { Token.More, 1 }, { Token.LessEqual, 1 }, { Token.MoreEqual, 1 },
|
|
|
+ { Token.Plus, 2 }, { Token.Minus, 2 },
|
|
|
+ { Token.Asterisk, 3 }, {Token.Slash, 3 },
|
|
|
+ { Token.Caret, 4 }
|
|
|
+ };
|
|
|
+
|
|
|
+ Stack<Value> stack = new Stack<Value>();
|
|
|
+ Stack<Token> operators = new Stack<Token>();
|
|
|
+
|
|
|
+ int i = 0;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (lastToken == Token.Value)
|
|
|
+ {
|
|
|
+ stack.Push(lex.Value);
|
|
|
+ }
|
|
|
+ else if (lastToken == Token.Identifer)
|
|
|
+ {
|
|
|
+ if (vars.ContainsKey(lex.Identifer))
|
|
|
+ {
|
|
|
+ stack.Push(vars[lex.Identifer]);
|
|
|
+ }
|
|
|
+ else if (funcs.ContainsKey(lex.Identifer))
|
|
|
+ {
|
|
|
+ string name = lex.Identifer;
|
|
|
+ List<Value> args = new List<Value>();
|
|
|
+ GetNextToken();
|
|
|
+ Match(Token.LParen);
|
|
|
+
|
|
|
+ start:
|
|
|
+ if (GetNextToken() != Token.RParen)
|
|
|
+ {
|
|
|
+ args.Add(Expr());
|
|
|
+ if (lastToken == Token.Comma)
|
|
|
+ goto start;
|
|
|
+ }
|
|
|
+
|
|
|
+ stack.Push(funcs[name](null, args));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Error("Undeclared variable " + lex.Identifer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (lastToken == Token.LParen)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ stack.Push(Expr());
|
|
|
+ Match(Token.RParen);
|
|
|
+ }
|
|
|
+ else if (lastToken >= Token.Plus && lastToken <= Token.Not)
|
|
|
+ {
|
|
|
+ if ((lastToken == Token.Minus || lastToken == Token.Minus) && (i == 0 || prevToken == Token.LParen))
|
|
|
+ {
|
|
|
+ stack.Push(new Value(0));
|
|
|
+ operators.Push(lastToken);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ while (operators.Count > 0 && prec[lastToken] <= prec[operators.Peek()])
|
|
|
+ Operation(ref stack, operators.Pop());
|
|
|
+ operators.Push(lastToken);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (i == 0)
|
|
|
+ Error("Empty expression");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ i++;
|
|
|
+ GetNextToken();
|
|
|
+ }
|
|
|
+
|
|
|
+ while (operators.Count > 0)
|
|
|
+ Operation(ref stack, operators.Pop());
|
|
|
+
|
|
|
+ return stack.Pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ void Operation(ref Stack<Value> stack, Token token)
|
|
|
+ {
|
|
|
+ Value b = stack.Pop();
|
|
|
+ Value a = stack.Pop();
|
|
|
+ Value result = a.BinOp(b, token);
|
|
|
+ stack.Push(result);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|