|
@@ -1,8 +1,5 @@
|
|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
-using System.Linq;
|
|
|
-using System.Reflection;
|
|
|
-using System.Text.RegularExpressions;
|
|
|
using NTERA.Core;
|
|
|
|
|
|
namespace NTERA.Interpreter
|
|
@@ -38,27 +35,22 @@ namespace NTERA.Interpreter
|
|
|
GenerateFunctionDictionary();
|
|
|
}
|
|
|
|
|
|
- public Value GetVar(string name)
|
|
|
+ private void Error(string text)
|
|
|
{
|
|
|
- if (!Variables.ContainsKey(name))
|
|
|
- throw new Exception("Variable with name " + name + " does not exist.");
|
|
|
- return Variables[name];
|
|
|
+ throw new Exception(text + " at line: " + lineMarker.Line);
|
|
|
}
|
|
|
|
|
|
- public void SetVar(string name, Value val)
|
|
|
+ protected bool TryAssertNextToken(Token tok, bool pullNext = true)
|
|
|
{
|
|
|
- if (!Variables.ContainsKey(name)) Variables.Add(name, val);
|
|
|
- else Variables[name] = val;
|
|
|
- }
|
|
|
+ if (pullNext)
|
|
|
+ GetNextToken();
|
|
|
|
|
|
- void Error(string text)
|
|
|
- {
|
|
|
- throw new Exception(text + " at line: " + lineMarker.Line);
|
|
|
+ return lastToken == tok;
|
|
|
}
|
|
|
|
|
|
- void Match(Token tok)
|
|
|
+ protected void AssertToken(Token tok, bool pullNext = true)
|
|
|
{
|
|
|
- if (lastToken != tok)
|
|
|
+ if (!TryAssertNextToken(tok, pullNext))
|
|
|
Error("Expect " + tok + " got " + lastToken);
|
|
|
}
|
|
|
|
|
@@ -71,10 +63,12 @@ namespace NTERA.Interpreter
|
|
|
tokens = lex.GetTokens().GetEnumerator();
|
|
|
|
|
|
GetNextToken();
|
|
|
- while (!exit) Line();
|
|
|
+
|
|
|
+ while (!exit)
|
|
|
+ Line();
|
|
|
}
|
|
|
|
|
|
- Token GetNextToken()
|
|
|
+ protected Token GetNextToken()
|
|
|
{
|
|
|
prevToken = lastToken;
|
|
|
|
|
@@ -87,7 +81,7 @@ namespace NTERA.Interpreter
|
|
|
return lastToken;
|
|
|
}
|
|
|
|
|
|
- void Line()
|
|
|
+ private void Line()
|
|
|
{
|
|
|
while (lastToken == Token.NewLine) GetNextToken();
|
|
|
|
|
@@ -98,13 +92,13 @@ namespace NTERA.Interpreter
|
|
|
}
|
|
|
|
|
|
lineMarker = lex.TokenMarker;
|
|
|
- Statment();
|
|
|
+ Statement();
|
|
|
|
|
|
if (lastToken != Token.NewLine && lastToken != Token.EOF)
|
|
|
Error("Expect new line got " + lastToken);
|
|
|
}
|
|
|
|
|
|
- void Statment()
|
|
|
+ private void Statement()
|
|
|
{
|
|
|
Token keyword = lastToken;
|
|
|
GetNextToken();
|
|
@@ -140,317 +134,20 @@ namespace NTERA.Interpreter
|
|
|
if (lastToken == Token.Colon)
|
|
|
{
|
|
|
GetNextToken();
|
|
|
- Statment();
|
|
|
+ Statement();
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- #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("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(KeywordMethodAttribute), true).FirstOrDefault() as KeywordMethodAttribute;
|
|
|
-
|
|
|
- if (attribute == null)
|
|
|
- continue;
|
|
|
-
|
|
|
- KeywordMethods[attribute.Token] = () => method.Invoke(this, null);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- [KeywordMethod(Token.Print)]
|
|
|
- void Print()
|
|
|
- {
|
|
|
- console.Write(Expr().ToString());
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.PrintL)]
|
|
|
- void PrintL()
|
|
|
- {
|
|
|
- console.PrintSingleLine(Expr().ToString());
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- [KeywordMethod(Token.PrintImg)]
|
|
|
- void PrintImg()
|
|
|
- {
|
|
|
- console.PrintImg(Expr().ToString().Trim().Trim('"'));
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- [KeywordMethod(Token.PrintButton)]
|
|
|
- void PrintButton()
|
|
|
- {
|
|
|
- console.PrintButton(Expr().ToString(), 0);
|
|
|
- }
|
|
|
-
|
|
|
- private static readonly Regex FormRegex = new Regex("{(.*?)}");
|
|
|
-
|
|
|
- [KeywordMethod(Token.PrintFormL)]
|
|
|
- void PrintFormL()
|
|
|
- {
|
|
|
- string rawString = Expr().ToString();
|
|
|
-
|
|
|
- var evaluator = new MatchEvaluator(match => Variables[match.Groups[1].Value].ToString());
|
|
|
-
|
|
|
- console.PrintSingleLine(FormRegex.Replace(rawString, evaluator));
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.DrawLine)]
|
|
|
- void DrawLine()
|
|
|
- {
|
|
|
- console.PrintBar();
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.DrawLineForm)]
|
|
|
- void DrawLineForm()
|
|
|
- {
|
|
|
- console.printCustomBar(Expr().ToString().Trim());
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.If)]
|
|
|
- private void If()
|
|
|
- {
|
|
|
- bool result = Expr().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();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(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();
|
|
|
- }
|
|
|
- }
|
|
|
+ public Dictionary<string, BasicFunction> FunctionDictionary { get; protected set; }
|
|
|
|
|
|
- [KeywordMethod(Token.EndIf)]
|
|
|
- private void EndIf()
|
|
|
- {
|
|
|
+ public Dictionary<Token, Action> KeywordMethods { get; set; }
|
|
|
|
|
|
- }
|
|
|
|
|
|
- [KeywordMethod(Token.End)]
|
|
|
- private void End()
|
|
|
- {
|
|
|
- exit = true;
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.Let)]
|
|
|
- private void Let()
|
|
|
- {
|
|
|
- if (lastToken != Token.Equal)
|
|
|
- {
|
|
|
- Match(Token.Identifer);
|
|
|
- GetNextToken();
|
|
|
- Match(Token.Equal);
|
|
|
- }
|
|
|
-
|
|
|
- string id = lex.Identifer;
|
|
|
-
|
|
|
- GetNextToken();
|
|
|
-
|
|
|
- SetVar(id, Expr());
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(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 (Variables[var].Operate(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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.Next)]
|
|
|
- private void Next()
|
|
|
- {
|
|
|
- Match(Token.Identifer);
|
|
|
- string var = lex.Identifer;
|
|
|
- Variables[var] = Variables[var].Operate(new Value(1), Token.Plus);
|
|
|
- lex.GoTo(new Marker(loops[var].Pointer - 1, loops[var].Line, loops[var].Column - 1));
|
|
|
- lastToken = Token.NewLine;
|
|
|
- }
|
|
|
-
|
|
|
- [KeywordMethod(Token.Times)]
|
|
|
- private void Times()
|
|
|
- {
|
|
|
- Match(Token.Identifer);
|
|
|
-
|
|
|
- string var = lex.Identifer;
|
|
|
-
|
|
|
- GetNextToken();
|
|
|
- Match(Token.Comma);
|
|
|
-
|
|
|
- GetNextToken();
|
|
|
- var arg2 = Expr();
|
|
|
-
|
|
|
- Variables[var] = Variables[var].Operate(arg2, Token.Asterisk);
|
|
|
-
|
|
|
- Match(Token.NewLine);
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- void Input()
|
|
|
+ private void Input()
|
|
|
{
|
|
|
while (true)
|
|
|
{
|
|
|
- Match(Token.Identifer);
|
|
|
+ AssertToken(Token.Identifer);
|
|
|
|
|
|
if (!Variables.ContainsKey(lex.Identifer)) Variables.Add(lex.Identifer, new Value());
|
|
|
|
|
@@ -462,13 +159,14 @@ namespace NTERA.Interpreter
|
|
|
else
|
|
|
Variables[lex.Identifer] = new Value(input);
|
|
|
|
|
|
- GetNextToken();
|
|
|
- if (lastToken != Token.Comma) break;
|
|
|
+ if (!TryAssertNextToken(Token.Comma))
|
|
|
+ break;
|
|
|
+
|
|
|
GetNextToken();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>()
|
|
|
+ private static readonly Dictionary<Token, int> OrderOfOps = new Dictionary<Token, int>
|
|
|
{
|
|
|
{ Token.Or, 0 }, { Token.And, 0 },
|
|
|
{ Token.Equal, 1 }, { Token.NotEqual, 1 },
|
|
@@ -478,7 +176,7 @@ namespace NTERA.Interpreter
|
|
|
{ Token.Caret, 4 }
|
|
|
};
|
|
|
|
|
|
- Value Expr()
|
|
|
+ private Value Expr()
|
|
|
{
|
|
|
Stack<Value> stack = new Stack<Value>();
|
|
|
Stack<Token> operators = new Stack<Token>();
|
|
@@ -501,9 +199,9 @@ namespace NTERA.Interpreter
|
|
|
string name = lex.Identifer;
|
|
|
List<Value> args = new List<Value>();
|
|
|
GetNextToken();
|
|
|
- Match(Token.LParen);
|
|
|
+ AssertToken(Token.LParen, false);
|
|
|
|
|
|
- while (GetNextToken() != Token.RParen)
|
|
|
+ while (!TryAssertNextToken(Token.RParen))
|
|
|
{
|
|
|
args.Add(Expr());
|
|
|
if (lastToken != Token.Comma)
|
|
@@ -521,7 +219,7 @@ namespace NTERA.Interpreter
|
|
|
{
|
|
|
GetNextToken();
|
|
|
stack.Push(Expr());
|
|
|
- Match(Token.RParen);
|
|
|
+ AssertToken(Token.RParen, false);
|
|
|
}
|
|
|
else if (lastToken.IsArithmetic())
|
|
|
{
|
|
@@ -554,7 +252,7 @@ namespace NTERA.Interpreter
|
|
|
return stack.Pop();
|
|
|
}
|
|
|
|
|
|
- void Operation(Stack<Value> stack, Token token)
|
|
|
+ private void Operation(Stack<Value> stack, Token token)
|
|
|
{
|
|
|
Value b = stack.Pop();
|
|
|
Value a = stack.Pop();
|