public void Metal() { Runner r = new Runner(); Action<string> adder = s => r.state.scope.set( "output", r.state.scope.get( "output" ) + s + "\r\n" ); r.state.scope.set( "testfunc", new FValue( (state, ps) => adder( "Native write: {0}\r\n".FormatI( ps[0] ) ) ) { scope = r.state.scope } ); r.state.scope.set( "metal", new MetalObject() { indexLookup = (state, idx) => { adder( "Index lookup for {0}".FormatI( idx ) ); state.pushResult( new LValue() { read = st => { adder( "Index {0} was read".FormatI( idx ) ); return 0; }, write = (st,val) => { adder( "Index {0} was written: {1}".FormatI( idx, val ) ); } } ); }, memberLookup = (state, name) => { adder( "Member lookup for {0}".FormatI( name ) ); state.pushResult( new LValue() { read = st => { adder( "Member {0} was read".FormatI( name ) ); return 0; }, write = (st,val) => { adder( "Member {0} was written: {1}".FormatI( name, val ) ); } } ); } } ); r.state.scope.set( "output", "" ); r.setScopeCallback( st => { if( st.StartsWithI( "#" ) ) { return st; } else return null; } ); string program = @" testfunc(""stuff!"") a = metal.foo metal.foo = 10 metal.bar = a b = metal[5] metal[""bear""] = ""kitten"" c = #10 "; runAndDump( "Metal", r, program, null ); }
public object invoke( VerbParameters param, bool coralContinuation ) { // Inject the verb script blob parameters as script variables. var scope = new Dictionary<string, object>(); scope["input"] = param.input; scope["inputwords"] = param.inputwords; scope["self"] = new Proxies.MobProxy(param.self, param.player); scope["obj"] = new Proxies.MobProxy(param.dobj, param.player); if (param.prep != Prep.None) scope["prep"] = param.prep.ToString().ToLowerInvariant(); else scope["prep"] = null; scope["indobj"] = new Proxies.MobProxy(param.iobj, param.player); if (param.prep2 != Prep.None) scope["prep2"] = param.prep2.ToString().ToLowerInvariant(); else scope["prep2"] = null; scope["indobj2"] = new Proxies.MobProxy(param.iobj2, param.player); scope["objwords"] = param.dobjwords; scope["prepwords"] = param.prepwords; scope["indobjwords"] = param.iobjwords; scope["prep2words"] = param.prep2words; scope["indobj2words"] = param.iobj2words; // Inject some standard MOO objects. scope["ambiguous"] = Proxies.MobProxy.Ambiguous; scope["none"] = Proxies.MobProxy.None; // Inject the player object. Proxies.PlayerProxy player = null; if (param.player != null) player = new Proxies.PlayerProxy( param.player, param.world ); scope["player"] = player; // "caller" is the same as the player, unless otherwise specified. if (param.caller != null) scope["caller"] = new Proxies.MobProxy(param.caller, param.player); else scope["caller"] = player; scope["args"] = param.args; scope["world"] = new Proxies.WorldProxy(param.world, param.player); scope["$"] = new Proxies.MobProxy(param.world.findObject(1), param.player); scope["perms"] = Proxies.PermBitsProxy.Static; Func<string,object> querier = (name) => { if (name.StartsWithI("#")) { int number = CultureFree.ParseInt(name.Substring(1)); if( number == Mob.Anon.id ) { Mob m = param.player.anonMob; if( m != null ) return new Proxies.MobProxy( m, param.player ); } return new Proxies.MobProxy(param.world.findObject(number), param.player); } return null; }; // Is the verb valid / compiled properly? if( !_coral.success ) { player.write( "Verb was not properly compiled." ); if( _coral.errors != null ) { foreach( var err in _coral.errors ) player.write( " line {0}, col {1}: {2}".FormatI( err.line, err.col, err.message ) ); } return null; } // We have to run the code first, for it to define its verb. var runner = new Coral.Runner(); var tempScope = new Coral.StandardScope( runner.state.baseScope ); runner.pushScope( tempScope ); runner.setScopeCallback( querier ); foreach( var kv in scope ) runner.addToScope( kv.Key, kv.Value ); runner.runSync( _coral ); Coral.FValue verbFunc = (Coral.FValue)tempScope.get( "verb" ); tempScope.delete( "verb" ); // Now that's done, hook up to the main state. runner = new Coral.Runner( param.player.coralState ); runner.pushScope( tempScope ); // Pass these on literally to any down-stream invokes. runner.state.baggage.set( VerbParamsKey, param ); Coral.StackTrace.StackFrame frame = new Coral.StackTrace.StackFrame() { unitName = "<climoo>", funcName = "<trampoline>" }; // If there wasn't one, throw a sensible error. if( verbFunc == null ) throw new InvalidOperationException( "Verb does not define a function called 'verb'." ); // Print security context debug info if the player's debug flag is set. if( player.get.attrHas( "debug" ) ) { var value = player.get.attrGet( "debug" ).contents; if( value is bool && (bool)value ) { string securityStack = String.Join( "->", param.player.actorContextStack.Select( id => "{0}".FormatI( id ) ) .ToArray() ); string securityText = "[color=#0cc]Running {0} as {1} ({2})[/color]".FormatI( this.name, param.player.actorContext, securityStack ); param.player.writeError( securityText ); } } // If we came from Coral and we're going to Coral, use a continuation. if( coralContinuation ) { return new Coral.AsyncAction() { action = Coral.AsyncAction.Action.Call, function = verbFunc, args = param.args, frame = frame }; } else { try { runner.state.scope.set( "!verb-" + this.name, verbFunc ); return runner.callFunction( "!verb-" + this.name, param.args, typeof( object ), frame ); } catch( Coral.CoralException ex ) { param.player.writeError( "Unhandled exception {0}: {1}\n{2}".FormatI( ex.name, ex.Message, ex.trace ) ); return null; } } }