Skip to content

Commit

Permalink
Add cy.eval.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed Apr 15, 2024
1 parent 58906d3 commit 2fbe57c
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 5 deletions.
9 changes: 8 additions & 1 deletion docs/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2975,7 +2975,14 @@ Builtin types are used internally by the compiler to define it's own primitive t
>Evaluates to the module's URI as a string. See [Module URI](#module-uri).
## Runtime execution.
> _Planned Feature_
`cy.eval` evaluates source code in an isolated VM.
If the last statement is an expression, a primitive or String can be returned to the caller:
```cy
use cy

var res = cy.eval('1 + 2')
print res --> 3
```
# libcyber.
Expand Down
2 changes: 2 additions & 0 deletions src/builtins/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ pub const Symbol = enum {
bytes,

AssertError,
EvalError,
FileNotFound,
MissingSymbol,
EndOfStream,
OutOfBounds,
InvalidResult,
InvalidArgument,
InvalidSignature,
InvalidRune,
Expand Down
2 changes: 2 additions & 0 deletions src/builtins/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,9 @@ pub fn prepThrowZError2(ctx: cy.Context, err: anyerror, optTrace: ?*std.builtin.
fn errorSymbol(err: anyerror) Symbol {
switch (err) {
error.AssertError => return .AssertError,
error.EvalError => return .EvalError,
error.Unicode => return .Unicode,
error.InvalidResult => return .InvalidResult,
error.InvalidArgument => return .InvalidArgument,
error.InvalidEnumTag => return .InvalidArgument,
error.FileNotFound => return .FileNotFound,
Expand Down
4 changes: 4 additions & 0 deletions src/builtins/cy.cy
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
--| Evaluates source code in an isolated VM.
--| If the last statement is an expression, a primitive or a String can be returned.
#host func eval(src String) any

--| Parses Cyber source string into a structured map object.
--| Currently, only metadata about static declarations is made available but this will be extended to include an AST.
#host func parse(src String) Map
Expand Down
58 changes: 57 additions & 1 deletion src/builtins/cy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const zErrFunc = cy.builtins.zErrFunc;

const NameFunc = struct { []const u8, cy.ZHostFuncFn };
const funcs = [_]NameFunc{
.{"eval", zErrFunc(eval)},
.{"parse", zErrFunc(parse)},
.{"parseCyon", zErrFunc(parseCyon)},
.{"repl", zErrFunc(repl)},
Expand Down Expand Up @@ -305,7 +306,7 @@ fn fromCyonValue(vm: *cy.VM, val: cy.DecodeValueIR) !cy.Value {
while (iter.next()) |entry| {
const child = try fromCyonValue(vm, dmap.getValue(entry.key_ptr.*));
const key = try vm.allocString(entry.key_ptr.*);
try map.map.set(vm, key, child);
try map.map.setConsume(vm, key, child);
}
return mapVal;
},
Expand Down Expand Up @@ -549,6 +550,61 @@ pub fn repl2(vm: *cy.VM, config: c.EvalConfig, read_line: IReplReadLine) !void {
}
}

fn eval(vm: *cy.VM, args: [*]const cy.Value, _: u8) anyerror!cy.Value {
// Create an isolated VM.
const ivm: *cy.VM = @ptrCast(@alignCast(c.create()));
defer c.destroy(@ptrCast(ivm));

// Use the same printers as the parent VM.
c.setPrinter(@ptrCast(ivm), c.getPrinter(@ptrCast(vm)));
c.setErrorPrinter(@ptrCast(ivm), c.getErrorPrinter(@ptrCast(vm)));

var config = c.defaultEvalConfig();
config.single_run = false;
config.file_modules = false;
config.reload = false;
config.backend = c.BackendVM;
config.spawn_exe = false;

const src = args[0].asString();
var val: cy.Value = undefined;
const res = c.evalExt(@ptrCast(ivm), c.toStr("eval"), c.toStr(src), config, @ptrCast(&val));
if (res != c.Success) {
switch (res) {
c.ErrorCompile => {
const report = c.newErrorReportSummary(@ptrCast(ivm));
defer c.freeStr(@ptrCast(ivm), report);
rt.err(vm, c.fromStr(report));
},
c.ErrorPanic => {
const report = c.newPanicSummary(@ptrCast(ivm));
defer c.freeStr(@ptrCast(ivm), report);
rt.err(vm, c.fromStr(report));
},
else => {
rt.err(vm, "unknown error\n");
},
}
return error.EvalError;
}

switch (val.getTypeId()) {
bt.Boolean,
bt.Integer,
bt.Float => {
return val;
},
bt.String => {
defer ivm.release(val);
return vm.allocString(val.asString());
},
else => {
defer ivm.release(val);
return error.InvalidResult;
}
}
}

const VmReadLine = struct {
vm: *cy.VM,
read_line: cy.Value,
Expand Down
1 change: 1 addition & 0 deletions test/behavior_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ if (!aot) {
run.case2(Config.initFileModules("./test/modules/import_sym_alias.cy"), "modules/import_sym_alias.cy");
}
run.case("modules/core.cy");
run.case("modules/cy.cy");
run.case("modules/math.cy");
run.case("modules/test_eq_panic.cy");
run.case("modules/test.cy");
Expand Down
18 changes: 17 additions & 1 deletion test/modules/cy.cy
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
use t 'test'
use cy

-- eval()
let res = cy.eval('1')
t.eq(res, 1)
res = cy.eval('1 + 2')
t.eq(res, 3)
res = cy.eval('''
func mul(a int, b int) int:
return a * b
mul(10, 5)''')
t.eq(res, 50)
res = cy.eval('"hello $(123)"')
t.eq(res, 'hello 123')
t.throws(() => cy.eval('a'), error.EvalError)

-- parse()
res = cy.parse('var .foo = 123')
t.eq(res['decls'][0]['type'], 'variable')
Expand Down Expand Up @@ -73,4 +87,6 @@ t.eq(cyon, '{}')
cyon = cy.toCyon({ a: 123 })
t.eq(cyon, '''{
a: 123,
}''')
}''')

--cytest: pass
5 changes: 3 additions & 2 deletions test/setup.zig
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,10 @@ pub const VMrunner = struct {
}

if (config.checkGlobalRc) {
if (c.getGlobalRC(vm) != 0) {
const grc = c.getGlobalRC(vm);
if (grc != 0) {
c.traceDumpLiveObjects(vm);
cy.panic("unreleased refcount");
cy.panicFmt("unreleased refcount {}", .{grc});
}
}
}
Expand Down

0 comments on commit 2fbe57c

Please sign in to comment.