jzignet
jzignet copied to clipboard
Zig library to connect Janet and Zig together
Jzignet
Zig is a general-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
Janet is a functional and imperative programming language and bytecode interpreter. It is a lisp-like language, but lists are replaced by other data structures (arrays, tables (hash table), struct (immutable hash table), tuples). The language also supports bridging to native code written in C, meta-programming with macros, and bytecode assembly.
Jzignet - Zig library to connect Janet and Zig together.
You can:
- Embed Janet programs into Zig
- Write Janet modules in Zig
- Write bindings in Zig for a C library to be used as a Janet module
Why use these bindings, besides obvious reasons such as connecting together two wonderful languages:
- You don't need to care about conversion between Zig and C. But you have full access to C internals if you need to.
- Plenty of tests which are great examples and guarantee minimal regressions when updating.
- Idiomatic Zig code - everything is typed, names are properly cased, operations on types use methods instead of prefixed global functions.
Currently supported versions:
- Zig 0.9.0
- Janet 1.23.0
Repository is available at sourcehut and at GitHub.
How to use
If you want to just start using it, jump to the examples. Copy them or look at the source code, it is heavily commented. Every example also has a readme file.
- Embed Janet into Zig
- Write Janet module in Zig
Write bindings in Zig for a C library to be used as a Janet module - this is very close to "Write Janet module in Zig" example, you just need to know how to wrap a C library in Zig, this repository is a perfect example for this.
Currently you can include jzignet as a git submodule. Janet is bundled as a single C source amalgamation file and is compiled directly into this library.
-
Include git submodule into your library, assuming further that
libpathis the directory where this library is installedgit submodule add https://git.sr.ht/~greenfork/jzignet libpath -
Include bundled Janet in
build.zigconst janet = b.addStaticLibrary("janet", null); janet.addCSourceFile("libpath/c/janet.c", &[_][]const u8{"-std=c99"}); janet.addIncludeDir("libpath/c"); janet.linkLibC(); -
Link the bundled Janet to your program in
build.zig. Here the example is for an executable, for library it is similar, see thebuild.zigfiles in the examplesexe.linkLibC(); exe.linkLibrary(janet); exe.addPackagePath("jzignet", "libpath/src/janet.zig"); exe.addIncludeDir("libpath/c");
Differences with C API
Naming
janet_prefix is mostly not present.- Every type is a Zig struct and corresponding functions are called as
methods, for example,
janet_table_get(table, key)becomestable.get(key). - All bindings have idiomatic Zig naming even when Janet uses different
ones, for example
arityandfixarityarearityandfixArityin Zig. - Functions like
janet_tableare available asTable.init, please consult the source code for that.
Semantics
-
Function return types return error sets as well as optional values where it makes sense to do so, for example,
table.getreturns?JanetandpcallreturnsSignal.Error!void. -
All types are wrapped into structs. Most of the types support this natively since they are structs in C too, others (Tuple, Struct, String, Keyword, Symbol) cannot be represented as structs directly and they are wrappers with a
ptrorslicefield containing the original value. -
All functions that have a type at the end, for example,
janet_get_number, instead use this signature:get(comptime T: type, ...). Currently these functions exist:get,opt,wrap,Janet.unwrap. -
When you need to supply a pointer to the array and a length in the C version, in Zig version you need to supply just a slice since it has both the pointer and the length, so it's one parameter instead of two. For example,
int janet_dobytes(JanetTable *env, const uint8_t *bytes, int32_t len, const char *sourcePath, Janet *out);becomes
pub fn doBytes(env: *Environment, bytes: []const u8, source_path: [:0]const u8) !Janet -
Abstracts are fully typed, no *void pointers to @ptrCast. Take a look at tests for examples with abstracts, they are generally reworked to make them easier to use.
-
All functions returning
Signalinstead returnvoidonOKand return error otherwise with the specified signal, signature isSignal.Error!void. -
doStringanddoBytesare aliases and return!Janetdirectly instead of accepting a reference for the return value. -
string,keyword,symbol,niltop-level functions are the only ones to create these types and they do what you want. -
Environmenttype introduced which is internally aTablebut allows conceptually different operations such as defining values and executing code.
Completeness
Bindings are not complete 100% but all the generally useful things are there. If you need any specific part of the API, feel free to contribute or just ask (and you shall receive).
Q'n'A
Q: What's with the name?
A: "janet".replace("a", "zig")
Q: I hate that name.
A: Yes, I know.
License
MIT, see LICENSE file.