Table of Contents

Embedding ezr²

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.

Currently, the only version recommended for embedding in your C# apps is the latest version of ezr² RE, available on NuGet.

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 to import it into Unity.

CodeExecutor

The high-level class to interact with the ezr² runtime is the CodeExecutor 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 to know more about wrappers.

public TMP_Text OutputText;

private void Start()
{
    CodeExecutor.CreateRuntimeContext("main");
    CodeExecutor.PopulateRuntimeContext(true);

    CodeExecutor.AddToContext(new EzrMethodWrapper((Action<string>)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 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:

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($"<color=#FF0000>{executionResult.Result.ToPureString(CodeExecutor.RuntimeResult)}</color>");
    else 
        ShowOnCanvas($"<color=#FF0000>{executionResult.LexerError ?? executionResult.ParseError}</color>");
}

Unity Shell Example

This is an example script for making a simple interpreter shell in Unity.

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<string>)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($"<color=#FF0000>{executionResult.Result.ToPureString(CodeExecutor.RuntimeResult)}</color>");
        else 
            ShowOnCanvas($"<color=#FF0000>{executionResult.LexerError ?? executionResult.ParseError}</color>");
    }
}

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!