Rhai - An embedded scripting language for Rust.

Last update: May 22, 2022

Rhai - Embedded Scripting for Rust

GitHub last commit Build Status stars license crates.io crates.io API Docs chat Reddit

Rhai logo

Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application.

Supported targets and builds

  • All common CPU targets for Windows, Linux and MacOS.
  • WebAssembly (WASM)
  • no-std
  • Minimum Rust version 1.49

Standard features

Protected against attacks

  • Don't Panic guarantee - Any panic is a bug. Rhai subscribes to the motto that a library should never panic the host system, and is coded with this in mind.
  • Sand-boxed - the scripting engine, if declared immutable, cannot mutate the containing environment unless explicitly permitted.
  • Rugged - protected against malicious attacks (such as stack-overflow, over-sized data, and runaway scripts etc.) that may come from untrusted third-party user-land scripts.
  • Track script evaluation progress and manually terminate a script run.

For those who actually want their own language

Project Site

rhai.rs

Documentation

See The Rhai Book for details on the Rhai scripting engine and language.

Playground

An Online Playground is available with syntax-highlighting editor, powered by WebAssembly.

Scripts can be evaluated directly from the editor.

License

Licensed under either of the following, at your choice:

Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

GitHub

https://github.com/rhaiscript/rhai
Comments
  • 1. Rhai playground (using WebAssembly)

    The idea is to make a playground for Rhai scripts that runs in a web browser which can showcase the features of the Rhai scripting language. It might also be possible for others to repurpose it as a Rhai script editor.

    I've started an attempt on https://github.com/alvinhochun/rhai-playground, but I am not making any promises. The master branch gets automatically built and deployed to: https://alvinhochun.github.io/rhai-playground-unstable/ I might irregularly upload specific builds to: https://alvinhochun.github.io/rhai-demo/

    Wish list (not a roadmap or to-do list):

    • [X] Ability to run Rhai scripts
    • [x] Proper syntax handling for Rhai scripts
    • [ ] Real-time AST compilation with error reporting (already works though pretty limited)
    • [ ] Integration with the book (embedding is now possible)
    • [ ] REPL
    • [ ] Autocomplete list for built-in packages
    • [ ] Provide more fancy IDE-like features
    • [ ] Engine settings
    • [ ] Custom modules
    • [ ] Heuristically guess variable types?
    • [ ] Debugging
    Reviewed by alvinhochun at 2020-06-21 13:40
  • 2. no_std error

    I keep getting can't find crate for std even with "no_std" feature

    [dependencies.rhai]
    git = "https://github.com/jonathandturner/rhai"
    features = ["no_std"]
    default-features = false
    

    Here is some of the output

    Blocking waiting for file lock on build directory
       Compiling core-error v0.0.0
       Compiling stm32f4 v0.11.0
    error[E0463]: can't find crate for `std`
      |                                                                                                                                  ing waiting for file lock on package cache
      = note: the `thumbv7em-none-eabihf` target may not be installed
    
    error: aborting due to previous error
    
    For more information about this error, try `rustc --explain E0463`.
    error: could not compile `core-error`.
    
    To learn more, run the command again with --verbose.
    warning: build failed, waiting for other jobs to finish...
    error: build failed
    
    Reviewed by ahmtcn123 at 2020-07-19 00:52
  • 3. [Question] How do I save rhai callbacks?

    Hey, I am trying to register a function that takes a rhai function as parameter and saves it in a struct, so that I can later call it. I am currently very confused and I don't know what I should do to achieve my goal.

    Should I save the AST of the function and later evaluate it?

    Setup

    engine.register_fn(
        "popup_new",
        |options: Map| {
            let mut p = Popup::new();
    
            for (key, val) in options {
                match key.as_str() {
                    // ... other stuff
                    "actions" => {
                        let actions = val.cast::<Array>();
                        
                        for action in actions {
                            let settings = action.cast::<Map>();
                            let mut action = PopupAction::default();
    
                            for (key, val) in settings {
                                match key.as_str() {
                                    "text" => { action.text = val.to_string(); },
                                    "cb" => { 
                                         // what do I do here?
                                    },
                                    _ => {}
                                };
                            }
                        }
                    }
                };
            }
        },
    )
    

    Usage

    popup_new(#{
        text: ["Checking for updates..."],
        padding: 5,
        actions: [#{
            text: "Update",
            cb: || { 
                print("Updating") 
            }
        }]
    });
    

    Thank you!

    Reviewed by TimUntersberger at 2020-08-16 09:52
  • 4. `DynamicWriteLock` used but not exposed in public API

    When creating a dynamic shared between the script and external environment, it's often convenient to create wrapper APIs for functions that do some of the heavy-lifted cast magic:

    struct Context {
        value: rhai::Dynamic,
    }
    
    impl Context {
        fn value(&self) -> rhai::INT {
            self.value.as_int().unwrap()
        }
    
        fn cooldown_mut(&self) -> rhai::dynamic::DynamicWriteLock<rhai::INT> {
            self.value.write_lock::<rhai::INT>().unwrap()
        }
    }
    

    However, DynamicWriteLock is not accessible, despite its use in the public API:

    error[E0603]: module `dynamic` is private
    
    Reviewed by CJKay at 2021-05-10 00:04
  • 5. Structural data construction and anonymous functions without closures

    Hello!

    I'm very impressed with Rhai project, and especially because of it's minimalistic design, transparent interoperability with Rust and active development. In my opinion Rhai is the best choice for Rust embedding scripting among other options we currently have in Rust ecosystem. And I'm going to use Rhai in my personal project of 3D procedural modelling toolkit based on ray marching of signed-distance fields. The scripting will be used for high-level scene live coding in my project.

    The scene itself consists of a hierarchy of domain specific objects describing various 3D primitives, their combinations and a lot of effects applied on top of them. And is currently constructing using Rust functions that I would like to export(at least partially) into Rhai context.

    There are practically two issues that I faced between my design and Rhai interoperability limitations. Let me show an example of a scene code in Rust:

    
    // starting scene description
    // "space" is a context of the current subspace of the scene
    Primitive::scene(|space| {
    
        space
            .view() // receiving &mut reference to the model view related to the current "space"
            .translate(1.5); // Mutating model view matrix
    
        space
            .add_spape(&Ellipsoid::sphere(1.0)) // creating a Sphere in the current context
            .union(|space| { // entering into a new inner context related to the just added shape of the outer context
                ... // similar operation with the inner context
            });
    
    });
    
    

    So, the first issue is that in Rhai we can't return &mut reference to the object. This is actually not a big deal as I can provide a setter instead of "&mut" getter to redefine(or update) owned property of the space object.

    But the second issue is actual hierarchical structures encoding. Entering into a new "space" scope as shown in my example above as far as I understand is not achievable in Rhai, because it doesn't have anonymous functions. In #159 @schungx mentioned that it would be easy to add such functions without closures, but he said without closures they are not really useful. I want to say that in my particular case they would be VERY useful even without closures. I don't practically use closures to the upper context that much. So, if you can include simplified syntax version in prior-1.0 release that would be very helpful.

    Also, as I understood from The Book, it is possible to deal with the context through the this variable inside a "method" function, but I didn't realize how to access(and especially how to "shadow") this in the registered functions if it's even possible.

    Since my project is in relatively early stage, I'm fine to slightly change my API design to better fit Rhai design requirements. I will be appreciated for your help and advices. My goal is to have a long-term cooperation with the project teams my project is related to. So if you need my contribution to Rhai, I will be glad to participate too.

    Thanks in advance!

    Ilya.

    Reviewed by Eliah-Lakhin at 2020-07-05 04:39
  • 6. Compiling Rhai to WASM

    I'm asking specifically for the wasm32-unknown-unknown target on web browser using wasm-pack. It looks like there should be nothing preventing Rhai from working, but since it is not explicitly stated anywhere I think I should ask.

    Reviewed by alvinhochun at 2020-06-16 12:28
  • 7. Allow functions to access global imports

    Splitting functionality up into multiple files is a bit annoying right now.

    Example

    main.rhai

    fn test1() {
      import "mod" as M;
    
      M::echo();
    }
    
    fn test2() {
      import "mod" as M;
    
      M::echo();
    }
    

    mod.rhai

    fn echo() {
      print("Hello World");
    }
    

    Having to write the import statement twice is really ugly and a bit annoying to write. Is there a way to write the main.rhai file like this?

    main.rhai

    import "mod" as M;
    
    fn test1() {
      M::echo();
    }
    
    fn test2() {
      M::echo();
    }
    

    Using the namespace merging feature makes this a bit more bearable, but requires you to scope your functions with prefixes.

    Any ideas?

    Reviewed by TimUntersberger at 2020-10-31 21:13
  • 8. Use cargo features additively.

    Adding more Cargo features should bring in more code and features, not less. Also adding a Cargo feature should almost never break existing scripts.

    With a major semver bump, I suggest to make the following changes:

    no_std

    Reverse it to std.

    plugins

    I don't know what it is, probably leave it as is.

    unchecked

    Reverse to checked.

    sync

    Leave as is.

    no_optimize

    Reverse to optimize. Maybe rhai::OptimizationLevel::None should be available even without it.

    no_float

    Reverse to float

    only_i32 only_i64

    This one is tricky. There should probably be integer feature that enables all integer types and also i32 and i64 features that selectively enable those types.

    integer should imply (but not be limited to) i32 + i64.

    A compile error should be emitted if none of three features (i32, i64, integers) are activated, unless Rhai can work without integers at all.

    It should be OK to activate both i32 and i64 features without integer feature.

    no_index

    Reverse to index.

    no_object

    Reverse to object

    no_function

    Reverse to function

    no_module

    Reverse to module.

    internals

    Leave as is.

    default = []

    default=["std","module","function","object","index","integer","float","optimize","checked","plugins?"]
    

    Additionally, I think keywords from missing features should still be reserved in scripts (unless explicitly configured otherwise from the Rust side), to avoid typical Rhai scripts breaking when a feature suddenly gets turned on from afar.

    Reviewed by vi at 2020-07-10 14:59
  • 9. Advice for adding rust_decimal support

    I'm looking to use Rhai for scripting in a financial application and I'm starting to research how I can add rust_decimal support to the runtime.

    Best case scenario for me is that all decimal literals are stored as a Decimal type. In my case I want to disable floating point support (which is already possible with features gates) and enable Decimal support.

    Does anyone have advice on the various approaches I can take?

    Reviewed by ObsceneGiraffe at 2020-08-17 12:03
  • 10. How to use with no-std

    I'm trying out rust on a cheap microcontroller following this tutorial https://polyfractal.com/post/rustl8710/

    The tutorial works great and the sample echo program runs over serial.

    I'd love to add in a rust scripting language and I really like the design of Rhai. But when I add the library, the compile fails because of std is missing.

    info: using existing install for 'nightly-x86_64-unknown-linux-gnu'
    info: override toolchain for '/home/tim/rustl8710/src/rust' set to 'nightly-x86_64-unknown-linux-gnu'
    
      nightly-x86_64-unknown-linux-gnu unchanged - rustc 1.16.0-nightly (4ce7accaa 2017-01-17)
    
    cd src/rust && xargo build --target thumbv7m-none-eabi
       Compiling rhai v0.4.0
    error[E0463]: can't find crate for `std`
      |
      = note: the `thumbv7m-none-eabi` target may not be installed
    

    I'm fairly new to rust still and am not quite clear on what this all means. Is Rhai meant to be used in such embedded use cases? It sure would be neat if it or something like it did work.

    Reviewed by creationix at 2017-01-18 04:18
  • 11. objects in rhai

    I see documentation on how to define structs in Rust and then make them available for use in Rhai. These structs can have fields that are accessible with dot notation, e.g:

    let foo = new_ts(); // new_ts() is a Rust-defined fn that returns a Rust-defined struct
    foo.bar = 5;
    

    Is there any way to create an object in Rhai without first having to define it in Rust? I'm looking for something like Javascript's objects which allow you do something like this:

    var foo = { bar: 0 };
    foo.bar = 5;
    

    Is this on the roadmap for Rhai?

    Reviewed by brianwp3000 at 2018-06-21 00:41
  • 12. access map in a map error

    let obj = #{"age": 9, "id":"who am i", "x-id": #{"nAAawwwwsshame":"zhu", "age": 100}}; log(obj["x-id"]["age"]);

    get a error: ErrorParsing(MalformedIndexExpr("Array or string expects numeric index, not a string"), 5:17)

    Reviewed by zhuchuanjing at 2022-05-16 21:38
  • 13. serialize/de-serialize doesn't appear to work for timestamp

    This is rhai:1.6.1

    I'm trying to serialize/de-serialize dynamic values and this works fine for strings and i64 (which I've tested). However, when I serialize a timestamp and try to de-serialize it, the value is de-serialized as a string.

    Hard to put together a good example, but this illustratess what I'm doing:

                    eprintln!("ABOUT TO INSERT, v: {:?}", value.type_name());
                    x.insert(key, value)
                        .map(|v: Option<Dynamic>| v.unwrap_or_else(|| Dynamic::from(())))
                        .map_err(|e: BoxError| e.to_string())?;
    
                    let raw: Dynamic = x
                        .get(key)
                        .map(|v: Option<Dynamic>| v.unwrap_or_else(|| Dynamic::from(())))
                        .map_err(|e: BoxError| e.to_string())?;
                    eprintln!("JUST RETRIEVED, v: {:?}", raw.type_name());
    
    

    Here's the output when I run something which exercises this code from rhai to insert as follows:

    (INSERTING SOME STUFF)
        request.context["a"] = 42;
        request.context["b"] = "a string";
        request.context["c"] = timestamp();
    
    (RETRIEVING SOME STUFF)
        let elapsed = response.context["a"];
        let elapsed = response.context["b"];
        let elapsed = response.context["c"];
    
    ABOUT TO INSERT, v: "i64"
    ABOUT TO INSERT, v: "string"
    ABOUT TO INSERT, v: "timestamp"
    JUST RETRIEVED, v: "i64"
    JUST RETRIEVED, v: "string"
    JUST RETRIEVED, v: "string" <= SHOULD BE timestamp
    
    Reviewed by garypen at 2022-05-05 15:39
  • 14. Consider adding to github/linguist

    I noticed that .rhai files don't have any highlighting on github, and that no issues exist about this yet. Github's highlighting is powered by github/linguist, and they allow people to submit new languages if they're popular enough (used in >200 repos). Unfortunately github's search is pretty broken atm so I'm not sure how many repositories rhai is in.

    Until github supports it, throwing this in a repo's .gitattributes will highlight it by pretending it's rust:

    *.rhai linguist-language=Rust
    
    Reviewed by Purpzie at 2022-03-14 01:23
  • 15. Type hints

    I'm in the middle of writing the LSP server I mentioned in #268, and in order to provide more useful information (such as field completions), some kind of type system is essential. Right now I'm planning HM-style type inference and external type definitions for modules just like it is in TypeScript's .d.ts files, but allowing users to define types for function signatures and let/const bindings inline would be very useful.

    Would it be possible (acceptable) to add optional type hint syntax in Rhai? I am thinking along the lines of Python's type hints, or a much simpler version of TypeScript's type system.

    Type hints would only serve the users and static analyzers, and they could be completely stripped when a script is compiled.

    I don't have exact fleshed-out proposal for this yet as I will have to experiment more in the LSP project first, but I would like to see something like this supported in Rhai in the future.

    Reviewed by tamasfe at 2021-11-15 03:16
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

May 13, 2022
[Proof of Concept] Embedded functional scripting language with YAML ¯\_(ツ)_/¯

[YAML, fun] Just an experimental project implementing embedded functional scripting language based on YAML syntax. API docs for the standard library:

Apr 28, 2022
Nyah is a programming language runtime built for high performance and comes with a scripting language.

?? Nyah ( Unfinished ) Nyah is a programming language runtime built for high performance and comes with a scripting language. ??️ Status Nyah is not c

Mar 6, 2022
Freebsd-embedded-hal - Like linux-embedded-hal but FreeBSD

freebsd-embedded-hal Implementation of embedded-hal traits for FreeBSD devices: gpio: using libgpio, with stateful and toggleable support, with suppor

Jan 12, 2022
Lisp dialect scripting and extension language for Rust programs

Ketos Ketos is a Lisp dialect functional programming language. The primary goal of Ketos is to serve as a scripting and extension language for program

Apr 25, 2022
A simple programming language made for scripting inspired on rust and javascript.

FnXY Programming Language Quick move: CONTRIBUTING | LICENSE What? FnXY is a simple programming language made for scripting inspired on rust and javas

Nov 27, 2021
A rusty dynamically typed scripting language
A rusty dynamically typed scripting language

dyon A rusty dynamically typed scripting language Tutorial Dyon-Interactive Dyon Snippets /r/dyon Dyon script files end with .dyon. To run Dyon script

May 24, 2022
A scripting language that allows complex key remapping on Linux.

Map2 A scripting language that allows complex key remapping on Linux, written in Rust. All of the functionality related to interacting with graphical

Apr 28, 2022
Simple, extendable and embeddable scripting language.

duckscript duckscript SDK CLI Simple, extendable and embeddable scripting language. Overview Language Goals Installation Homebrew Binary Release Ducks

May 22, 2022
A shell scripting language

Slash The system level language for getting the job done. Detailed documentation is available on the Slash site Motivation Bash is an awesome shell, b

May 18, 2022
An interactive scripting language where you can read and modify code comments as if they were regular strings
An interactive scripting language where you can read and modify code comments as if they were regular strings

An interactive scripting language where you can read and modify code comments as if they were regular strings. Add and view text-based visualizations and debugging information inside your source code file.

Apr 28, 2022
Lagoon is a dynamic, weakly-typed and minimal scripting language. 🏞
Lagoon is a dynamic, weakly-typed and minimal scripting language. 🏞

Lagoon is a dynamic, weakly-typed and minimal scripting language. It draws inspiration from a handful of modern languages including JavaScript, Rust and PHP.

Mar 1, 2022
Scripting language focused on processing tabular data.
Scripting language focused on processing tabular data.

ogma Welcome to the ogma project! ogma is a scripting language focused on ergonomically and efficiently processing tabular data, with batteries includ

May 16, 2022
A small scripting language running on V8.

Rook A small scripting language running on V8. After spending a few hours playing with V8, it seems like the only way to make this work is by transpil

Oct 30, 2021
A forth-inspired, bytecode-compiled scripting language for Anachro Powerbus

Anachro Forth (core) Anachro Forth is a forth-inspired, bytecode-compiled scripting language for Anachro Powerbus platform. Use Case The intended use

Apr 28, 2022
A small embeddable scripting language

There is currently a tree-walking interpreter called bird in progress, but the plan is to have a bytecode vm in the end language_name is a small embed

Apr 28, 2022
Sol is a lightweight language primarily designed for scripting environments

☀️ Sol A dynamically-typed (for now) and procedural programming language. About Sol is a lightweight language primarily designed for scripting environ

Mar 31, 2022
whydia is a small embeddable scripting language

whydia is a small embeddable scripting language It's inspired by Javascript, Lox, Lua, Python, Rust and more Reference Overview Declaring variables us

Apr 15, 2022
A simple, human-friendly, embeddable scripting language

Mica Language reference · Rust API A simple, human-friendly scripting language, developed one feature at a time. Human-friendly syntax inspired by Rub

Apr 4, 2022
The GameLisp scripting language

GameLisp GameLisp is a scripting language for Rust game development. To get started, take a look at the homepage. Please note that GameLisp currently

May 14, 2022