public static MonkeyEnvironment NewEnclosedEnvironment(MonkeyEnvironment outer) { var env = NewEnvironment(); env.Outer = outer; return(env); }
private static MonkeyEnvironment ExtendFunctionEnv(MonkeyFunction fn, List <IMonkeyObject> args) { var env = MonkeyEnvironment.NewEnclosedEnvironment(fn.Env); for (var i = 0; i < fn.Parameters.Count; i++) { env.Set(fn.Parameters[i].Value, args[i]); } return(env); }
private static IMonkeyObject EvalIdentifier(Identifier node, MonkeyEnvironment env) { var(val, inCurrentEnvironment) = env.Get(node.Value); if (inCurrentEnvironment) { return(val); } MonkeyBuiltin fn; var inBuiltinEnvironment = MonkeyBuiltins.Builtins.TryGetValue(node.Value, out fn); if (inBuiltinEnvironment) { return(fn); } return(NewError($"Identifier not found: {node.Value}")); }
private static IMonkeyObject EvalBlockStatement(List <Statement> statements, MonkeyEnvironment env) { IMonkeyObject result = Evaluator.Null; foreach (var stmt in statements) { result = Eval(stmt, env); var rt = result.Type; if (rt == ObjectType.ReturnValue || rt == ObjectType.Error) { // Compared to EvalProgram(), we don't unwrap the return // value. Instead when an ReturnValue is encountered as the // result of evaluating a statement, we return it to // EvalProgram() for unwrapping. This halts outer block // evaluation and bubbles up the result. return(result); } } return(result); }
private static IMonkeyObject EvalIfExpression(IfExpression ie, MonkeyEnvironment env) { var condition = Eval(ie.Condition, env); if (IsError(condition)) { return(condition); } if (IsTruthy(condition)) { return(Eval(ie.Consequence, env)); } else if (ie.Alternative != null) { return(Eval(ie.Alternative, env)); } return(Null); }
private static List <IMonkeyObject> EvalExpressions(List <IExpression> exps, MonkeyEnvironment env) { var result = new List <IMonkeyObject>(); // Observe how, by definition, the arguments are evaluated left to // right. Since the side effect of evaluating one argument might be // relied on during the evaluation of the next, defining an explicit // evaluation order is important. foreach (var e in exps) { var evaluated = Eval(e, env); if (IsError(evaluated)) { return(new List <IMonkeyObject> { evaluated }); } result.Add(evaluated); } return(result); }
private static IMonkeyObject EvalProgram(List <Statement> statements, MonkeyEnvironment env) { IMonkeyObject result = Evaluator.Null; foreach (var stmt in statements) { result = Eval(stmt, env); // Prevents further evaluation if the result of the evaluation // is a return statement. Note how we don't return MReturnValue // directly, but unwrap its value. MReturnValue is an internal // detail to allow Eval() to signal to its caller that it // encountered and evaluated a return statement. if (result is MonkeyReturnValue rv) { return(rv.Value); } if (result is MonkeyError e) { return(e); } } return(result); }
private static IMonkeyObject EvalHashLiteral(HashLiteral node, MonkeyEnvironment env) { var pairs = new Dictionary <HashKey, HashPair>(); foreach (var kv in node.Pairs) { var key = Eval(kv.Key, env); if (IsError(key)) { return(key); } if (key is IHashable k) { var value = Eval(kv.Value, env); if (IsError(value)) { return(value); } var hashKey = k.HashKey(); var hashPair = new HashPair { Key = key, Value = value }; pairs.Add(hashKey, hashPair); } else { return(NewError($"Unusable as hash key: {key.GetType()}")); } } return(new MonkeyHash { Pairs = pairs }); }
public static IMonkeyObject Eval(INode node, MonkeyEnvironment env) { switch (node) { // Statements case Program p: return(EvalProgram(p.Statements, env)); case ExpressionStatement es: return(Eval(es.Expression, env)); case BlockStatement bs: return(EvalBlockStatement(bs.Statements, env)); case ReturnStatement rs: { var val = Eval(rs.ReturnValue, env); // Check for errors whenever Eval is called inside Eval in // order to stop errors from being passed around and // bubbling up far from their origin. if (IsError(val)) { return(val); } return(new MonkeyReturnValue(val)); } case LetStatement ls: { var val = Eval(ls.Value, env); if (IsError(val)) { return(val); } return(env.Set(ls.Name.Value, val)); } // Expressions case IntegerLiteral il: return(new MonkeyInteger(il.Value)); case Boolean_ b: return(NativeBoolToBooleanObject(b.Value)); case PrefixExpression pe: { var right = Eval(pe.Right, env); if (IsError(right)) { return(right); } return(EvalPrefixExpression(pe.Operator, right)); } case InfixExpression ie: { var left = Eval(ie.Left, env); if (IsError(left)) { return(left); } var right = Eval(ie.Right, env); return(IsError(right) ? right : EvalInfixExpression(ie.Operator, left, right)); } case IfExpression ife: return(EvalIfExpression(ife, env)); case Identifier i: return(EvalIdentifier(i, env)); case FunctionLiteral fl: { return(new MonkeyFunction(fl.Parameters, fl.Body, env)); } case CallExpression ce: { var function = Eval(ce.Function, env); if (IsError(function)) { return(function); } var args = EvalExpressions(ce.Arguments, env); if (args.Count == 1 && IsError(args[0])) { return(args[0]); } return(ApplyFunction(function, args)); } case ArrayLiteral al: { var elements = EvalExpressions(al.Elements, env); if (elements.Count == 1 && IsError(elements[0])) { return(elements[0]); } return(new MonkeyArray(elements)); } case IndexExpression ide: { var left = Eval(ide.Left, env); if (IsError(left)) { return(left); } var index = Eval(ide.Index, env); if (IsError(index)) { return(index); } return(EvalIndexExpression(left, index)); } case StringLiteral sl: return(new MonkeyString(sl.Value)); case HashLiteral hl: return(EvalHashLiteral(hl, env)); default: throw new Exception($"Invalid node type: {node.GetType()}"); } }
public MonkeyFunction(List <Identifier> parameters, BlockStatement body, MonkeyEnvironment env) { Parameters = parameters; Body = body; Env = env; }