Skip to content

Commit

Permalink
Support @host vars.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Sep 24, 2023
1 parent fe8ae79 commit 83328b1
Show file tree
Hide file tree
Showing 26 changed files with 721 additions and 350 deletions.
26 changes: 14 additions & 12 deletions .github/workflows/latest-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -238,22 +238,22 @@ jobs:
- name: Move. (windows lib)
if: env.BUILD_TARGET == 'x86_64-windows-gnu' && env.BUILD_CMD == 'lib'
run: |
mv zig-out/lib/cyber.dll cyber.dll
mv zig-out/lib/cyber.lib cyber-windows-x64.lib
- name: Move. (linux lib)
if: env.BUILD_TARGET == 'x86_64-linux-gnu' && env.BUILD_CMD == 'lib'
run: |
mv zig-out/lib/libcyber.so libcyber.so
mv zig-out/lib/libcyber.a libcyber-linux-x64.a
- name: Move. (macos lib)
if: env.BUILD_TARGET == 'x86_64-macos-none' && env.BUILD_CMD == 'lib'
run: |
mv zig-out/lib/libcyber.dylib libcyber.dylib
mv zig-out/lib/libcyber.a libcyber-macos-x64.a
- name: Move. (macos lib)
if: env.BUILD_TARGET == 'aarch64-macos-none' && env.BUILD_CMD == 'lib'
run: |
mv zig-out/lib/libcyber.dylib libcyber-arm64.dylib
mv zig-out/lib/libcyber.a libcyber-macos-arm64.a
- name: Move. (wasm)
if: env.BUILD_TARGET == 'wasm32-freestanding'
Expand Down Expand Up @@ -293,6 +293,7 @@ jobs:
with:
name: bin

# Manual dispatch.
- name: Github Latest Release.
uses: marvinpinto/action-automatic-releases@latest
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
Expand All @@ -307,12 +308,13 @@ jobs:
cyber-macos-x64.tar.gz
cyber-macos-arm64.tar.gz
cyber-windows-x64.zip
cyber.dll
libcyber.so
libcyber.dylib
libcyber-arm64.dylib
cyber-windows-x64.lib
libcyber-linux-x64.a
libcyber-macos-x64.a
libcyber-macos-arm64.a
cyber.wasm
# Auto dispatch.
- name: Github Release.
uses: softprops/action-gh-release@v1
# Releases for tags only.
Expand All @@ -324,8 +326,8 @@ jobs:
cyber-macos-x64.tar.gz
cyber-macos-arm64.tar.gz
cyber-windows-x64.zip
cyber.dll
libcyber.so
libcyber.dylib
libcyber-arm64.dylib
cyber-windows-x64.lib
libcyber-linux-x64.a
libcyber-macos-x64.a
libcyber-macos-arm64.a
cyber.wasm
29 changes: 23 additions & 6 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var vmEngine: config.Engine = undefined;
var testFilter: ?[]const u8 = undefined;
var trace: bool = undefined;
var optFFI: ?bool = undefined;
var optStatic: ?bool = undefined;

var stdx: *std.build.Module = undefined;
var tcc: *std.build.Module = undefined;
Expand All @@ -27,6 +28,7 @@ pub fn build(b: *std.build.Builder) !void {
vmEngine = b.option(config.Engine, "vm", "Build with `zig` or `c` VM.") orelse .c;
optMalloc = b.option(config.Allocator, "malloc", "Override default allocator: `malloc`, `mimalloc`, `zig`");
optFFI = b.option(bool, "ffi", "Override default FFI: true, false");
optStatic = b.option(bool, "static", "Override default lib build type: true=static, false=dynamic");
trace = b.option(bool, "trace", "Enable tracing features.") orelse (optimize == .Debug);

stdx = b.createModule(.{
Expand Down Expand Up @@ -86,12 +88,22 @@ pub fn build(b: *std.build.Builder) !void {
opts.malloc = .malloc;
opts.applyOverrides();

const lib = b.addSharedLibrary(.{
.name = "cyber",
.root_source_file = .{ .path = "src/lib.zig" },
.target = target,
.optimize = optimize,
});
var lib: *std.build.Step.Compile = undefined;
if (opts.static) {
lib = b.addStaticLibrary(.{
.name = "cyber",
.root_source_file = .{ .path = "src/lib.zig" },
.target = target,
.optimize = optimize,
});
} else {
lib = b.addSharedLibrary(.{
.name = "cyber",
.root_source_file = .{ .path = "src/lib.zig" },
.target = target,
.optimize = optimize,
});
}
if (lib.optimize != .Debug) {
lib.strip = true;
}
Expand Down Expand Up @@ -294,6 +306,7 @@ pub const Options = struct {
target: std.zig.CrossTarget,
optimize: std.builtin.OptimizeMode,
malloc: config.Allocator,
static: bool,
gc: bool,
ffi: bool,

Expand All @@ -304,6 +317,9 @@ pub const Options = struct {
if (optFFI) |ffi| {
self.ffi = ffi;
}
if (optStatic) |static| {
self.static = static;
}
}
};

Expand All @@ -329,6 +345,7 @@ fn getDefaultOptions(target: std.zig.CrossTarget, optimize: std.builtin.Optimize
.optimize = optimize,
.gc = true,
.malloc = malloc,
.static = !target.getCpuArch().isWasm(),
.ffi = !target.getCpuArch().isWasm(),
};
}
Expand Down
7 changes: 6 additions & 1 deletion docs/hugo/content/docs/toc/control-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ while iter.next() some entry:
print entry.name
```

`for` loops can iterate over a range that starts at a `int` (inclusive) to a target `int` (exclusive). When the range operator `..` is replaced with `..=`, the target `int` is inclusive. The range can be given a custom step.
`for` loops can iterate over a range that starts at a `int` (inclusive) to a target `int` (exclusive).
The range can be given a custom step.
```cy
for 0..100 each i:
print i -- 0, 1, 2, ... , 99
Expand All @@ -63,6 +64,10 @@ for 0..100, 10 each i:
for 100..0, 1 each i:
print i -- 100, 99, 98, ... , 1

```
When the range operator `..` is replaced with `..=`, the target `int` is inclusive.
> _Planned Feature_
```cy
for 100..=0, 1 each i:
print i -- 100, 99, 98, ... , 0
```
Expand Down
4 changes: 3 additions & 1 deletion docs/hugo/content/docs/toc/data-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ var b = float(a)
## Strings.
The `string` type represents a sequence of UTF-8 codepoints, also known as `runes`. Each rune is stored internally as 1-4 bytes and can be represented as an `int`. Under the hood, Cyber implements 6 different internal string types to optimize string operations, but the user just sees them as one type and doesn't need to care about this detail under normal usage.

Strings are **immutable**, so operations that do string manipulation return a new string. By default, small strings are interned to reduce memory footprint. To mutate an existing string, use the [StringBuffer](#string-buffer).
Strings are **immutable**, so operations that do string manipulation return a new string. By default, small strings are interned to reduce memory footprint.

To mutate an existing string, use the [StringBuffer](#string-buffer).
> _Planned Feature_
A string is always UTF-8 validated. [rawstrings](#rawstring) outperform strings but you'll have to validate them and take care of indexing yourself.
Expand Down
2 changes: 2 additions & 0 deletions docs/hugo/content/docs/toc/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ You can call functions with named parameters.
var d = dist(x0: 10, x1: 20, y0: 30, y1: 40)
```
### Shorthand syntax.
The shorthand method for calling functions omits parentheses and commas. This only works for functions that accept parameters:
> _Incomplete: Only the most trivial cases work with the shorthand method. The case with operators being separated by spaces might not end up being implemented._
```cy
Expand Down Expand Up @@ -177,6 +178,7 @@ var a = myFunc 'hello' (1 + 2 * 3)
a = myFunc 'hello' (otherFunc 1+2 'world')
```

### Call block syntax.
The call expression block continues to add arguments from the block's body. If arguments are omitted from the initial call expression they can be added inside using the `..` syntax. Arguments mapped to named parameters have a key value syntax separated by a `:`. All other arguments are added into a list and passed as the last argument.
> _Planned Feature_
```cy
Expand Down
3 changes: 1 addition & 2 deletions docs/hugo/content/docs/toc/modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ print id
| `parseCyon(src any) any` | Parses a CYON string into a value. |
| `performGC() map` | Runs the garbage collector once to detect reference cycles and abandoned objects. Returns the statistics of the run in a map value. |
| `pointer(val any) pointer` | Converts a `int` to a `pointer` value, or casts to a `pointer`. This is usually used with FFI. |
| `print(s string) none` | Prints a value as a string to stdout. The new line is also printed. |
| `prints(s string) none` | Prints a value as a string to stdout. |
| `print(s string) none` | Prints a value. The host determines how it is printed. |
| `rawstring(str string) rawstring` | Converts a string to a `rawstring`. |
| `runestr(val int) string` | Converts a rune to a string. |
| `string(val any) string` | Converts a value to a string. |
Expand Down
131 changes: 82 additions & 49 deletions examples/c-embedded/bind_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,103 @@
#include <stdio.h>
#include "cyber.h"

// This example shows how to setup a module loader for custom modules.
// Declarations from a module's source code invokes callbacks to bind with the host's functions and types.
// To see how to inject symbols programmatically, see `inject_module.c`.

// Compile this program with a C compiler. `zig cc` is used here as an example.
// zig cc bind_module.c -I src -lcyber -L <Path to cyber.dll/so/dylib> -o main.exe
// zig cc bind_module.c -I ../../src/include ../../zig-out/lib/libcyber.a -o main.exe

bool loadCore(CyVM* vm, CyModule* mod);
bool loadMyMod(CyVM* vm, CyModule* mod);
CyValue print(CyVM* vm, CyValue* args, uint8_t nargs);
CyValue add(CyVM* vm, CyValue* args, uint8_t nargs);
// Convenience macros to deal with Cyber string slices.
#define STR(s) ((CsStr){ s, strlen(s) })
#define PRINTS(s) (printf("%.*s\n", (int)s.len, s.buf))

int main() {
CyVM* vm = cyVmCreate();
CsValue add(CsVM* vm, const CsValue* args, uint8_t nargs) {
double res = csAsFloat(args[0]) + csAsFloat(args[1]);
return csFloat(res);
}
struct { char* n; CsHostFuncFn fn; } funcs[] = {
{"add", add},
};

// Add syms to the default module (Loaded into each script's namespace).
cyVmAddModuleLoader(vm, cstr("core"), loadCore);
CsHostFuncFn funcLoader(CsVM* vm, CsHostFuncInfo funcInfo) {
// Check that the name matches before setting the function pointer.
if (strncmp(funcs[funcInfo.idx].n, funcInfo.name.buf, funcInfo.name.len) == 0) {
return funcs[funcInfo.idx].fn;
} else {
return NULL;
}
}

// Add syms to a custom builtin named "mymod".
cyVmAddModuleLoader(vm, cstr("mymod"), loadMyMod);
// C has limited static initializers (and objects need a vm instance) so initialize them in `main`.
typedef struct { char* n; CsValue v; } NameValue;
NameValue vars[2];

CStr src = cstr(
"import mod 'mymod'\n"
"\n"
"a = 2\n"
"print add(a, 100)\n"
"print mod.add(a, a)\n"
"print mod.hello"
);
CyValue val;
int res = cyVmEval(vm, src, &val);
if (res == CY_Success) {
printf("Success!\n");
cyVmRelease(vm, val);
bool varLoader(CsVM* vm, CsHostVarInfo varInfo, CsValue* out) {
// Check that the name matches before setting the value.
if (strncmp(vars[varInfo.idx].n, varInfo.name.buf, varInfo.name.len) == 0) {
// Objects are consumed by the module.
*out = vars[varInfo.idx].v;
return true;
} else {
CStr err = cyVmGetLastErrorReport(vm);
printf("%s\n", err.charz);
return NULL;
}
cyVmDestroy(vm);
return 0;
}

bool loadMyMod(CyVM* vm, CyModule* mod) {
cyVmSetModuleFunc(vm, mod, cstr("add"), 2, add);
bool modLoader(CsVM* vm, CsStr spec, CsModuleLoaderResult* out) {
if (strncmp("my_engine", spec.buf, spec.len) == 0) {
out->src = STR(
"@host func add(a float, b float) float\n"
"@host var MyConstant\n"
"@host var MyList\n"
);
out->srcIsStatic = true;
out->funcLoader = funcLoader;
out->varLoader = varLoader;
return true;
} else {
// Fallback to the default module loader to load `builtins`.
return csDefaultModuleLoader(vm, spec, out);
}
}

CyValue str = cyValueGetOrAllocStringInfer(vm, cstr("hello world"));
cyVmSetModuleVar(vm, mod, cstr("hello"), str);
return true;
void print(CsVM* vm, CsStr str) {
printf("MY_VM: %.*s\n", (int)str.len, str.buf);
}

bool loadCore(CyVM* vm, CyModule* mod) {
cyVmSetModuleFunc(vm, mod, cstr("add"), 2, add);
int main() {
// csVerbose = true;
CsVM* vm = csCreate();
csSetModuleLoader(vm, modLoader);
csSetPrint(vm, print);

// Override core.print.
cyVmSetModuleFunc(vm, mod, cstr("print"), 1, print);
// Initialize var array for loader.
vars[0] = (NameValue){"MyConstant", csFloat(1.23)};
vars[1] = (NameValue){"MyList", csNewList(vm)};

return true;
}
CsStr main = STR(
"import mod 'my_engine'\n"
"\n"
"var a = 1.0\n"
"print mod.add(a, 2)\n"
"print(-mod.MyConstant)\n"
"mod.MyList.append(3)\n"
"print mod.MyList.len()\n"
);
CsValue resv;
CsResultCode res = csEval(vm, main, &resv);
if (res == CS_SUCCESS) {
printf("Success!\n");
} else {
CsStr err = csAllocLastErrorReport(vm);
PRINTS(err);
csFreeStr(vm, err);
}

CyValue add(CyVM* vm, CyValue* args, uint8_t nargs) {
double left = cyValueAsNumber(args[0]);
double right = cyValueAsNumber(args[1]);
return cyValueNumber(left + right);
}
// Check that all references were accounted for. (Should be 0)
csDeinit(vm);
printf("Global RC %zu\n", csGetGlobalRC(vm));
csDestroy(vm);

CyValue print(CyVM* vm, CyValue* args, uint8_t nargs) {
CStr str = cyValueToTempRawString(vm, args[0]);
printf("Overrided print: %s\n", str.charz);
cyVmRelease(vm, args[0]);
return cyValueNone();
return 0;
}
4 changes: 0 additions & 4 deletions src/api.zig
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,6 @@ pub const UserVM = struct {
return cy.arc.getGlobalRC(@ptrCast(self));
}

pub inline fn checkMemory(self: *UserVM) !bool {
return cy.arc.checkMemory(self.internal());
}

pub inline fn validate(self: *UserVM, srcUri: []const u8, src: []const u8) !cy.ValidateResult {
return self.internal().validate(srcUri, src, .{ .enableFileModules = true });
}
Expand Down
1 change: 0 additions & 1 deletion src/builtins/builtins.cy
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
@host func parseCyon(src any) any
@host func performGC() Map
@host func print(str any) none
@host func prints(str any) none
@host func runestr(val int) string
@host func toCyon(val any) string
@host func typeid(val any) int
Expand Down
8 changes: 0 additions & 8 deletions src/builtins/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const funcs = [_]NameHostFunc{
.{"parseCyon", parseCyon},
.{"performGC", performGC},
.{"print", print},
.{"prints", prints},
.{"runestr", runestr},
.{"toCyon", toCyon},
.{"typeid", typeid},
Expand Down Expand Up @@ -432,13 +431,6 @@ pub fn performGC(vm: *cy.UserVM, _: [*]const Value, _: u8) linksection(cy.StdSec
}

pub fn print(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value {
const str = vm.valueToTempRawString(args[0]);
vm.internal().print(vm, cy.Str.initSlice(str));
vm.internal().print(vm, cy.Str.initSlice("\n"));
return Value.None;
}

pub fn prints(vm: *cy.UserVM, args: [*]const Value, _: u8) linksection(cy.StdSection) Value {
const str = vm.valueToTempRawString(args[0]);
vm.internal().print(vm, cy.Str.initSlice(str));
return Value.None;
Expand Down
Loading

0 comments on commit 83328b1

Please sign in to comment.