using System; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using PorygonSharp.DiagnosticHandling; using PorygonSharp.EvalValues; namespace PorygonSharp { [StructLayout(LayoutKind.Sequential)] internal struct InternalScript { private readonly IntPtr _evaluator; private readonly IntPtr _scriptVariables; private readonly IntPtr _scriptTypes; private readonly SharedPointer _boundScript; private readonly SharedPointer _returnType; private readonly IntPtr _scriptOptions; internal readonly SharedPointer Diagnostics; } [StructLayout(LayoutKind.Sequential)] internal struct SharedPointer { private readonly IntPtr _pointer; private readonly int _references; public static implicit operator IntPtr(SharedPointer o) { return o._pointer; } public T Unwrap() { return Marshal.PtrToStructure(_pointer); } } public class Script : IDisposable { private readonly IntPtr _internalScriptHandle; private readonly InternalScript _internalScript; private Diagnostics _diagnostics; public Diagnostics Diagnostics => _diagnostics ?? (_diagnostics = new Diagnostics(_internalScript.Diagnostics)); public bool HasErrors => Diagnostics.HasErrors(); private static readonly RuntimeTypeHandle SetupHandle = typeof(CoreSetup).TypeHandle; public Script(string s, ScriptOptions options = null) { // Ensure core setup has been called RuntimeHelpers.RunClassConstructor(SetupHandle); var optionsPointer = options?.GetRawPointer() ?? IntPtr.Zero; _internalScriptHandle = Create(s, optionsPointer); _internalScript = Marshal.PtrToStructure(_internalScriptHandle); } private Script(IntPtr ptr) { _internalScriptHandle = ptr; _internalScript = Marshal.PtrToStructure(_internalScriptHandle); } public static Script Clone(Script script) { // Ensure core setup has been called RuntimeHelpers.RunClassConstructor(SetupHandle); return new Script(CloneScript(script._internalScriptHandle)); } public void Dispose() { Marshal.FreeHGlobal(_internalScriptHandle); } internal IntPtr GetRawPointer() { return _internalScriptHandle; } public EvalValue Evaluate() { var ptr = Evaluate(_internalScriptHandle); using (var result = new EvaluateResult(ptr)) { if (result.IsSuccess()) { return result.GetValue(); } throw new EvaluationException(result.GetError()); } } public bool HasVariable(string key) { return HasVariable(_internalScriptHandle, key); } public EvalValue GetVariable(string key) { var ptr = GetVariable(_internalScriptHandle, key); return new EvalValue(ptr); } public bool HasFunction(string key) { return HasFunction(_internalScriptHandle, key); } public EvalValue CallFunction(string key, params object[] parameters) { var scriptParameters = parameters.Select(x => EvalValueCreator.CreateValue(x).GetPointer()).ToArray(); var ptr = CallFunction(_internalScriptHandle, key, scriptParameters, scriptParameters.Length); foreach (var parameter in scriptParameters) { Marshal.FreeHGlobal(parameter); } using (var result = new EvaluateResult(ptr)) { if (result.IsSuccess()) { return result.GetValue(); } throw new EvaluationException(result.GetError()); } } public string GetBoundTreeString() { var length = GetTreeStringLength(_internalScriptHandle); var sb = new StringBuilder(length); GetTreeString(_internalScriptHandle, sb); var s = sb.ToString(); s = s.Substring(0, length); return s; } [DllImport("PorygonLang", EntryPoint = "CreateScript", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr Create([MarshalAs(UnmanagedType.LPWStr)]string s, IntPtr options); [DllImport("PorygonLang", EntryPoint = "EvaluateScript", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr Evaluate(IntPtr script); [DllImport("PorygonLang", EntryPoint = "HasVariable", CallingConvention = CallingConvention.Cdecl)] private static extern bool HasVariable(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key); [DllImport("PorygonLang", EntryPoint = "GetVariable", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr GetVariable(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key); [DllImport("PorygonLang", EntryPoint = "HasFunction", CallingConvention = CallingConvention.Cdecl)] private static extern bool HasFunction(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key); [DllImport("PorygonLang", EntryPoint = "CallFunction", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CallFunction(IntPtr script, [MarshalAs(UnmanagedType.LPWStr)]string key, IntPtr[] parameters, int parameterCount); [DllImport("PorygonLang", EntryPoint = "CloneScript", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr CloneScript(IntPtr script); [DllImport("PorygonLang", EntryPoint = "GetTreeStringLength", CallingConvention = CallingConvention.Cdecl)] private static extern int GetTreeStringLength(IntPtr script); [DllImport("PorygonLang", EntryPoint = "GetTreeString", CallingConvention = CallingConvention.Cdecl)] private static extern void GetTreeString(IntPtr script, StringBuilder sb); } }