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