habedi/element-0
A small embeddable Lisp for the Zig ecosystem λ
v0.1.0.tar.gz
Element 0 programming language is a new Lisp dialect inspired by Scheme and aims to be compliant with the R5RS standard to a good degree, but not limited to it.
This project provides an interpreter for the Element 0 language written in Zig. The interpreter is named Elz (pronounced "el-zee") and can be integrated into Zig applications as a scripting engine. In addition, Elz comes with a read-eval-print loop (REPL) for interactive development and testing, and it can easily be extended using Zig code using foreign function interface (FFI) or Element 0 code.
Having an embeddable scripting language is useful in a Zig project. For example, you can write the core parts of your application in Zig for performance. Then you can write features like plugins or configuration files in Element 0. This lets you change parts of your application without the need to recompile the entire project.
See the ROADMAP.md for the list of implemented and planned features.
IMPORTANT Element 0 is in early development, so bugs and breaking changes are expected. Please use the issues page to report bugs or request features.
Element 0 is implemented in Zig 0.14.1 and needs at least Zig 0.14.1 to build.
Clone the repository
git clone https://github.com/habedi/element-0.git
cd element-0
Build and run the REPL
zig build repl && ./zig-out/bin/elz-repl
Run an Element 0 script file
./zig-out/bin/elz-repl --file examples/elz/e13-hello-world.elz
You can add Elz to your project as a dependency and use it as a scripting engine.
Run the following command in the root directory of your project to add Elz as a dependency.
zig fetch --save=elz "https://github.com/habedi/element-0/archive/main.tar.gz"
This command downloads the latest development version of Elz.
It adds it to Zig's global cache and updates your project's build.zig.zon
file.
Next, modify your build.zig
file. This will make the Elz library available to your application as a module.
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "your-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// 1. Get the Elz dependency object from the builder.
const elz_dep = b.dependency("elz", .{});
// 2. Create a module for the Elz library.
const elz_module = elz_dep.module("elz");
// 3. Add the module to your executable so you can @import("elz").
exe.root_module.addImport("elz", elz_module);
// 4. Link system libraries required by Elz.
exe.linkSystemLibrary("c");
b.installArtifact(exe);
}
Finally, you can @import("elz")
and use the interpreter in your Zig application.
The example below shows how to evaluate a simple script. It also shows how to use the FFI to call a Zig function from Elz.
const std = @import("std");
const elz = @import("elz");
// Define a native Zig function you want to call from Elz.
fn zig_multiply(a: f64, b: f64) f64 {
return a * b;
}
pub fn main() !void {
// 1. Initialize the Elz interpreter.
var interpreter = try elz.Interpreter.init(.{});
const stdout = std.io.getStdOut().writer();
// --- Example 1: Evaluate a simple string of Elz code ---
std.debug.print("--- Evaluating simple Elz code ---\n", .{});
const source1 = "(* 10 5)";
var fuel1: u64 = 1000;
const result1 = try interpreter.evalString(source1, &fuel1);
try stdout.print("Result of {s} is: ", .{source1});
try elz.write(result1, stdout);
try stdout.print("\n\n", .{});
// --- Example 2: Expose a Zig function to Elz and call it ---
std.debug.print("--- Calling a Zig function from Elz ---\n", .{});
// 2. Register your Zig function with the interpreter.
// It will be available in Elz under the name "zig-mul".
try elz.env_setup.define_foreign_func(
interpreter.root_env,
"zig-mul",
zig_multiply,
);
// 3. Write and evaluate Elz code that calls your Zig function.
const source2 = "(zig-mul 7 6)";
var fuel2: u64 = 1000;
const result2 = try interpreter.evalString(source2, &fuel2);
try stdout.print("Result of {s} is: ", .{source2});
try elz.write(result2, stdout);
try stdout.print("\n", .{});
}
When you build and run this program, the output will be:
--- Evaluating simple Elz code ---
Result of (* 10 5) is: 50
--- Calling a Zig function from Elz ---
Result of (zig-mul 7 6) is: 42
You can find the full API documentation for the latest release of Elz here.
Alternatively, you can use the make docs
command to generate the API documentation for the current version of
Elz from the source code.
This will generate HTML documentation in the docs/api
directory, which you can serve locally with make serve-docs
and view in your web browser at http://localhost:8000.
See the std.elz file for the full list of available items (like functions, variables, etc.) in the standard library.
Check out the examples directory for various usage examples, including both Element 0 code and Zig FFI examples.
Contributions are always welcome! Please see CONTRIBUTING.md for details on how to make a contribution.
Element 0 is licensed under the Apache License, Version 2.0 (see LICENSE).