private static Object_AST evalHashLiteral(HashLiteral hash, Environment_AST env) { Dictionary <HashKey, HashPair> pairs = new Dictionary <HashKey, HashPair>(); foreach (var item in hash.Pairs) { Object_AST key = Eval(item.Key, env); if (key is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(key); //higher level error obscuring the true source of error } IHashable hashKey = key as IHashable; if (hashKey is null) { return(new Error_AST(string.Format("unusable as hash key: {0}", key.Type))); } Object_AST val = Eval(item.Value, env); if (val is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(val); //higher level error obscuring the true source of error } HashKey hashed = hashKey.HashKey(); pairs[hashed] = new HashPair { Key = key, Value = val }; } return(new Hash_AST { Pairs = pairs }); }
public static Environment_AST NewEnclosedEnvironment_AST(Environment_AST outer) { //Environment_AST env = new Environment_AST() { _outer = outer}; //env._outer = outer; return(new Environment_AST() { _outer = outer }); }
public static void Start(TextReader tIn, TextWriter tOut) { //create the env variable in outer scope so that it persists after executing a line of code and we can use a IDENT later Environment_AST env = Environment_AST.NewEnvironment_AST(); while (true) { tOut.Write(prompt); string line = tIn.ReadLine(); if (string.IsNullOrEmpty(line)) { break; } Lexer lex = new Lexer(line); #region MyRegion //var tok = lex.NextToken(); //while (tok.Type != TokenHelper.TokenType.EOF) { // tOut.WriteLine(tok); // tok = lex.NextToken(); //} #endregion Parser p = new Parser(lex); Program program = p.ParseProgram(); if (p.Errors().Count != 0) { printParserError(tOut, p.Errors()); continue; } //tOut.WriteLine(program.ToString()); //tOut.WriteLine(); Object_AST evaluated = Evaluator.Eval(program, env); if (evaluated != null) { tOut.WriteLine(evaluated.Inspect()); } } }
private Environment_AST _outer;//used to keep track of nested scopes private Environment_AST() { this._store = new Dictionary <string, Object_AST>(); this._outer = null; }
public static Object_AST Eval(AST_Helper.Node node, Environment_AST env) { switch (node) //makes use of pattern matching. otherwise would have to use if else { case Program p: return(EvalProgram(p.Statements, env)); //logic for Program and BlockStatement is similar but varies in the case where the BlockStatement is //actually a nested block having a return in the nested(deeper/inner) level case BlockStatement blkStmt: //would handle, for e.g., consequence and alternative statement blocks in IfExpression return(EvalBlockStatement(blkStmt.Statements, env)); case ExpressionStatement stmt: return(Eval(stmt.Expression, env)); case FunctionExpression funcExpr: return(new Function_AST(funcExpr.Parameters, funcExpr.Body, env)); case HashLiteral hash: return(evalHashLiteral(hash, env)); case FunctionCallExpression funcCallExpr: Object_AST func = Eval(funcCallExpr.Function, env); //Function could be Identifier(case below) or FunctionExpression(case above) if (func is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(func); //higher level error obscuring the true source of error } List <Object_AST> args = evalExpressions(funcCallExpr.Arguments, env); //Arguments is just a list of Expression if (args.Count == 1 && args[0] is Error_AST) { return(args[0]); } //now that we have Eval 'ed the Function(Identifier or FunctionExpression) as well as the arguments, why do't we just Eval the Body which is just a BlockStatement. //Before we Eval the Body, we have to provide it its own Enviroment (for binding the above eval'ed arguments to the function parameters) return(applyFunction(func, args)); case ArrayIndexExpression arrIdxExpr: Object_AST left = Eval(arrIdxExpr.Left, env); if (left is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(left); //higher level error obscuring the true source of error } Object_AST index = Eval(arrIdxExpr.Index, env); if (index is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(left); //higher level error obscuring the true source of error } return(evalIndexExpression(left, index)); case LetStatement letStmt: Object_AST val = Eval(letStmt.Value, env); if (val is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(val); //higher level error obscuring the true source of error } env.Set(letStmt.Name.Value, val); return(val); case ArrayLiteral arr: List <Object_AST> elements = evalExpressions(arr.Elements, env); if (elements.Count == 1 && elements[0] is Error_AST) { return(elements[0]); } return(new Array_AST { Elements = elements }); case Identifier ident: return(evalIdentifier(ident, env)); case PrefixExpression prefixExpression: Object_AST rightExpr = Eval(prefixExpression.Right, env); if (rightExpr is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(rightExpr); //higher level error obscuring the true source of error } return(evalPrefixExpression(prefixExpression.Operator, rightExpr)); case InfixExpression infixExpression: Object_AST leftExpr = Eval(infixExpression.Left, env); if (leftExpr is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(leftExpr); //higher level error obscuring the true source of error } Object_AST right = Eval(infixExpression.Right, env); if (right is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(right); //higher level error obscuring the true source of error } return(evalInfixExpression(infixExpression.Operator, leftExpr, right)); case ReturnStatement retStmt: //we evaluate the expression associated with ReturnStatement and wrap that in a ReturnValue_AST object to be used later Object_AST retVal = Eval(retStmt.ReturnValue, env); //so if we are returning a int, that int is first evaluated and wrapped in a Integer_AST if (retVal is Error_AST) //for recursive Eval calls, check the returned result of the recursive Eval. If an error, return rightaway. Otherwise we will get a { return(retVal); //higher level error obscuring the true source of error } return(new ReturnValue_AST(retVal)); //and then that Integer_AST is again wrapped inside a ReturnValue_AST. case IfExpression ifExpr: return(evalIfExpression(ifExpr, env)); case IntegerLiteral i: return(new Integer_AST(i.Value)); case BooleanLiteral i: //return new Boolean_AST(i.Value);// this will create new objects for every true or false encountered. stop this profileration return(nativeBoolToBooleanObject(i.Value)); case StringLiteral i: return(new String_AST(i.Value)); default: //return new Null_AST();// this will create new null objects every time. stop this profileration return(null_AST); case null: throw new ArgumentNullException(nameof(node)); } }