/// <summary> /// Unpacks the specified LValue, if it's an LValue. Otherwise the value is returned again. /// </summary> public static object Deref( State state, object value ) { if( value is LValue ) return ((LValue)value).read( state ); else return value; }
/// <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 ); }
/// <summary> /// Unpacks the LValue from the result stack, if it's an LValue. Otherwise the value is returned again. /// </summary> public static object Deref( State state ) { object value = state.popResult(); if( value is LValue ) return ((LValue)value).read( state ); else return value; }
/// <summary> /// Handles various method calls on string objects. /// </summary> public static FValue Method( State state, string str, string name ) { if( name == "length" ) { return new FValue( (st2, args) => { st2.pushResult( str.Length ); } ); } else if( name == "format" ) { return new FValue( (st2, args) => { string result = str.FormatI( args ); st2.pushResult( result ); } ); } else if( name == "split" ) { return new FValue( (st2, args) => { // We should have 1 or 2 args. if( args.Length < 1 ) throw CoralException.GetArg( "Not enough args to string.split" ); // Convert the split-by array if necessary. string[] splitBy; if( args[0] is string ) splitBy = new string[] { Util.CoerceString( args[0] ) }; else splitBy = Util.CoerceStringArray( args[0] ); if( args.Length == 1 ) st2.pushResult( Util.CoerceStringListObject( str.Split( splitBy, StringSplitOptions.None ) ) ); else st2.pushResult( Util.CoerceStringListObject( str.Split( splitBy, Util.CoerceNumber( args[1] ), StringSplitOptions.None ) ) ); } ); } else if( name == "replace" ) { return new FValue( (st2, args) => { if( args.Length != 2 ) throw CoralException.GetArg( "Must pass replace() two args" ); string from = Util.CoerceString( args[0] ); string to = Util.CoerceString( args[1] ); st2.pushResult( str.Replace( from, to ) ); } ); } else throw CoralException.GetArg( "String object has no method '{0}'".FormatI( name ) ); }
/// <summary> /// Queues a Coral function to run asynchronously. /// </summary> public static void CallFunction( State state, string name, object[] args, StackTrace.StackFrame frame ) { // Convert the arguments. AstNode[] wrapped = WrapArgs( args ); // Construct a synthetic AstIdentifier for the name. AstIdentifier id = new AstIdentifier( name ); // Construct a synthetic AstCall. AstCall call = new AstCall( id, wrapped, frame ); call.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 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 ); }; }
string dumpScope( State s ) { string rv = ""; string[] names = s.scope.getNames(); foreach( string n in names ) { // Don't include things from the const values. if( s.constScope.has( n ) ) continue; object val = s.scope.get( n ); rv += "{0} = {1}\r\n".FormatI( n, dumpObject( val ) ); } return rv; }
public Player( int id ) { _id = id; // Default to acting as the player. _coralState = new Coral.State(); Coral.Runner r = new Coral.Runner( _coralState ); r.pushSecurityContext( new SecurityContext( "base", _id ) ); // If we're anon, make a mob for it. if( id == Mob.Anon.id ) { _anonWorld = new AnonWorld(); _anonMob = new AnonMob( _anonWorld, this ); _anonWorld.anonMob = _anonMob; } }
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 ); }
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 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 ); } }
// 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 void setProperty( State state, string name, object value ) { if( name == "arbitrary" ) _f = (string)value; if( name == "throws" ) throw new ArgumentException( "Can't write this" ); }
public bool hasProperty( State state, string name ) { return name == "arbitrary" || name == "other" || name == "throws"; }
public bool hasMethod( State state, string name ) { return name == "test2" || name == "complex" || name == "dumpcontext"; }
public object getProperty( State state, string name ) { if( name == "arbitrary" ) return "it's arbitrary, yo"; else if( name == "throws" ) throw new ArgumentException( "Can't read this" ); else return "something else"; }
public bool hasProperty( State state, string name ) { return false; }
public object callMethod( State state, string name, object[] args ) { if( name == "other" ) { Runner r = new Runner(); r.runSync( _code ); return new AsyncAction() { action = AsyncAction.Action.Call, function = (FValue)r.state.scope.get( "verb" ), args = args, frame = new StackTrace.StackFrame() }; } else if( name == "thrower" ) { Runner r = new Runner(); r.runSync( _code ); return new AsyncAction() { action = AsyncAction.Action.Call, function = (FValue)r.state.scope.get( "thrower" ), args = args, frame = new StackTrace.StackFrame() }; } else throw new NotImplementedException(); }
public Runner( State st ) { _state = st; }
public Runner() { _state = new State(); }
public object getProperty( State state, string name ) { return 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 ); } } }
public void setProperty( State state, string name, object value ) { }
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 ); }
/// <summary> /// Queues a Coral function to run asynchronously. /// </summary> public static void CallFunction( State state, FValue func, object[] args, StackTrace.StackFrame frame ) { // Convert the arguments. AstNode[] wrapped = WrapArgs( args ); // Construct a synthetic AstIdentifier for the name. AstNode funcNode = new WrapperAstObject( func ); // Construct a synthetic AstCall. AstCall call = new AstCall( funcNode, wrapped, frame ); call.run( state ); }
public object callMethod( State state, string name, object[] args ) { if( name == "test2" ) return "test worked " + String.Join( ",", args.Select( x => x.ToStringI() ).ToArray() ); else if( name == "dumpcontext" ) { var cxt = state.securityContext; return "Current context: " + ( cxt == null ? "none" : cxt.name ); } else if( name == "complex" ) { int which = (int)args[0]; if( which == 1 ) { return new AsyncAction() { action = AsyncAction.Action.Call, function = (FValue)args[1], args = new object[] { "added\r\n" }, frame = new StackTrace.StackFrame() { line = 0, col = 0, unitName = "test", funcName = "PtTest.{0}".FormatI( name ) } }; } else if( which == 2 ) { return new AsyncAction[] { new AsyncAction() { action = AsyncAction.Action.Variable, name = "shouldexist", value = "oh cool" }, new AsyncAction() { action = AsyncAction.Action.Callback, callback = st => { var rv = "{0}".FormatI( st.scope.get( "shouldexist" ) ); st.pushResult( rv ); } } }; } else if( which == 3 ) { var constscope = new ConstScope( state.scope ); constscope.setConstant( "testconst", "bob" ); return new AsyncAction[] { new AsyncAction() { action = AsyncAction.Action.Code, code = Compiler.Compile( "test", @" def innerfunc(x): return [x + 1, testconst] " ) }, new AsyncAction() { action = AsyncAction.Action.Call, name = "innerfunc", args = new object[] { 5 }, frame = new StackTrace.StackFrame() { line = 0, col = 0, unitName = "test", funcName = "PtTest.{0}".FormatI( name ) } }, new AsyncAction() { action = AsyncAction.Action.PushScope, scope = constscope } }; } else if( which == 4 ) { return new AsyncAction[] { new AsyncAction() { action = AsyncAction.Action.Code, code = Compiler.Compile( "test", @" def innerfunc(pt): return pt.dumpcontext() " ) }, new AsyncAction() { action = AsyncAction.Action.Call, name = "innerfunc", args = new object[] { this }, frame = new StackTrace.StackFrame() }, new AsyncAction() { action = AsyncAction.Action.PushSecurityContext, securityContext = new TestSC( "Context 1" ) } }; } else if( which == 5 ) { return new AsyncAction[] { new AsyncAction() { action = AsyncAction.Action.Code, code = Compiler.Compile( "test", @" def innerfunc(pt): return pt.dumpcontext() " ) }, new AsyncAction() { action = AsyncAction.Action.Call, name = "innerfunc", args = new object[] { this }, frame = new StackTrace.StackFrame() } }; } else /* if( which == 6 ) */ { throw new ArgumentException( "No idea what you're talking about!" ); } } else return null; }
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 bool hasMethod( State state, string name ) { return true; }