diff --git a/README.md b/README.md index ee98e6d..a8eb993 100644 --- a/README.md +++ b/README.md @@ -105,4 +105,8 @@ Comments can be added with `//`. When a comment character is reached, the interp - `neq arg1 arg2 ...` - Opposite of eq. - `not arg` - Reverses a boolean value. - `or arg1 arg2 ...` - Returns true if any of the args are true, false otherwise. -- `and arg1 arg2 ...` - Returns true if all of the args are true, false otherwise. \ No newline at end of file +- `and arg1 arg2 ...` - Returns true if all of the args are true, false otherwise. +- `< arg1 arg2` - returns whether the first value is less than the second. +- `<= arg1 arg2` - returns whether the first value is less than or equal to the second. +- `> arg1 arg2` - returns whether the first value is greater than the second. +- `>= arg1 arg2` - returns whether the first value is greater than or equal to the second. \ No newline at end of file diff --git a/examples/fibonacci.zisp b/examples/fibonacci.zisp index 9973a1a..7d80bc1 100644 --- a/examples/fibonacci.zisp +++ b/examples/fibonacci.zisp @@ -1,16 +1,14 @@ -// not doable yet with current std (var "cache" (createTable)) (def fibonacci (n) ( - (if (or (eq n 0) (eq n 1)) ( - (return n) + (if (<= n 1) ( + return n )) (if (has cache n) ( - (return (kget cache n)) + return (kget cache n) )) - (var "v" (- (fibonacci (- n 1)) (fibonacci (- n 2)))) - + (var "v" (+ (fibonacci (- n 1)) (fibonacci (- n 2)))) (put cache n v) (return v) diff --git a/examples/return.zisp b/examples/return.zisp new file mode 100644 index 0000000..665d769 --- /dev/null +++ b/examples/return.zisp @@ -0,0 +1,5 @@ +(def test () ( + return "hi" +)) + +(println (test)) \ No newline at end of file diff --git a/src/eval.zig b/src/eval.zig index 27026bc..95e83e4 100644 --- a/src/eval.zig +++ b/src/eval.zig @@ -4,11 +4,23 @@ const String = @import("string").String; const internal_std = @import("std/root.zig"); pub fn evaluate(allocator: std.mem.Allocator, ast: std.ArrayList(model.TokenTree), runtime: *Runtime) !?model.Atom { + if(runtime.env.has_returned()) { + return runtime.env.get_current_return(); + } + if (ast.items.len == 1) { return switch (ast.items[0]) { .constant => |constant| constant, .context => |context| evaluate(allocator, context, runtime), - .ident => |ident| runtime.run_function(allocator, ident.str(), &[_]*model.Atom{}), + .ident => |ident| { + if(std.mem.eql(u8, ident.str(), "return")) { + // no-value return + runtime.env.return_current(null); + return null; + } + + return runtime.run_function(allocator, ident.str(), &[_]*model.Atom{}); + }, .list_init => |items| { var contents = std.ArrayList(model.Atom).init(allocator); @@ -35,6 +47,37 @@ pub fn evaluate(allocator: std.mem.Allocator, ast: std.ArrayList(model.TokenTree return switch (ast.items[0]) { .ident => |ident| { + if(std.mem.eql(u8, ident.str(), "return")) { + if(ast.items.len == 2) { + return switch(ast.items[1]) { + .constant => |constant| { + runtime.env.return_current(constant); + return constant; + }, + .context => |context| { + const atom = try evaluate(allocator, context, runtime); + runtime.env.return_current(atom); + return atom; + }, + .ident => |ident2| { + const atom = (try runtime.env.fetch_variable(ident2.str())).?.*; + runtime.env.return_current(atom); + return atom; + }, + .list_init => { + var list = std.ArrayList(model.TokenTree).init(allocator); + defer list.deinit(); + try list.append(ast.items[1]); + + const list2 = try evaluate(allocator, list, runtime); + runtime.env.return_current(list2); + + return list2; + }, + }; + } + } + if (std.mem.eql(u8, ident.str(), "if")) { // if cond (true) (false) const cond = switch (ast.items[1]) { @@ -115,6 +158,7 @@ pub fn evaluate(allocator: std.mem.Allocator, ast: std.ArrayList(model.TokenTree // TODO evaluation could be null, need an actual error handling .context => |context| { + // runtime.env.print_stacktrace(); var result = (try evaluate(allocator, context, runtime)).?; try args.append(&result); @@ -144,6 +188,10 @@ pub fn evaluate(allocator: std.mem.Allocator, ast: std.ArrayList(model.TokenTree _ = try evaluate(allocator, context, runtime); for (ast.items[1..]) |tree| { + if(runtime.env.has_returned()) { + return runtime.env.get_current_return(); + } + switch (tree) { .context => |context2| _ = try evaluate(allocator, context2, runtime), .constant => return error.CannotCallValue, @@ -156,7 +204,7 @@ pub fn evaluate(allocator: std.mem.Allocator, ast: std.ArrayList(model.TokenTree } } - return null; + return runtime.env.get_current_return(); }, .list_init => return error.CannotCallValue, }; @@ -208,6 +256,7 @@ pub const Runtime = struct { const val = try self.env.fetch_variable(ident); if (val == null) { + std.debug.print("Identifier: {s}\n", .{ident}); return error.IdentDoesNotExist; } @@ -259,7 +308,7 @@ pub const Environment = struct { } pub fn enter_new_frame(self: *Self, functionName: []const u8, allocator: std.mem.Allocator) !void { - try self.stack.append(StackFrame{ .name = functionName, .locals = std.StringHashMap(model.Atom).init(allocator) }); + try self.stack.append(StackFrame{ .name = functionName, .locals = std.StringHashMap(model.Atom).init(allocator), .return_value = null, .has_returned = false }); } pub fn exit_frame(self: *Self) void { @@ -314,6 +363,20 @@ pub const Environment = struct { }, }); } + + pub fn return_current(self: *Self, val: ?model.Atom) void { + var frame = self.current_stack_frame(); + frame.return_value = val; + frame.has_returned = true; + } + + pub fn get_current_return(self: *Self) ?model.Atom { + return self.current_stack_frame().return_value; + } + + pub fn has_returned(self: *Self) bool { + return self.current_stack_frame().has_returned; + } }; pub const StackFrame = struct { @@ -321,6 +384,8 @@ pub const StackFrame = struct { locals: std.StringHashMap(model.Atom), name: []const u8, + return_value: ?model.Atom, + has_returned: bool, pub fn deinit(self: *Self) void { //self.name.deinit(); diff --git a/src/std/conditional.zig b/src/std/conditional.zig index 97a74e5..179c340 100644 --- a/src/std/conditional.zig +++ b/src/std/conditional.zig @@ -11,6 +11,10 @@ pub fn setup(env: *eval.Environment) !void { try env.register_internal_function("not", internal_not); try env.register_internal_function("or", internal_or); try env.register_internal_function("and", internal_and); + try env.register_internal_function("<", internal_lessthan); + try env.register_internal_function("<=", internal_lessthaneq); + try env.register_internal_function(">", internal_greaterthan); + try env.register_internal_function(">=", internal_greaterthaneq); } pub fn internal_eq(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom { @@ -77,3 +81,59 @@ pub fn internal_and(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom return model.Atom{ .bool = true }; } + +pub fn internal_lessthan(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom { + if(args.len != 2) { + return error.InvalidArgCount; + } + + return switch(args[0].*) { + .int => |a| switch(args[1].*) { + .int => |b| model.Atom { .bool = a < b }, + else => error.TypeMismatch, + }, + else => error.TypeMismatch, + }; +} + +pub fn internal_lessthaneq(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom { + if(args.len != 2) { + return error.InvalidArgCount; + } + + return switch(args[0].*) { + .int => |a| switch(args[1].*) { + .int => |b| model.Atom { .bool = a <= b }, + else => error.TypeMismatch, + }, + else => error.TypeMismatch, + }; +} + +pub fn internal_greaterthan(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom { + if(args.len != 2) { + return error.InvalidArgCount; + } + + return switch (args[0].*) { + .int => |a| switch (args[1].*) { + .int => |b| model.Atom { .bool = a > b }, + else => error.TypeMismatch, + }, + else => error.TypeMismatch, + }; +} + +pub fn internal_greaterthaneq(_: Allocator, args: []*model.Atom, _: *Runtime) !?model.Atom { + if(args.len != 2) { + return error.InvalidArgCount; + } + + return switch (args[0].*) { + .int => |a| switch(args[1].*) { + .int => |b| model.Atom { .bool = a >= b }, + else => error.TypeMismatch, + }, + else => error.TypeMismatch, + }; +} \ No newline at end of file