Exemplo n.º 1
0
        /// <summary>
        /// Executes an equality instruction between two values
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the equality</returns>
        private KBool Eq(KermitAST tree)
        {
            KObject a = Execute((KermitAST)tree.GetChild(0));
            KObject b = Execute((KermitAST)tree.GetChild(1));

            return(a == b);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Assign a value to a variable
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        private void Assign(KermitAST tree)
        {
            KermitAST lhs   = (KermitAST)tree.GetChild(0);
            KermitAST expr  = tree.GetChild(1) as KermitAST;
            KObject   value = Execute(expr);

            if (value != null && !value.IsVoid)
            {
                if (lhs.Type == KermitParser.DOT || lhs.Type == KermitParser.INDEX)
                {
                    FieldAssign(lhs, value);
                }
                else
                {
                    MemorySpace space = GetSpaceWithSymbol(lhs.Text) ?? _currentSpace;
                    KLocal      var   = space[lhs.Text];
                    if (var == null)
                    {
                        space[lhs.Text] = new KLocal(lhs.Text, value);
                    }
                    else
                    {
                        var.Value = value;
                    }
                }
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Load a field of an object
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the field</returns>
        private KObject FieldLoad(KermitAST tree)
        {
            KermitAST leftExpr = (KermitAST)tree.GetChild(0);
            KermitAST field    = (KermitAST)tree.GetChild(1);
            KObject   obj      = Execute(leftExpr);
            KObject   val;
            string    name;

            if (field.Type == KermitParser.INDEX)
            {
                field = (KermitAST)field.GetChild(0);
            }

            if (field.Type == KermitParser.CALL)
            {
                name = field.GetChild(0).Text;
                FunctionSpace fSpace     = new FunctionSpace(new FunctionSymbol(leftExpr.Text + "." + name, GlobalScope));
                MemorySpace   savedSpace = _currentSpace;
                _stack.Push(fSpace);
                object[] args = new object[field.ChildCount - 1];
                for (int i = 0; i < args.Length; ++i)
                {
                    KObject ko = Execute((KermitAST)field.GetChild(i + 1));
                    args[i] = obj is KArray ? ko : ko.Value;
                }
                try
                {
                    _currentSpace = fSpace;
                    val           = obj.CallInnerFunction(name, args);
                }
                finally
                {
                    _stack.Pop();
                    _currentSpace = savedSpace;
                }
            }
            else
            {
                name = field.Text;
                val  = obj.GetInnerField(name);
            }

            field = (KermitAST)field.Parent;
            if (field.Type == KermitParser.INDEX)
            {
                KermitAST memberTree = (KermitAST)field.GetChild(1);
                val = LoadItem(val, Execute(memberTree));
                if (val == null)
                {
                    ThrowHelper.TypeError($"'{name}' is not enumerable", StackTrace);
                }
            }

            if (val == null)
            {
                ThrowHelper.NoFieldError(obj.Value.GetType().Name, name, StackTrace);
            }
            return(val);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Adds two values.
        /// - If both are numbers, executes the arithmetic operation
        /// - If one of them is a string, then the two string representations are concatenated
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the execution</returns>
        private KObject Add(KermitAST tree)
        {
            KObject a = Execute((KermitAST)tree.GetChild(0));
            KObject b = Execute((KermitAST)tree.GetChild(1));

            if (a.IsString || b.IsString)
            {
                return(new KString(a.Value + b.Value.ToString()));
            }
            return(Arithmetic(tree));
        }
Exemplo n.º 5
0
        /// <summary>
        /// Load an item from an enumerable object
        /// </summary>
        /// <param name="tree">The tree to be executed</param>
        /// <returns>The loaded object</returns>
        private KObject LoadItem(KermitAST tree)
        {
            KermitAST expr       = (KermitAST)tree.GetChild(0);
            KermitAST memberTree = (KermitAST)tree.GetChild(1);
            KObject   res        = LoadItem(Execute(expr), Execute(memberTree));

            if (res == null)
            {
                ThrowHelper.TypeError($"'{expr.Text}' is not enumerable", StackTrace);
            }
            return(res);
        }
Exemplo n.º 6
0
        /// <summary>
        /// Compares two values if they are IComparable
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the comparison</returns>
        private int Compare(KermitAST tree)
        {
            KObject a = Execute((KermitAST)tree.GetChild(0));
            KObject b = Execute((KermitAST)tree.GetChild(1));

            if (a.Is <IComparable>() && b.Is <IComparable>())
            {
                return(((IComparable)a).CompareTo(b));
            }
            string name = a.Is <IComparable>() ? b.GetType().Name : a.GetType().Name;

            ThrowHelper.TypeError($"Type {name} is not comparable");
            return(-1);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Call a function
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the function</returns>
        private KObject Call(KermitAST tree)
        {
            string    fName    = tree.GetChild(0).Text;
            KFunction function = GetFunction(fName);

            if (function == null)
            {
                ThrowHelper.NameNotExists(fName, StackTrace);
                //Listener.Error($"Function name {fName} is not defined");
                return(null);
            }

            int argCount = tree.ChildCount - 1;

            if (!function.IsNative &&
                (function.Value.Arguments == null && argCount > 0 ||
                 function.Value.Arguments != null && argCount != function.Value.Arguments.Count))
            {
                ThrowHelper.TypeError($"Function {fName} takes {argCount} arguments", StackTrace);
                //Listener.Error($"Function {fName}: argument list mismatch");
                return(null);
            }

            List <KLocal> param     = new List <KLocal>(argCount);
            var           arguments = function.Value.Arguments.Values.GetEnumerator();

            for (int i = 0; i < argCount; ++i)
            {
                arguments.MoveNext();
                string    name         = function.IsNative ? "" : arguments.Current.Name;
                KermitAST argumentTree = (KermitAST)tree.GetChild(i + 1);
                KLocal    var;
                if (argumentTree.Type == KermitParser.REF)
                {
                    var = new KGlobal(name, argumentTree.GetChild(0).Text, _currentSpace);
                }
                else
                {
                    KObject argumentValue = Execute(argumentTree);
                    var = new KLocal(name, argumentValue);
                }
                param.Add(var);
            }

            return(CallFunction(function, param));
        }
Exemplo n.º 8
0
        /// <summary>
        /// Executes an if statement
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        private void IfStatement(KermitAST tree)
        {
            KermitAST condition = (KermitAST)tree.GetChild(0);
            KermitAST code      = (KermitAST)tree.GetChild(1);
            KermitAST elseCode  = tree.ChildCount == 3 ? (KermitAST)tree.GetChild(2) : null;

            KBool cres = TypeHelper.ToBool(Execute(condition));

            if (cres)
            {
                Execute(code);
            }
            else if (elseCode != null)
            {
                Execute(elseCode);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Assign a value to a field
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <param name="value">Value to be set</param>
        private void FieldAssign(KermitAST tree, KObject value)
        {
            KermitAST leftExpr = (KermitAST)tree.GetChild(0);
            KermitAST field    = (KermitAST)tree.GetChild(1);
            KObject   obj      = Execute(leftExpr);
            bool      isTree   = tree.Type == KermitParser.INDEX || field.Type == KermitParser.INDEX;

            if (field.Type == KermitParser.INDEX)
            {
                obj   = obj.GetInnerField(field.GetChild(0).Text);
                field = (KermitAST)field.GetChild(1);
            }

            if (isTree)
            {
                object fieldValue = Execute(field).Value;
                if (obj is KArray)
                {
                    ((KArray)obj)[(int)fieldValue] = value;
                }
                else
                {
                    object     real    = obj.Value;
                    Type       objType = real.GetType();
                    MethodInfo info    = objType.GetMethod("set_Item");
                    if (info != null)
                    {
                        info.Invoke(real, new[] { fieldValue, value.Value });
                    }
                    else
                    {
                        ThrowHelper.AttributeError($"{leftExpr.Text} is not asignable", StackTrace);
                    }
                }
            }
            else if (tree.Type == KermitParser.DOT)
            {
                string name = field.Text;
                if (!obj.SetInnerField(name, value))
                {
                    ThrowHelper.NoFieldError(obj.Value.GetType().Name, name, StackTrace);
                }
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Print the result of the node execution to the console if in REPL mode
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        private void ReplPrint(KermitAST tree)
        {
            KermitAST exec = (KermitAST)tree.GetChild(0);
            KObject   obj  = Execute(exec);

            if (ReplMode && obj != null && !(obj is KVoid))
            {
                Listener.Write(obj.ToString());
            }
        }
Exemplo n.º 11
0
        /// <summary>
        /// Performs a while loop
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        private void WhileLoop(KermitAST tree)
        {
            KermitAST condition = (KermitAST)tree.GetChild(0);
            KermitAST code      = (KermitAST)tree.GetChild(1);

            ScopeSpace  sp   = new ScopeSpace("while", _currentSpace);
            MemorySpace save = _currentSpace;

            try
            {
                _currentSpace = sp;
                KBool cond = Execute(condition).Cast <KBool>();
                while (cond)
                {
                    Execute(code);
                    cond = Execute(condition).Cast <KBool>();
                }
            }
            finally
            {
                _currentSpace = save;
            }
        }
Exemplo n.º 12
0
        /// <summary>
        /// Performs a for loop
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        private void ForLoop(KermitAST tree)
        {
            KermitAST begin     = (KermitAST)tree.GetChild(0);
            KermitAST condition = (KermitAST)tree.GetChild(1);
            KermitAST action    = (KermitAST)tree.GetChild(2);
            KermitAST code      = (KermitAST)tree.GetChild(3);

            ScopeSpace  sp   = new ScopeSpace("while", _currentSpace);
            MemorySpace save = _currentSpace;

            try
            {
                _currentSpace = sp;
                for (Execute(begin); (KBool)Execute(condition); Execute(action))
                {
                    Execute(code);
                }
            }
            finally
            {
                _currentSpace = save;
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Instance an object
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The instantiated object</returns>
        private KObject Instance(KermitAST tree)
        {
            KermitAST objName = (KermitAST)tree.GetChild(0);

            Symbol s = objName.Scope.Resolve(objName.Text);

            if (s is NativeSymbol)
            {
                object[] args = tree.Children.Skip(1).Select(x => Execute((KermitAST)x).Value).ToArray();
                return(InstantiateObject(s as NativeSymbol, args));
            }
            ThrowHelper.NameNotExists(objName.Text, StackTrace);
            return(null);
        }
Exemplo n.º 14
0
        /// <summary>
        /// Performs an arithmetic operation over two values
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The result of the execution</returns>
        private KObject Arithmetic(KermitAST tree)
        {
            KNumber a = Execute((KermitAST)tree.GetChild(0)).Cast <KNumber>();
            KNumber b = Execute((KermitAST)tree.GetChild(1)).Cast <KNumber>();

            switch (tree.Type)
            {
            case KermitParser.ADD:
                return(a + b);

            case KermitParser.SUB:
                return(a - b);

            case KermitParser.MUL:
                return(a * b);

            case KermitParser.DIV:
                return(a / b);
            }

            // We shouldn't reach this fragment
            ThrowHelper.TypeError($"Unsupported arithmetic operation {tree.Text}");
            return(null);
        }
Exemplo n.º 15
0
        /// <summary>
        /// Main interpreter method.
        /// This method distributes all node types to the corresponding functions to be executed
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>An object or null, the result of the execution</returns>
        /// <exception cref="InterpreterException">If an internal error occurs</exception>
        private KObject Execute(KermitAST tree)
        {
            try
            {
                switch (tree.Type)
                {
                case KermitParser.RPRINT:
                    ReplPrint(tree);
                    break;

                case KermitParser.BLOCK:
                    Block(tree);
                    break;

                case KermitParser.ASSIGN:
                    Assign(tree);
                    break;

                case KermitParser.IF:
                    IfStatement(tree);
                    break;

                case KermitParser.WHILE:
                    WhileLoop(tree);
                    break;

                case KermitParser.FOR:
                    ForLoop(tree);
                    break;

                case KermitParser.RETURN:
                    Return(tree);
                    break;

                case KermitParser.CALL:
                    return(Call(tree));

                case KermitParser.NEW:
                    return(Instance(tree));

                case KermitParser.ARR:
                    return(CreateArray(tree));

                // Arithmetic operations
                case KermitParser.ADD:
                    return(Add(tree));

                case KermitParser.SUB:
                case KermitParser.MUL:
                case KermitParser.DIV:
                    return(Arithmetic(tree));

                // Logic operations
                case KermitParser.EQ:
                    return(Eq(tree));

                case KermitParser.NE:
                    return((KBool) !Eq(tree));

                case KermitParser.LT:
                    return(Lt(tree));

                case KermitParser.BT:
                    return(Bt(tree));

                case KermitParser.LTE:
                    return((KBool) !Bt(tree));

                case KermitParser.BTE:
                    return((KBool) !Lt(tree));

                case KermitParser.NOT:
                    return(Not(tree));

                // Keep types at the bottom
                case KermitParser.TRUE:
                    return(KBool.True);

                case KermitParser.FALSE:
                    return(KBool.False);

                case KermitParser.NUM:
                    return((KNumber)Execute((KermitAST)tree.GetChild(0)) * (tree.ChildCount == 2 ? -1 : 1));

                case KermitParser.INT:
                    return((KInt)int.Parse(tree.Text));

                case KermitParser.CHAR:
                    return((KChar)tree.Text[1]);

                case KermitParser.FLOAT:
                    return((KFloat)float.Parse(tree.Text));

                case KermitParser.STRING:
                    return((KString)tree.Text.Substring(1, tree.Text.Length - 2));

                // Accessors
                case KermitParser.INDEX:
                case KermitParser.DOT:
                case KermitParser.ID:
                    return(Load(tree));

                default:
                    throw new InvalidOperationException("Node " + tree.Text + "<" + tree.Type + "> not handled");
                }
            }
            catch (InterpreterException e) when(e.CallStack == null)
            {
                e.CallStack = StackTrace;
                throw;
            }
            catch (Exception e) when(!(e is InterpreterException || e is ReturnValue))
            {
                ThrowHelper.InterpreterException(e.Message, e, StackTrace);
            }

            return(null);
        }
Exemplo n.º 16
0
 /// <summary>
 /// Interrupts the current block execution and sets the return value
 /// </summary>
 /// <param name="tree">Tree to be executed</param>
 private void Return(KermitAST tree)
 {
     _sharedReturnValue.Value = Execute((KermitAST)tree.GetChild(0));
     throw _sharedReturnValue;
 }
Exemplo n.º 17
0
        /// <summary>
        /// Negates an expression
        /// </summary>
        /// <param name="tree">Tree to be executed</param>
        /// <returns>The negated expression</returns>
        private KBool Not(KermitAST tree)
        {
            KObject inner = Execute((KermitAST)tree.GetChild(0));

            return(!inner);
        }