using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Antlr4.Runtime; using MoonSharp.Interpreter.CoreLib; using MoonSharp.Interpreter.Debugging; using MoonSharp.Interpreter.Diagnostics; using MoonSharp.Interpreter.Execution.VM; using MoonSharp.Interpreter.Interop; using MoonSharp.Interpreter.Loaders; using MoonSharp.Interpreter.Tree; using MoonSharp.Interpreter.Tree.Statements; namespace MoonSharp.Interpreter { /// /// This class implements a Moon# scripting session. Multiple Script objects can coexist in the same program but cannot share /// data among themselves unless some mechanism is put in place. /// public class Script { Processor m_MainRoutine = null; List m_Coroutines = new List(); ByteCode m_ByteCode; List m_Sources = new List(); Table m_GlobalTable; IDebugger m_Debugger; IScriptLoader m_ScriptLoader = DefaultScriptLoader; Table[] m_TypeMetatables = new Table[(int)DataType.MaxMetaTypes]; static Script() { DefaultScriptLoader = new ClassicLuaScriptLoader(); } /// /// Initializes a new instance of the class. /// public Script() : this(CoreModules.Preset_Default) { } /// /// Initializes a new instance of the class. /// /// The core modules to be pre-registered in the default global table. public Script(CoreModules coreModules) { DebugPrint = s => { Console.WriteLine(s); }; m_ByteCode = new ByteCode(); m_GlobalTable = new Table(this).RegisterCoreModules(coreModules); m_MainRoutine = new Processor(this, m_GlobalTable, m_ByteCode); ReseedRandomGenerator(DateTime.Now.Millisecond); } /// /// Gets or sets the script loader to use. A script loader wraps all code loading from files, so that access /// to the filesystem can be completely overridden. /// /// /// The current script loader. /// public IScriptLoader ScriptLoader { get { return m_ScriptLoader; } set { m_ScriptLoader = value; } } /// /// Gets or sets the script loader which will be used as the value of the /// ScriptLoader property for all newly created scripts. /// public static IScriptLoader DefaultScriptLoader { get; set; } /// /// Gets the default global table for this script. Unless a different table is intentionally passed (or setfenv has been used) /// execution uses this table. /// public Table Globals { get { return m_GlobalTable; } } /// /// Loads a string containing a Lua/Moon# function. /// /// The code. /// The global table to bind to this chunk. /// Name of the function used to report errors, etc. /// /// A DynValue containing a function which will execute the loaded code. /// public DynValue LoadFunction(string code, Table globalTable = null, string funcFriendlyName = null) { string chunkName = string.Format("", m_Sources.Count); SourceCode source = new SourceCode(funcFriendlyName ?? chunkName, code); m_Sources.Add(source); int address = Loader.LoadFunctionFromICharStream(new AntlrInputStream(code), m_ByteCode, funcFriendlyName ?? chunkName, m_Sources.Count - 1, globalTable ?? m_GlobalTable); if (m_Debugger != null) m_Debugger.SetSourceCode(m_ByteCode, null); return MakeClosure(address); } /// /// Loads a string containing a Lua/Moon# script. /// /// The code. /// The global table to bind to this chunk. /// Name of the code - used to report errors, etc. /// /// A DynValue containing a function which will execute the loaded code. /// public DynValue LoadString(string code, Table globalTable = null, string codeFriendlyName = null) { string chunkName = string.Format("", m_Sources.Count); SourceCode source = new SourceCode(codeFriendlyName ?? chunkName, code); m_Sources.Add(source); int address = Loader.LoadChunkFromICharStream(new AntlrInputStream(code), m_ByteCode, codeFriendlyName ?? chunkName, m_Sources.Count - 1, globalTable ?? m_GlobalTable); if (m_Debugger != null) m_Debugger.SetSourceCode(m_ByteCode, null); return MakeClosure(address); } /// /// Loads a string containing a Lua/Moon# script. /// /// The code. /// The global table to bind to this chunk. /// A DynValue containing a function which will execute the loaded code. public DynValue LoadFile(string filename, Table globalContext = null) { filename = m_ScriptLoader.ResolveFileName(filename, globalContext ?? m_GlobalTable); return LoadResolvedFile(filename, globalContext); } private DynValue LoadResolvedFile(string filename, Table globalContext) { if (m_ScriptLoader.HasCustomFileLoading()) { return LoadString(m_ScriptLoader.LoadFile(filename, globalContext ?? m_GlobalTable), globalContext, filename); } else { return LoadString(File.ReadAllText(filename), globalContext, filename); } } /// /// Loads and executes a string containing a Lua/Moon# script. /// /// The code. /// The global context. /// /// A DynValue containing the result of the processing of the loaded chunk. /// public DynValue DoString(string code, Table globalContext = null) { DynValue func = LoadString(code, globalContext); return Call(func); } /// /// Loads and executes a file containing a Lua/Moon# script. /// /// The filename. /// The global context. /// /// A DynValue containing the result of the processing of the loaded chunk. /// public DynValue DoFile(string filename, Table globalContext = null) { DynValue func = LoadFile(filename, globalContext); return Call(func); } /// /// Runs the specified file with all possible defaults for quick experimenting. /// /// The filename. /// A DynValue containing the result of the processing of the executed script. public static DynValue RunFile(string filename) { Script S = new Script(); return S.DoFile(filename); } /// /// Runs the specified code with all possible defaults for quick experimenting. /// /// The Lua/Moon# code. /// A DynValue containing the result of the processing of the executed script. public static DynValue RunString(string code) { Script S = new Script(); return S.DoString(code); } /// /// Creates a closure from a bytecode address. /// /// The address. /// private DynValue MakeClosure(int address) { Closure c = new Closure(this, address, new SymbolRef[0], new DynValue[0]); return DynValue.NewClosure(c); } /// /// Calls the specified function. /// /// The Lua/Moon# function to be called - callbacks are not supported. /// public DynValue Call(DynValue function) { return m_MainRoutine.Call(function, new DynValue[0]); } /// /// Calls the specified function. /// /// The Lua/Moon# function to be called - callbacks are not supported. /// The arguments to pass to the function. /// public DynValue Call(DynValue function, params DynValue[] args) { return m_MainRoutine.Call(function, args); } /// /// Calls the specified function. /// /// The Lua/Moon# function to be called - callbacks are not supported. /// The arguments to pass to the function. /// public DynValue Call(DynValue function, params object[] args) { return m_MainRoutine.Call(function, args.Select(v => DynValue.FromObject(this, v)).ToArray()); } /// /// Calls the specified function. /// /// The Lua/Moon# function to be called - callbacks are not supported. /// public DynValue Call(object function) { return Call(DynValue.FromObject(this, function)); } /// /// Calls the specified function. /// /// The Lua/Moon# function to be called - callbacks are not supported. /// The arguments to pass to the function. /// public DynValue Call(object function, params object[] args) { return Call(DynValue.FromObject(this, function), args); } /// /// Gets the main chunk function. /// /// A DynValue containing a function which executes the first chunk that has been loaded. public DynValue GetMainChunk() { return MakeClosure(0); } /// /// Attaches a debugger. /// /// The debugger object. public void AttachDebugger(IDebugger debugger) { m_Debugger = debugger; m_MainRoutine.AttachDebugger(debugger); foreach (var C in m_Coroutines) C.AttachDebugger(debugger); m_Debugger.SetSourceCode(m_ByteCode, null); } /// /// Loads a module as per the "require" Lua function. http://www.lua.org/pil/8.1.html /// /// The module name /// The global context. /// /// Raised if module is not found public DynValue RequireModule(string modname, Table globalContext = null) { Table globals = globalContext ?? m_GlobalTable; string filename = m_ScriptLoader.ResolveModuleName(modname, globals); if (filename == null) throw new ScriptRuntimeException("module '{0}' not found", modname); DynValue func = LoadResolvedFile(filename, globalContext); return func; } /// /// Gets the random generator associated with this Script /// public Random RandomGenerator { get; private set; } /// /// Reseeds the random generator. /// /// The seed. public void ReseedRandomGenerator(double seed) { RandomGenerator = new Random(); } /// /// Gets a type metatable. /// /// The type. /// public Table GetTypeMetatable(DataType type) { int t = (int)type; if (t >= 0 && t < m_TypeMetatables.Length) return m_TypeMetatables[t]; return null; } /// /// Sets a type metatable. /// /// The type. Must be Nil, Boolean, Number, String or Function /// The metatable. /// Specified type not supported : + type.ToString() public void SetTypeMetatable(DataType type, Table metatable) { int t = (int)type; if (t >= 0 && t < m_TypeMetatables.Length) m_TypeMetatables[t] = metatable; else throw new ArgumentException("Specified type not supported : " + type.ToString()); } /// /// Gets or sets the debug print handler /// public Action DebugPrint { get; set; } /// /// Warms up the parser/lexer structures so that Moon# operations start faster. /// public static void WarmUp() { Script s = new Script(CoreModules.Basic); s.LoadString("return 1;"); } } }