private static IMonkeyObject EvalInfixExpression(string op, IMonkeyObject left, IMonkeyObject right) { if (left.Type == ObjectType.Integer && right.Type == ObjectType.Integer) { return(EvalIntegerInfixExpression(op, left, right)); } else if (left.Type == ObjectType.String && right.Type == ObjectType.String) { return(EvalStringInfixExpression(op, left, right)); } // Observe how for MonkeyBooleans we use reference comparison to // check for equality. This works only because the references are to // our singleton True and False instances. This wouldn't work for // MonkeyIntegers since those aren't singletons. 5 == 5 would be // false with reference equals. To compare MonkeyIntegers we must // unwrap the integers stored inside the MonkeyIntegers and compare // their values. else if (op == "==") { return(NativeBoolToBooleanObject(left == right)); } else if (op == "!=") { return(NativeBoolToBooleanObject(left != right)); } else if (left.Type != right.Type) { return(NewError($"Type mismatch: {left.Type} {op} {right.Type}")); } return(NewError($"Unknown operator: {left.Type} {op} {right.Type}")); }
private static void TestIntegerObject(IMonkeyObject obj, long expected) { Assert.IsType <MonkeyInteger>(obj); var result = (MonkeyInteger)obj; Assert.Equal(expected, result.Value); }
private static void TestBooleanObject(IMonkeyObject obj, bool expected) { Assert.IsType <MonkeyBoolean>(obj); var result = (MonkeyBoolean)obj; Assert.Equal(expected, result.Value); }
private static IMonkeyObject EvalPrefixExpression(string op, IMonkeyObject right) { return(op switch { "!" => EvalBangOperatorExpression(right), "-" => EvalMinusPrefixOperatorExpression(right), _ => new MonkeyError($"Unknown operator: {op}{right.Type}") });
private static IMonkeyObject EvalIndexExpression(IMonkeyObject left, IMonkeyObject index) { if (left.Type == ObjectType.Array && index.Type == ObjectType.Integer) { return(EvalArrayIndexExpression(left, index)); } else if (left.Type == ObjectType.Hash) { return(EvalHashIndexExpression(left, index)); } return(NewError($"Index operator not supported {left.Type}")); }
private static IMonkeyObject EvalMinusPrefixOperatorExpression(IMonkeyObject right) { if (right.Type != ObjectType.Integer) { return(NewError($"Unknown operator: -{right.Type}")); } var value = ((MonkeyInteger)right).Value; return(new MonkeyInteger { Value = -value }); }
private static IMonkeyObject UnwrapReturnValue(IMonkeyObject obj) { // Unwrapping is necessary because otherwise a return statement // would bubble up through several functions and stop the evaluation // in all of them. We only want to stop the evaluation of the last // called function's body. Otherwise, EvalBlockStatement() would // stop evaluating statements in the "outer" functions. if (obj is MonkeyReturnValue rv) { return(rv.Value); } return(obj); }
private static IMonkeyObject EvalPrefixExpression(string op, IMonkeyObject right) { switch (op) { case "!": return(EvalBangOperatorExpression(right)); case "-": return(EvalMinusPrefixOperatorExpression(right)); default: return(NewError($"Unknown operator: {op}{right.Type}")); } }
private static IMonkeyObject EvalStringInfixExpression(string op, IMonkeyObject left, IMonkeyObject right) { if (op != "+") { return(NewError($"Unknown operator: {left.Type} {op} {right.Type}")); } var leftVal = ((MonkeyString)left).Value; var rightVal = ((MonkeyString)right).Value; return(new MonkeyString { Value = leftVal + rightVal }); }
private static IMonkeyObject EvalArrayIndexExpression(IMonkeyObject array, IMonkeyObject index) { var arrayObject = (MonkeyArray)array; var idx = ((MonkeyInteger)index).Value; var max = arrayObject.Elements.Count - 1; if (idx < 0 || idx > max) { // Some languages throw an exception when the index is out of // bounds. In Monkey by definition we return null as the result. return(Null); } return(arrayObject.Elements[(int)idx]); }
private static IMonkeyObject EvalBangOperatorExpression(IMonkeyObject right) { if (right == True) { return(False); } else if (right == False) { return(True); } else if (right == Null) { return(True); } return(False); }
private static IMonkeyObject ApplyFunction(IMonkeyObject fn, List <IMonkeyObject> args) { if (fn is MonkeyFunction f) { var extendedEnv = ExtendFunctionEnv(f, args); var evaluated = Eval(f.Body, extendedEnv); return(UnwrapReturnValue(evaluated)); } else if (fn is MonkeyBuiltin b) { return(b.Fn(args)); } else { return(NewError($"Not a function: {fn.Type}")); } }
private static bool IsTruthy(IMonkeyObject obj) { if (obj == Null) { return(false); } else if (obj == True) { return(true); } else if (obj == False) { return(false); } return(true); }
private static IMonkeyObject EvalIntegerInfixExpression(string op, IMonkeyObject left, IMonkeyObject right) { var leftVal = ((MonkeyInteger)left).Value; var rightVal = ((MonkeyInteger)right).Value; switch (op) { case "+": return(new MonkeyInteger { Value = leftVal + rightVal }); case "-": return(new MonkeyInteger { Value = leftVal - rightVal }); case "*": return(new MonkeyInteger { Value = leftVal * rightVal }); case "/": return(new MonkeyInteger { Value = leftVal / rightVal }); case "<": return(NativeBoolToBooleanObject(leftVal < rightVal)); case ">": return(NativeBoolToBooleanObject(leftVal > rightVal)); case "==": return(NativeBoolToBooleanObject(leftVal == rightVal)); case "!=": return(NativeBoolToBooleanObject(leftVal != rightVal)); default: return(NewError($"Unknown operator: {left.Type} {op} {right.Type}")); } }
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); }
public static IMonkeyObject EvalHashIndexExpression(IMonkeyObject hash, IMonkeyObject index) { var hashObject = (MonkeyHash)hash; if (index is IHashable key) { HashPair pair; var ok = hashObject.Pairs.TryGetValue(key.HashKey(), out pair); if (!ok) { return(Null); } return(pair.Value); } else { return(NewError($"Unusable as hash key: {index.Type}")); }; }
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); }
public MonkeyReturnValue(IMonkeyObject value) { Value = value; }
private static void TestNullObject(IMonkeyObject obj) => Assert.Equal(Evaluator.Null, obj);
public IMonkeyObject Set(string name, IMonkeyObject val) { Store[name] = val; return(val); }
public HashPair(IMonkeyObject key, IMonkeyObject value) { Key = key; Value = value; }
private static bool IsError(IMonkeyObject obj) => obj != null ? obj.Type == ObjectType.Error : false;