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