|
@@ -1,14 +1,13 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
-using NTERA.Interpreter.Functions;
|
|
|
+using System.Linq;
|
|
|
+using System.Reflection;
|
|
|
+using NTERA.Core;
|
|
|
|
|
|
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;
|
|
@@ -16,8 +15,9 @@ namespace NTERA.Interpreter
|
|
|
private Dictionary<string, Value> vars;
|
|
|
private Dictionary<string, Marker> loops;
|
|
|
|
|
|
- public delegate Value BasicFunction(Interpreter interpreter, List<Value> args);
|
|
|
- private Dictionary<string, BasicFunction> funcs;
|
|
|
+ public delegate Value BasicFunction(List<Value> args);
|
|
|
+
|
|
|
+ private readonly IConsole console;
|
|
|
|
|
|
private int ifcounter;
|
|
|
|
|
@@ -25,14 +25,16 @@ namespace NTERA.Interpreter
|
|
|
|
|
|
private bool exit;
|
|
|
|
|
|
- public Interpreter(string input)
|
|
|
+ public Interpreter(IConsole console, string input)
|
|
|
{
|
|
|
+ this.console = console;
|
|
|
lex = new Lexer(input);
|
|
|
vars = new Dictionary<string, Value>();
|
|
|
loops = new Dictionary<string, Marker>();
|
|
|
- funcs = new Dictionary<string, BasicFunction>();
|
|
|
ifcounter = 0;
|
|
|
- BuiltInFunctions.InstallAll(this);
|
|
|
+
|
|
|
+ GenerateKeywordDictionary();
|
|
|
+ GenerateFunctionDictionary();
|
|
|
}
|
|
|
|
|
|
public Value GetVar(string name)
|
|
@@ -48,12 +50,6 @@ namespace NTERA.Interpreter
|
|
|
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);
|
|
@@ -104,28 +100,35 @@ namespace NTERA.Interpreter
|
|
|
{
|
|
|
Token keyword = lastToken;
|
|
|
GetNextToken();
|
|
|
- switch (keyword)
|
|
|
+
|
|
|
+ if (KeywordMethods.ContainsKey(keyword))
|
|
|
+ KeywordMethods[keyword]();
|
|
|
+ else
|
|
|
{
|
|
|
- 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;
|
|
|
+ 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 goto default;
|
|
|
+ break;
|
|
|
+ case Token.EOF:
|
|
|
+ exit = true;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ Error("Expect keyword got " + keyword);
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
if (lastToken == Token.Colon)
|
|
|
{
|
|
|
GetNextToken();
|
|
@@ -133,26 +136,293 @@ namespace NTERA.Interpreter
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #region Functions
|
|
|
+
|
|
|
+ private Dictionary<string, BasicFunction> FunctionDictionary { get; set; }
|
|
|
+
|
|
|
+ private void GenerateFunctionDictionary()
|
|
|
+ {
|
|
|
+ FunctionDictionary = new Dictionary<string, BasicFunction>(StringComparer.InvariantCultureIgnoreCase);
|
|
|
+
|
|
|
+ foreach (var method in typeof(Interpreter).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
|
|
+ {
|
|
|
+ var attribute = method.GetCustomAttributes(typeof(BuiltInFunctionAttribute), true).FirstOrDefault() as BuiltInFunctionAttribute;
|
|
|
+
|
|
|
+ if (attribute == null)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ FunctionDictionary[attribute.Name] = args => (Value)method.Invoke(this, new object[] { args });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ [BuiltInFunction("str")]
|
|
|
+ private Value Str(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 1)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return args[0].Convert(ValueType.String);
|
|
|
+ }
|
|
|
+
|
|
|
+ [BuiltInFunction("num")]
|
|
|
+ private Value Num(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 1)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return args[0].Convert(ValueType.Real);
|
|
|
+ }
|
|
|
+
|
|
|
+ [BuiltInFunction("abs")]
|
|
|
+ private Value Abs(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 1)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return new Value(Math.Abs(args[0].Real));
|
|
|
+ }
|
|
|
+
|
|
|
+ [BuiltInFunction("min")]
|
|
|
+ private Value Min(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 2)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return new Value(Math.Min(args[0].Real, args[1].Real));
|
|
|
+ }
|
|
|
+
|
|
|
+ [BuiltInFunction("max")]
|
|
|
+ private Value Max(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 2)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return new Value(Math.Max(args[0].Real, args[1].Real));
|
|
|
+ }
|
|
|
+
|
|
|
+ [BuiltInFunction("not")]
|
|
|
+ private Value Not(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 1)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return new Value(args[0].Real == 0 ? 1 : 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private readonly Random random = new Random();
|
|
|
+
|
|
|
+ [BuiltInFunction("rand")]
|
|
|
+ private Value Rand(List<Value> args)
|
|
|
+ {
|
|
|
+ if (args.Count < 2)
|
|
|
+ throw new ArgumentException();
|
|
|
+
|
|
|
+ return new Value(random.Next((int)args[0].Real, (int)args[1].Real - 1));
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Keywords
|
|
|
+
|
|
|
+ private Dictionary<Token, Action> KeywordMethods { get; set; }
|
|
|
+
|
|
|
+ private void GenerateKeywordDictionary()
|
|
|
+ {
|
|
|
+ KeywordMethods = new Dictionary<Token, Action>();
|
|
|
+
|
|
|
+ foreach (var method in typeof(Interpreter).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
|
|
+ {
|
|
|
+ var attribute = method.GetCustomAttributes(typeof(TargetKeywordAttribute), true).FirstOrDefault() as TargetKeywordAttribute;
|
|
|
+
|
|
|
+ if (attribute == null)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ KeywordMethods[attribute.Token] = () => method.Invoke(this, null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ [TargetKeyword(Token.Print)]
|
|
|
void Print()
|
|
|
{
|
|
|
- if (!HasPrint)
|
|
|
- Error("Print command not allowed");
|
|
|
+ console.Write(Expr().ToString());
|
|
|
+ }
|
|
|
|
|
|
- Console.WriteLine(Expr().ToString());
|
|
|
+ [TargetKeyword(Token.PrintL)]
|
|
|
+ void PrintL()
|
|
|
+ {
|
|
|
+ console.PrintSingleLine(Expr().ToString());
|
|
|
}
|
|
|
|
|
|
- void Input()
|
|
|
+ [TargetKeyword(Token.DrawLine)]
|
|
|
+ void DrawLine()
|
|
|
{
|
|
|
- if (!HasInput)
|
|
|
- Error("Input command not allowed");
|
|
|
+ console.PrintBar();
|
|
|
+ }
|
|
|
|
|
|
+ [TargetKeyword(Token.DrawLineForm)]
|
|
|
+ void DrawLineForm()
|
|
|
+ {
|
|
|
+ console.printCustomBar(Expr().ToString());
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.If)]
|
|
|
+ private void If()
|
|
|
+ {
|
|
|
+ bool result = Expr().BinOp(new Value(0), Token.Equal).Real == 1;
|
|
|
+
|
|
|
+ Match(Token.Then);
|
|
|
+ GetNextToken();
|
|
|
+
|
|
|
+ if (result)
|
|
|
+ {
|
|
|
+ int i = ifcounter;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (lastToken == Token.If)
|
|
|
+ {
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ else if (lastToken == Token.Else)
|
|
|
+ {
|
|
|
+ if (i == ifcounter)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (lastToken == Token.EndIf)
|
|
|
+ {
|
|
|
+ if (i == ifcounter)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ i--;
|
|
|
+ }
|
|
|
+ GetNextToken();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.Else)]
|
|
|
+ private void Else()
|
|
|
+ {
|
|
|
+ int i = ifcounter;
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if (lastToken == Token.If)
|
|
|
+ {
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ else if (lastToken == Token.EndIf)
|
|
|
+ {
|
|
|
+ if (i == ifcounter)
|
|
|
+ {
|
|
|
+ GetNextToken();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ i--;
|
|
|
+ }
|
|
|
+ GetNextToken();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.EndIf)]
|
|
|
+ private void EndIf()
|
|
|
+ {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.End)]
|
|
|
+ private void End()
|
|
|
+ {
|
|
|
+ exit = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.Let)]
|
|
|
+ private void Let()
|
|
|
+ {
|
|
|
+ if (lastToken != Token.Equal)
|
|
|
+ {
|
|
|
+ Match(Token.Identifer);
|
|
|
+ GetNextToken();
|
|
|
+ Match(Token.Equal);
|
|
|
+ }
|
|
|
+
|
|
|
+ string id = lex.Identifer;
|
|
|
+
|
|
|
+ GetNextToken();
|
|
|
+
|
|
|
+ SetVar(id, Expr());
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.For)]
|
|
|
+ private void For()
|
|
|
+ {
|
|
|
+ Match(Token.Identifer);
|
|
|
+ string var = lex.Identifer;
|
|
|
+
|
|
|
+ GetNextToken();
|
|
|
+ Match(Token.Equal);
|
|
|
+
|
|
|
+ GetNextToken();
|
|
|
+ Value v = Expr();
|
|
|
+
|
|
|
+ if (loops.ContainsKey(var))
|
|
|
+ {
|
|
|
+ loops[var] = lineMarker;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ SetVar(var, v);
|
|
|
+ loops.Add(var, lineMarker);
|
|
|
+ }
|
|
|
+
|
|
|
+ Match(Token.To);
|
|
|
+
|
|
|
+ GetNextToken();
|
|
|
+ v = Expr();
|
|
|
+
|
|
|
+ if (vars[var].BinOp(v, Token.More).Real == 1)
|
|
|
+ {
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ while (!(GetNextToken() == Token.Identifer && prevToken == Token.Next)) ;
|
|
|
+ if (lex.Identifer == var)
|
|
|
+ {
|
|
|
+ loops.Remove(var);
|
|
|
+ GetNextToken();
|
|
|
+ Match(Token.NewLine);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ [TargetKeyword(Token.Next)]
|
|
|
+ private void Next()
|
|
|
+ {
|
|
|
+ Match(Token.Identifer);
|
|
|
+ string var = lex.Identifer;
|
|
|
+ vars[var] = vars[var].BinOp(new Value(1), Token.Plus);
|
|
|
+ lex.GoTo(new Marker(loops[var].Pointer - 1, loops[var].Line, loops[var].Column - 1));
|
|
|
+ lastToken = Token.NewLine;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ void Input()
|
|
|
+ {
|
|
|
while (true)
|
|
|
{
|
|
|
Match(Token.Identifer);
|
|
|
|
|
|
if (!vars.ContainsKey(lex.Identifer)) vars.Add(lex.Identifer, new Value());
|
|
|
-
|
|
|
- string input = Console.ReadLine();
|
|
|
+
|
|
|
+ console.WaitInput(new Core.Interop.InputRequest() { InputType = Core.Interop.InputType.StrValue });
|
|
|
+ string input = console.LastInput;
|
|
|
double d;
|
|
|
if (double.TryParse(input, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out d))
|
|
|
vars[lex.Identifer] = new Value(d);
|
|
@@ -193,7 +463,7 @@ namespace NTERA.Interpreter
|
|
|
{
|
|
|
stack.Push(vars[lex.Identifer]);
|
|
|
}
|
|
|
- else if (funcs.ContainsKey(lex.Identifer))
|
|
|
+ else if (FunctionDictionary.ContainsKey(lex.Identifer))
|
|
|
{
|
|
|
string name = lex.Identifer;
|
|
|
List<Value> args = new List<Value>();
|
|
@@ -208,7 +478,7 @@ namespace NTERA.Interpreter
|
|
|
goto start;
|
|
|
}
|
|
|
|
|
|
- stack.Push(funcs[name](null, args));
|
|
|
+ stack.Push(FunctionDictionary[name](args));
|
|
|
}
|
|
|
else
|
|
|
{
|