using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using NTERA.Core; namespace NTERA.Interpreter { public partial class Interpreter { private Lexer lex; private Token prevToken; private Token lastToken; private Dictionary vars; private Dictionary loops; public delegate Value BasicFunction(List args); private readonly IConsole console; private int ifcounter; private Marker lineMarker; private bool exit; public Interpreter(IConsole console, string input) { this.console = console; lex = new Lexer(input); vars = new Dictionary(); loops = new Dictionary(); ifcounter = 0; GenerateKeywordDictionary(); GenerateFunctionDictionary(); } 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; } 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(); 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 goto default; break; case Token.EOF: exit = true; break; default: Error("Expect keyword got " + keyword); break; } } if (lastToken == Token.Colon) { GetNextToken(); Statment(); } } #region Functions private Dictionary FunctionDictionary { get; set; } private void GenerateFunctionDictionary() { FunctionDictionary = new Dictionary(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 args) { if (args.Count < 1) throw new ArgumentException(); return args[0].Convert(ValueType.String); } [BuiltInFunction("num")] private Value Num(List args) { if (args.Count < 1) throw new ArgumentException(); return args[0].Convert(ValueType.Real); } [BuiltInFunction("abs")] private Value Abs(List args) { if (args.Count < 1) throw new ArgumentException(); return new Value(Math.Abs(args[0].Real)); } [BuiltInFunction("min")] private Value Min(List 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 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 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 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 KeywordMethods { get; set; } private void GenerateKeywordDictionary() { KeywordMethods = new Dictionary(); 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() { console.Write(Expr().ToString()); } [TargetKeyword(Token.PrintL)] void PrintL() { console.PrintSingleLine(Expr().ToString()); } [TargetKeyword(Token.DrawLine)] void DrawLine() { 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()); 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); else vars[lex.Identifer] = new Value(input); GetNextToken(); if (lastToken != Token.Comma) break; GetNextToken(); } } Value Expr() { Dictionary prec = new Dictionary() { { 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 stack = new Stack(); Stack operators = new Stack(); 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 (FunctionDictionary.ContainsKey(lex.Identifer)) { string name = lex.Identifer; List args = new List(); GetNextToken(); Match(Token.LParen); start: if (GetNextToken() != Token.RParen) { args.Add(Expr()); if (lastToken == Token.Comma) goto start; } stack.Push(FunctionDictionary[name](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 stack, Token token) { Value b = stack.Pop(); Value a = stack.Pop(); Value result = a.BinOp(b, token); stack.Push(result); } } }