Beispiel #1
0
        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);
    }
Beispiel #4
0
 private static IMonkeyObject EvalPrefixExpression(string op, IMonkeyObject right)
 {
     return(op switch
     {
         "!" => EvalBangOperatorExpression(right),
         "-" => EvalMinusPrefixOperatorExpression(right),
         _ => new MonkeyError($"Unknown operator: {op}{right.Type}")
     });
Beispiel #5
0
 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}"));
 }
Beispiel #6
0
        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
            });
        }
Beispiel #7
0
 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);
 }
Beispiel #8
0
        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}"));
            }
        }
Beispiel #9
0
        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
            });
        }
Beispiel #10
0
        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]);
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
 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}"));
     }
 }
Beispiel #13
0
        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);
        }
Beispiel #14
0
        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}"));
            }
        }
Beispiel #15
0
        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);
        }
Beispiel #16
0
        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}"));
            };
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
 public MonkeyReturnValue(IMonkeyObject value)
 {
     Value = value;
 }
 private static void TestNullObject(IMonkeyObject obj) => Assert.Equal(Evaluator.Null, obj);
Beispiel #20
0
 public IMonkeyObject Set(string name, IMonkeyObject val)
 {
     Store[name] = val;
     return(val);
 }
Beispiel #21
0
 public HashPair(IMonkeyObject key, IMonkeyObject value)
 {
     Key   = key;
     Value = value;
 }
Beispiel #22
0
 private static bool IsError(IMonkeyObject obj) =>
 obj != null ? obj.Type == ObjectType.Error : false;