Beispiel #1
0
 public override void run( State state )
 {
     // We execute here by simply "running" each of the steps and letting them
     // push onto the stack. There is no branching possible, and these calls will
     // be shallow / non-blocking, so there's no need to do anything tricky or conditional.
     //
     // One point of interest is that we push a "clear results" step after each statement
     // just to verify that nothing was left on the results stack (this happens and is okay).
     foreach( AstNode c in ((IEnumerable<AstNode>)_children).Reverse() )
     {
     // Clear the result stack after each statement.
     state.pushAction( new Step( this, st2 => st2.clearResults(), "[Clear Results]" ) );
     c.run( state );
     };
 }
Beispiel #2
0
 public override void run( State state )
 {
     // We execute by executing the "lhs" and "rhs" code, then using the lhs's
     // LValue callbacks to set the value.
     state.pushAction(
     new Step( this, s =>
     {
         LValue lv = (LValue)s.popResult();
         object rv = LValue.Deref( s );
         lv.write( s, rv );
     } )
     );
     this.lhs.run( state );
     this.rhs.run( state );
 }
Beispiel #3
0
        public override void run( State state )
        {
            // We execute here by searching up the stack for the previous function call
            // scope marker, and unwinding past that, then pushing on our return value.
            state.pushAction( new Step( this, st =>
            {
            st.pushAction( new Step( this, st2 =>
            {
                // Make sure we don't leak LValues.
                st2.pushResult( LValue.Deref( st2 ) );

                // And do the stack unwind.
                st2.unwindActions( step => AstCall.IsScopeMarker( step ) );
            }, "return: stack unwinder" ) );

            if( this.value != null )
                this.value.run( st );
            else
                st.pushResult( null );
            } ) );
        }
Beispiel #4
0
 public override void run( State state )
 {
     // We execute here by executing the condition, then interpreting the
     // outcome of that expression to determin if we should push on the contents
     // of the block.
     //
     // In order to handle multiple elif clauses, we curry a "clause index" value
     // in the continuations so that it can chain to the next elif or else if
     // the condition isn't true.
     state.pushAction( new Step( this, st => ifRunner( st, 0 ), this.clauses[0].ToString() ) );
     this.clauses[0].condition.run( state );
 }
Beispiel #5
0
        // Pulls the result of a conditional comparison and either queues the attached
        // block, or queues the next comparison.
        void ifRunner( State st, int clauseIndex )
        {
            object result = LValue.Deref( st );
            bool conv = Util.CoerceBool( result );

            if( conv )
            this.clauses[clauseIndex].block.run( st );
            else
            {
            ++clauseIndex;
            if( clauseIndex >= this.clauses.Length )
                return;

            if( this.clauses[clauseIndex].condition == null )
            {
                this.clauses[clauseIndex].block.run( st );
            }
            else
            {
                st.pushAction( new Step( this, st2 => ifRunner( st2, clauseIndex ), this.clauses[clauseIndex].ToString() ) );
                this.clauses[clauseIndex].condition.run( st );
            }
            }
        }
Beispiel #6
0
 // This IEnumerable implementation will be very inefficient for large arrays.
 void oneIteration( State state )
 {
     state.pushAction( new Step( this, st =>
     {
         object resultObj = LValue.Deref( st );
         bool result = Util.CoerceBool( resultObj );
         if( result )
         {
             st.pushAction( new Step( this, st2 =>
                 {
                     oneIteration( st2 );
                 }, "while: next block runner" ) );
             if( this.postLoop != null )
                 this.postLoop.run( st );
             st.pushAction( new Step( this, a => {}, BlockMarker ) );
             this.block.run( st );
         }
     }, "while: test checker" ) );
     this.test.run( state );
 }
Beispiel #7
0
 public override void run( State state )
 {
     // We execute by first evaluating the test, and then if that's true, pushing one
     // iteration of the loop onto the action stack. Each iteration, if it completes
     // successfully, will push the next on. We also push a while loop marker on so
     // that break and continue work.
     state.pushAction( new Step( this, a => {}, LoopMarker ) );
     oneIteration( state );
     if( this.preLoop != null )
     this.preLoop.run( state );
 }
Beispiel #8
0
        public override void run( State state )
        {
            // We execute by pushing all the code for building the keys and values onto the
            // action stack, and finish off with one that will combine them all.
            state.pushAction(
            new Step( this, s =>
            {
                var dict = new Dictionary<object, object>();
                for( int i=0; i<this.pairs.Count; ++i )
                {
                    object key = LValue.Deref( s );
                    object value = LValue.Deref( s );
                    dict[key] = value;
                }

                s.pushResult( dict );
            } )
            );
            foreach( Pair p in this.pairs )
            {
            p.key.run( state );
            p.value.run( state );
            }
        }
Beispiel #9
0
 public override void run( State state )
 {
     // We execute by pushing on a stack unwind marker step, then executing our
     // try block. If nothing happens, the unwind marker will execute any finally block.
     // If an exception is thrown, then we'll unwind the stack until we hit the stack
     // marker, then execute any except block. We'll then execute any finally block.
     //
     // Because exceptions can happen deep in the stack and/or from random sources,
     // we provide a throwException method to take care of the dirty work.
     state.pushAction( new Step( this, a =>
     {
         if( this.finallyBlock != null )
             this.finallyBlock.run( state );
     },
     TryMarker )
     );
     this.tryBlock.run( state );
 }
Beispiel #10
0
 public override void run( State state )
 {
     // How we execute depends on whether we're a lambda or not. In the lambda case, we
     // just push an FValue on the result stack and call it done. For the non-lambda case,
     // we create a variable in the scope with an FValue in it. In either case, there's not
     // much actual executing being done here.
     FValue fv = new FValue( this );
     fv.scope = state.scope;
     if( this.lambda )
     {
     state.pushAction( new Step( this, st => st.pushResult( fv ) ) );
     }
     else
     {
     state.pushAction( new Step( this, st => st.scope.set( this.name, fv ) ) );
     }
 }
Beispiel #11
0
        // This IEnumerable implementation will be very inefficient for large arrays.
        void oneIteration( State state, IEnumerable<object> array, int curIndex )
        {
            if( curIndex < array.Count() - 1 )
            {
            state.pushAction( new Step( this,
                st =>
                {
                    oneIteration( st, array, curIndex + 1 );
                },
                NextIterationMarker )
            );
            }
            else
            {
            // This is still needed to make continue work.
            state.pushAction( new Step( this, st => {}, NextIterationMarker ) );
            }

            this.block.run( state );

            state.pushAction( new Step( this,
            st =>
            {
                st.scope.set( this.loopVariable, array.Skip( curIndex ).First() );
            },
            "for: assign iterator " + this.loopVariable )
            );
        }
Beispiel #12
0
        public override void run( State state )
        {
            // We execute by first evaluating the loopOver and then pushing one iteration of
            // the loop onto the action stack. Each iteration, if it completes successfully,
            // will push the next on. The array and current index are curried. We also push
            // a for loop marker on so that break works.
            state.pushAction( new Step( this, st =>
            {
            object over = LValue.Deref( st );
            IEnumerable<object> overTyped;
            if( over is List<object> )
                overTyped = (IEnumerable<object>)over;
            else if( over is Dictionary<object,object> )
                overTyped = ((Dictionary<object,object>)over).Keys;
            else
                throw CoralException.GetArg( "Value is not enumerable" );

            IScope forScope = new ParameterScope( st.scope, new string[] { this.loopVariable } );
            state.pushActionAndScope( new Step( this, a => {}, ScopeMarker ), forScope );
            state.scope.set( this.loopVariable, null );

            oneIteration( st, overTyped, 0 );
            } ) );
            this.loopOver.run( state );
        }
Beispiel #13
0
        public override void run( State state )
        {
            // We execute here by evaulating each of the parameters as well as the source, and then
            // evaluating the source FValue; of course if we don't get an FValue, that's an error.
            state.pushAction( new Step( this, st =>
            {
            // Make sure the FValue isn't an LValue.
            object fvo = st.popResult();
            if( fvo is FValue )
            {
                // Everything's okay
            }
            else if( fvo is LValue )
                fvo = LValue.Deref( st, fvo );
            else
                throw CoralException.GetArg( "Attempted call to non-function" );
            if( fvo == null )
                throw CoralException.GetArg( "Attempted call to null function" );
            FValue fv = (FValue)fvo;

            // Gather arguments before we push any scopes.
            var argsArray = new List<object>();
            if( fv.func != null )
            {
                for( int i=0; i<this.parameters.Length; ++i )
                {
                    object value = LValue.Deref( st );
                    argsArray.Add( value );
                }
            }
            else
            {
                object[] args = this.parameters.Select( n => LValue.Deref( st ) ).ToArray();
                argsArray.AddRange( args );
            }

            // Push on a scope marker so that the function will run in its own scope.
            IScope oldScope;
            if( fv.scope == null )
                oldScope = st.constScope;
            else
                oldScope = fv.scope;
            st.pushActionAndScope( new Step( this, a => {}, ScopeMarker ), new StandardScope( oldScope ) );

            // The actual run action.
            if( fv.func != null )
            {
                // Push on a pusher for a null return value. This is to ensure that calls always return
                // something, for result stack sanity. This won't get executed if the function returns
                // a value on its own.
                st.pushAction( new Step( this, st2 => st2.pushResult( null ), "call: result pusher" ) );

                // Set a second scope with just the parameters.
                IScope callScope = new ParameterScope( st.scope,
                    fv.func.parameters.Union( new string[] { "arguments" } ).ToArray()  );
                st.pushActionAndScope( new Step( this, a => {}, "call: parameter scope" ), callScope );

                callScope.set( "arguments", argsArray );
                for( int i=0; i<argsArray.Count; ++i )
                    if( i < fv.func.parameters.Length )
                    {
                        string name = fv.func.parameters[i];
                        st.scope.set( name, argsArray[i] );
                    }
                    else
                        break;

                // Do the actual function call.
                fv.func.block.run( st );
            }
            else if( fv.metal != null )
            {
                fv.metal( st, argsArray.ToArray() );
            }
            else
                throw CoralException.GetArg( "Attempt to call null function" );
            } ) );
            this.source.run( state );
            foreach( AstNode p in this.parameters )
            p.run( state );
        }
Beispiel #14
0
 public override void run( State state )
 {
     // We execute by evaulating the RValue portion first, then producing an LValue. If
     // the RValue is itself an LValue, we need to deref that first.
     state.pushAction( new Step( this, st =>
     {
     object rval = LValue.Deref( st );
     if( rval is MetalObject )
     {
         // Metal object callbacks produce an LValue automatically.
         ((MetalObject)rval).memberLookup( st, this.member );
     }
     else
     {
         // We must produce an LValue ourselves, that wraps the dictionary access.
         st.pushResult( new LValue()
         {
             read = st3 =>
             {
                 if( rval is Dictionary<object,object> )
                 {
                     var dict = (Dictionary<object,object>)rval;
                     if( dict.ContainsKey( this.member ) )
                         return dict[this.member];
                     else
                         return null;
                 }
                 else if( rval is List<object> )
                 {
                     if( this.member == "length" )
                     {
                         return new FValue( (st4, args) =>
                             {
                                 st4.pushResult( ((List<object>)rval).Count );
                             }
                         );
                     }
                     else
                         return null;
                 }
                 else if( rval is string )
                 {
                     return StringObject.Method( st3, (string)rval, this.member );
                 }
                 else
                 {
                     return null;
                 }
             },
             write = ( st3, val ) =>
             {
                 if( rval is Dictionary<object,object> )
                 {
                     var dict = (Dictionary<object,object>)rval;
                     dict[this.member] = val;
                 }
                 else
                 {
                     throw CoralException.GetArg( "Can't access value as object for write" );
                 }
             }
         } );
     }
     } ) );
     this.rvalue.run( state );
 }
Beispiel #15
0
        public override void run( State state )
        {
            // We execute by getting the indexer value and the source, looking the
            // value in question up, and then pushing the result on the result stack.
            state.pushAction( new Step( this, st =>
            {
            object source = LValue.Deref( st );
            object index = LValue.Deref( st );

            if( source is Dictionary<object,object> )
            {
                var sourcedict = (Dictionary<object,object>)source;
                if( sourcedict.ContainsKey( index ) )
                    st.pushResult( sourcedict[index] );
                else
                    st.pushResult( null );
            }
            else if( source is List<object> && index is int )
            {
                var sourcelist = (List<object>)source;
                int indexint = (int)index;
                if( indexint < sourcelist.Count )
                    st.pushResult( sourcelist[indexint] );
                else
                    st.pushResult( null );
            }
            else if( source is string && index is int )
            {
                st.pushResult( StringObject.ArrayAccess( (string)source, (int)index ) );
            }
            else if( source is MetalObject && ((MetalObject)source).indexLookup != null )
            {
                ((MetalObject)source).indexLookup( st, index );
            }
            else
            {
                // Whatever is left never has a value.
                st.pushResult( null );
            }
            } ) );
            this.source.run( state );
            this.index.run( state );
        }
Beispiel #16
0
        /// <summary>
        /// Throws an exception.
        /// </summary>
        /// <param name="thrown">
        /// The object to throw. It's recommended that this be a dictionary with a
        /// "name" parameter at the least.
        /// </param>
        public void throwException( State state, object thrown )
        {
            // We want to have stack traces, so this is necessary... but we also want to
            // throw data objects and not C# objects. So we have to convert back and forth.
            if( !(thrown is CoralException) )
            thrown = new CoralException( thrown );
            var cex = (CoralException)thrown;
            if( cex.trace == null )
            cex.setStackTrace( state );
            thrown = cex.data;

            state.pushAction( new Step( this, st =>
            {
                // We'll want to hold on to the finally runner if there is one. This will
                // let us properly handle nested exceptions.
                Step finallyStep = null;
                st.unwindActions( step => IsTryMarker( step ), this.finallyBlock != null );
                if( this.finallyBlock != null )
                    finallyStep = st.popAction();

                // The except block may have a parameter.
                if( this.exceptIdentifer != null )
                {
                    IScope exceptScope = new ParameterScope( st.scope, new string[] { this.exceptIdentifer } );
                    exceptScope.set( this.exceptIdentifer, thrown );
                    state.pushActionAndScope( new Step( this, a => {}, "except: scope" ), exceptScope );
                }
                this.exceptBlock.run( st );

                if( finallyStep != null )
                {
                    finallyStep.description = "finally block";
                    st.pushAction( finallyStep );
                }
            },
            "throw" )
            );
        }
Beispiel #17
0
        /// <summary>
        /// Works like throwException(), except we search for the right try clause.
        /// </summary>
        public static void ThrowException( State state, object thrown )
        {
            Step tryStep = state.findAction( step => IsTryMarker( step ) );
            if( tryStep == null )
            {
            // Make sure we have a CoralException.
            if( !(thrown is CoralException) )
                thrown = new CoralException( thrown );

            CoralException cex = (CoralException)thrown;
            if( cex.trace == null )
                cex.setStackTrace( state );

            // Unwind everything else off the stack. There is an implicit "except" at the top anyway.
            state.clearActions();

            // Push on a thrower. We do this in a separate step so that the same try/catch can deal
            // with it at the Runner level.
            state.pushAction( new Step( null, a => { throw new UnhandledException( (CoralException)thrown ); }, "re-thrower" ) );
            return;
            }

            AstTry node = (AstTry)tryStep.node;
            node.throwException( state, thrown );
        }
Beispiel #18
0
        public override void run( State state )
        {
            // We execute by running the "lhs" and "rhs", and then applying the operator to them,
            // producing another value. If the result of either is an LValue, we have to dereference it.

            state.pushAction(
            new Step( this, st =>
            {
                object right = LValue.Deref( st );

                if( this.op == "+=" )
                {
                    object left = st.popResult();

                    // This one is a bit trickier since we do different things based on different
                    // types, and the left side needs to be an LValue.
                    if( !(left is LValue) )
                        throw CoralException.GetArg( "Left-hand side of += must be LValue" );

                    // We need to write the LValue back, but keep the result value on
                    // the result stack since this can be used as a normal expression too.
                    LValue lv = (LValue)left;
                    object val = lv.read( st );
                    val = Plus( val, right );
                    lv.write( st, val );
                    st.pushResult( val );
                }
                else
                {
                    if( this.left != null )
                    {
                        object left = LValue.Deref( st );
                        st.pushResult( s_operations[this.op]( left, right ) );
                    }
                    else if( this.op == "-" )
                    {
                        st.pushResult( s_operations[this.op]( null, right ) );
                    }
                    else if( this.op == "!" )
                    {
                        st.pushResult( !((bool)Util.CoerceBool( right ) ) );
                    }
                    else
                        throw CoralException.GetInvProg( "Unary operator not supported" );
                }
            } )
            );
            this.right.run( state );
            if( this.left != null )
            this.left.run( state );
            else
            state.pushResult( null );
        }
Beispiel #19
0
        public override void run( State state )
        {
            state.pushAction( new Step( this, st =>
            {
            object source = LValue.Deref( st );
            object obegin = LValue.Deref( st );
            object oend = LValue.Deref( st );

            int? begin = obegin == null ? (int?)null : Util.CoerceNumber( obegin );
            int? end = oend == null ? (int?)null : Util.CoerceNumber( oend );

            if( source is List<object> )
            {
                var slist = (List<object>)source;
                int ibegin, iend;
                Util.ArraySlice( slist.Count, begin, end, out ibegin, out iend );
                var result = new List<object>( slist.Skip( ibegin ).Take( iend - ibegin ) );
                st.pushResult( result );
            }
            else if( source is string )
            {
                st.pushResult( StringObject.ArraySlice( (string)source, begin, end ) );
            }
            else
                throw CoralException.GetArg( "Can't array slice this type" );
            } ) );

            // These are different enough that we just break them out.
            this.source.run( state );
            if( this.begin != null && this.end != null )
            {
            this.begin.run( state );
            this.end.run( state );
            }
            else if( this.begin == null )
            {
            state.pushAction( new Step( this, st => st.pushResult( null ), "slice: null pusher" ) );
            this.end.run( state );
            }
            else
            {
            this.begin.run( state );
            state.pushAction( new Step( this, st => st.pushResult( null ), "slice: null pusher" ) );
            }
        }