Crate cxc

source ·
Expand description

cxc

cxc is a high-performance scripting language designed for speed: both of writing code, and of running code. It is built to be used as a Rust-hosted scripting language and as such, it has full interoperability with Rust. cxc is about playing with software at a low level–getting your hands dirty without gloves, just because it’s fun. Like Rust, cxc compiles down to an MIR, which can then be compiled or interpreted by a number of backends, including LLVM and Cranelift. Because of this, cxc has near-Rust performance, but it can be compiled and modified at runtime.

Installation

Put the following in your Cargo.toml:

[dependencies]
cxc = "0.3"

The default features of the crate use cranelift as a compiler backend. Alternatively, you can activate the “backend-llvm” feature, which uses LLVM, but it does require that you have LLVM installed, and the subdirectories llvm/includes and llvm/bin both in your path. Both backends have full feature parity. The Cranelift backend has faster compile times, and is more portable, but the emitted code is slower. The LLVM backend is less portable because it requires that users have LLVM installed, but the emitted code is faster.

You can access the LLVM backend by putting this in your Cargo.toml:

[dependencies]
cxc = {
    version = "0.3",
    default-features = false, 
    features = ["backend-llvm", "ffi-assertions"]
}

Example

prime.cxc

# function that takes a 32 bit integer and returns a boolean
is_prime(num: i32); bool {
    divider := 2 # declare divider (type is inferred as i32)

    @ divider < num { # while divider is less than num
        ? num % divider == 0 { # if num is divisible by divider
            ; false # number is not prime, so return false
        }

        divider = divider + 1 # increment divider
    }

    ; true # num is not divisible by any numbers, so return true
}

main.rs

fn main() {
    let mut unit = cxc::Unit::new();

    let prime_code: String = 
        include_str!("../README.md").lines().skip(27).take(14).collect::<Vec<_>>().join("\n");
    unit.push_script(&*prime_code).unwrap();

    let is_prime = unit.get_fn("is_prime").unwrap().downcast::<(i32,), bool>();

    assert_eq!(unsafe { is_prime(29) }, true);
}

⚠️ WARNING ⚠️ ( USE AT YOUR OWN RISK )

  • The compiler is very young, and there are still many bugs. (If you are using the language, please feel free to throw any issue reports my way! Feedback of any kind is greatly appreciated.)
  • cxc is a low-level language, so it does a lot of low-level and unsafe operations at runtime. this means that the communication between cxc and Rust code is unsafe and unstable.
  • The “ffi-assertions” feature is designed to catch isses when you improperly match up Rust and cxc types and functions, but it isn’t a catch-all. The cxc_derive macro helps with this, but it’s easy to mess up.
  • Minimum Supported Rust Version (MSRV) is latest nightly. The crate uses unstable features, and FFI bugs will occur if you use any other version.
  • There is no documentation, because the langauge is still changing. Look in the tests folder for some examples if you want to see some.

If any of these are deal breakers for you, you can try a different Rust scripting solution.

Modules

Structs

  • An array data type.
  • A boolean data type.
  • The individual field of a StructType.
  • A function data type. This is used both for the value of a first class function, and to represent the specified argument and return types for a declared and compiled function.
  • An integer data type, wrapping [IntSize].
  • A reference data type.
  • A struct data type, including the Repr.
  • One of the most important structs in the compiler, Type holds a data type.
  • One of the most important structs in the compiler, Unit is a God object that contains everything. Most of the important interactions with Cxc as a scripting language are done through the Unit, including, but not limited to Unit::push_script, which adds code to the Cxc module.
  • A dynamic representation of a value in cxc. Contains a Type for its data type and a Box<u8> for its data.

Enums

Traits

Type Aliases

Attribute Macros

Derive Macros