diff --git a/docs/docfx.json b/docs/docfx.json index 6081ec7..8637c72 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -10,6 +10,7 @@ } ], "dest": "api", + "filter": "filterConfig.yml", "includePrivateMembers": true } ], diff --git a/docs/docsrc/CSharp-and-ezrSquared.md b/docs/docsrc/CSharp-and-ezrSquared.md index d470868..e2cff79 100644 --- a/docs/docsrc/CSharp-and-ezrSquared.md +++ b/docs/docsrc/CSharp-and-ezrSquared.md @@ -12,6 +12,7 @@ To learn more about embedding ezr² into your application, check the documentati Now, this is where **CSAELs** step in. CSAELs, or, **CSharp Assisted ezr² Libraries** are libraries written in C# for ezr² scripting. With CSAELs, anyone who knows C# can add any feature they want to ezr². These features can also be built-in to ezr² - it's a win-win situation! -All you have to do is wrap C# variables, functions and whatnot with attributes the ezr² interpreter can identify. All these "wrapper" attributes are part of the ezr² API. +All you have to do is wrap C# variables, functions and whatnot with attributes the ezr² interpreter can identify. All these "wrapper" attributes +are part of the ezr² API. To learn more about CSAELs, check the documentation [***here***](Get-started-with-CSAELs.md) diff --git a/docs/docsrc/Embedding-ezrSquared.md b/docs/docsrc/Embedding-ezrSquared.md index f87f5c1..c88ae93 100644 --- a/docs/docsrc/Embedding-ezrSquared.md +++ b/docs/docsrc/Embedding-ezrSquared.md @@ -1 +1,152 @@ -# TODO \ No newline at end of file +# Embedding ezr² + +Currently, the only version recommended for embedding in your C# apps is +the latest version of ezr² RE, available on [*NuGet*](https://www.nuget.org/packages/ezrSquared). + +The NuGet package is targeted for .NET 9 and .NET Standard 2.1 and is compatible with Unity. +You may need to use third-party packages like [*NuGetForUnity*](https://github.com/GlitchEnzo/NuGetForUnity) +to import it into Unity. + +Also, this documentation only applies to the *bleeding-edge latest version* of ezr² RE, which you may +have to compile from the `ezrSquared-re` branch of the repository. + +## CodeExecutor.cs + +The high-level class to interact with the ezr² runtime is the [`CodeExecutor`](~/api/EzrSquared.Executor.CodeExecutor.yml) class. + +### Setup + +All you have to do is call `CodeExecutor.CreateRuntimeContext("main");`. "main" is just the name for the runtime context. + +If you want to include built-in constants (like `true`, `false`, `nothing`), +functions (like `throw_error`, `assert`) or types (like `runtime_error`, `math_error`) +in the context, just call `CodeExecutor.PopulateRuntimeContext()`. + +If you are not making a console app, or want control of the IO, you can choose to not include the built-in IO +functions (`show`, `get` and `clear`), you can set `excludeIO` to `true`: `CodeExecutor.PopulateRuntimeContext(true)`. + +`CodeExecutor` also provides methods to add objects to the context. For example, you can add +a reference to a custom `show` function, which uses Unity's UI instead of `Console.WriteLine`. +You can check out the [*"Get Started with CSAELS" page*](Get-started-with-CSAELs.md) to know +more about wrappers. + +```csharp +public TMP_Text OutputText; + +private void Start() +{ + CodeExecutor.CreateRuntimeContext("main"); + CodeExecutor.PopulateRuntimeContext(true); + + CodeExecutor.AddToContext(new EzrMethodWrapper((Action)ShowOnCanvas, Context.Empty, Position.None, Position.None)); +} + +[WrapMember("show")] +private async void ShowOnCanvas(string text) +{ + await Awaitable.MainThreadAsync(); // Switch into Unity's main thread to modify the UI. + OutputText.text += $"\n{text}"; + + await Awaitable.BackgroundThreadAsync(); // Switch back to the background thread, so that ezr² does not block Unity. +} +``` + +### Interpreting Code + +Just call `CodeExecutor.Execute` with the code you want to interpret! It returns a +[*ExecutionResult*](~/api/EzrSquared.Executor.ExecutionResult.yml) object, which contains info +about the execution, like the AST, parse errors, runtime errors, and the actual result. + +So, for example, you can run a script asynchronously in Unity like so: + +```csharp +private async Awaitable RunScript(string code) +{ + ShowOnCanvas($">>> {code}"); + + await Awaitable.BackgroundThreadAsync(); + ExecutionResult executionResult = CodeExecutor.Execute(code); + + if (executionResult.Success) + ShowOnCanvas(executionResult.Result.ToString(CodeExecutor.RuntimeResult)); + else if (executionResult.Result is EzrRuntimeError) + ShowOnCanvas($"{executionResult.Result.ToPureString(CodeExecutor.RuntimeResult)}"); + else + ShowOnCanvas($"{executionResult.LexerError ?? executionResult.ParseError}"); +} +``` + +### Unity Shell Example + +This is an example script for making a simple interpreter shell in Unity. + +```csharp +using EzrSquared; +using EzrSquared.Executor; +using EzrSquared.Runtime; +using EzrSquared.Runtime.Types.Core.Errors; +using EzrSquared.Runtime.Types.Wrappers; +using EzrSquared.Runtime.Types.Wrappers.Members.Methods; +using System; +using TMPro; +using UnityEngine; + +public class Runner : MonoBehaviour +{ + [Tooltip("Text to show interpreter output.")] + public TMP_Text OutputText; + + [Tooltip("Input field for getting the user's code.")] + public TMP_InputField InputField; + + private void Start() + { + // Initialize the runtime context. + CodeExecutor.CreateRuntimeContext("main"); + + // Add built-ins, excluding IO functions. + CodeExecutor.PopulateRuntimeContext(true); + + // Add wrapper for "show" function which uses Unity UI. + CodeExecutor.AddToContext(new EzrMethodWrapper((Action)ShowOnCanvas, Context.Empty, Position.None, Position.None)); + } + + // This function will be called by a "Submit" or "Run" button. + public async void Run() + { + await RunScript(InputField.text); + } + + // Function to show text on the screen. + [WrapMember("show")] + private async void ShowOnCanvas(string text) + { + await Awaitable.MainThreadAsync(); // Switch into Unity's main thread to modify the UI. + OutputText.text += $"\n{text}"; + + await Awaitable.BackgroundThreadAsync(); // Switch back to the background thread, so that ezr² does not block Unity. + } + + // Function to run ezr² code in a background thread. + private async Awaitable RunScript(string code) + { + ShowOnCanvas($">>> {code}"); + + await Awaitable.BackgroundThreadAsync(); + ExecutionResult executionResult = CodeExecutor.Execute(code); + + if (executionResult.Success) + ShowOnCanvas(executionResult.Result.ToString(CodeExecutor.RuntimeResult)); + else if (executionResult.Result is EzrRuntimeError) + ShowOnCanvas($"{executionResult.Result.ToPureString(CodeExecutor.RuntimeResult)}"); + else + ShowOnCanvas($"{executionResult.LexerError ?? executionResult.ParseError}"); + } +} +``` + +## Lower-Level Access + +You can directly use the `Lexer`, `Parser` and `Interpreter` classes directly to execute code. You can even inherit from them to +customize the tokenization, parsing and interpretation of the ezr² language. This requires knowledge of the ezr² language +infrastructure, but provides much more customizability. You can start by cloning the ezr² repository and exploring the API! diff --git a/docs/filterConfig.yml b/docs/filterConfig.yml new file mode 100644 index 0000000..77f8513 --- /dev/null +++ b/docs/filterConfig.yml @@ -0,0 +1,8 @@ +### YamlMime:ManagedReference +apiRules: +- include: # The namespaces to generate + uidRegex: ^EzrSquared + type: Namespace +- exclude: + uidRegex: .* # Every other namespaces are ignored + type: Namespace \ No newline at end of file diff --git a/src/Executor/CodeExecutor.cs b/src/Executor/CodeExecutor.cs index d9fe8ec..4cec7aa 100644 --- a/src/Executor/CodeExecutor.cs +++ b/src/Executor/CodeExecutor.cs @@ -32,11 +32,11 @@ public static class CodeExecutor /// /// Creates the runtime context. /// - /// The file path to the code file this context will be used to execute. - public static void CreateRuntimeContext(string filePath) + /// The name of the context. + public static void CreateRuntimeContext(string name) { RuntimeContext?.Release(); - RuntimeContext = new Context(filePath, true, new Position(0, 0, filePath, string.Empty)); + RuntimeContext = new Context(name, true, new Position(0, 0, name, string.Empty)); } ///