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 ); }; }
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 ); }
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 ); } ) ); }
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 ); }
// 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 ); } } }
// 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 ); }
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 ); }
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 ); } }
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 ); }
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 ) ) ); } }
// 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 ) ); }
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 ); }
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 ); }
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 ); }
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 ); }
/// <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" ) ); }
/// <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 ); }
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 ); }
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" ) ); } }