Esempio n. 1
0
        public static MonkeyEnvironment NewEnclosedEnvironment(MonkeyEnvironment outer)
        {
            var env = NewEnvironment();

            env.Outer = outer;
            return(env);
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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}"));
        }
Esempio n. 4
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);
        }
Esempio n. 5
0
        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);
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
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);
        }
Esempio n. 8
0
        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
            });
        }
Esempio n. 9
0
        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()}");
            }
        }
Esempio n. 10
0
 public MonkeyFunction(List <Identifier> parameters, BlockStatement body, MonkeyEnvironment env)
 {
     Parameters = parameters;
     Body       = body;
     Env        = env;
 }