From 530342c1053e2fb33406bacb574545722b389217 Mon Sep 17 00:00:00 2001 From: Luuk de Gram Date: Tue, 1 Sep 2020 15:07:34 +0200 Subject: [PATCH] Added examples that can be run with `zig build example -Dexample` --- .gitattributes | 1 + build.zig | 13 +++++++++- examples/arithmetic.luf | 9 +++++++ examples/enums.luf | 8 ++++++ examples/example_runner.zig | 50 +++++++++++++++++++++++++++++++++++++ examples/function.luf | 8 ++++++ examples/if.luf | 12 +++++++++ examples/lists.luf | 7 ++++++ examples/loops.luf | 18 +++++++++++++ src/luf.zig | 1 + src/value.zig | 41 ++++++++++++++++++++++++++++++ src/vm.zig | 4 +-- 12 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 .gitattributes create mode 100644 examples/arithmetic.luf create mode 100644 examples/enums.luf create mode 100644 examples/example_runner.zig create mode 100644 examples/function.luf create mode 100644 examples/if.luf create mode 100644 examples/lists.luf create mode 100644 examples/loops.luf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..49e8125 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.zig text=auto eol=lf \ No newline at end of file diff --git a/build.zig b/build.zig index 0aac955..d4e2ba7 100644 --- a/build.zig +++ b/build.zig @@ -5,7 +5,18 @@ pub fn build(b: *Builder) void { const lib = b.addStaticLibrary("luf", "src/luf.zig"); lib.setBuildMode(mode); lib.install(); - + + var examples = b.addExecutable("example", "examples/example_runner.zig"); + examples.setBuildMode(mode); + examples.addPackagePath("luf", "src/luf.zig"); + const example_runner = examples.run(); + + const example = b.option([]const u8, "example", "The example to run (without .luf suffix)"); + + examples.addBuildOption(?[]const u8, "example", example); + + const example_step = b.step("examples", "Runs an example using the -Dexample flag"); + example_step.dependOn(&example_runner.step); var main_tests = b.addTest("src/luf.zig"); main_tests.setBuildMode(mode); diff --git a/examples/arithmetic.luf b/examples/arithmetic.luf new file mode 100644 index 0000000..0d115b0 --- /dev/null +++ b/examples/arithmetic.luf @@ -0,0 +1,9 @@ +const x = 15 +const y = 20 + +mut z = x + y +z = z << 1 + + +//this allows example to print last value +z \ No newline at end of file diff --git a/examples/enums.luf b/examples/enums.luf new file mode 100644 index 0000000..229d6eb --- /dev/null +++ b/examples/enums.luf @@ -0,0 +1,8 @@ +const MyEnum = enum{ + first_enum, + second_enum, + third_enum +} + +//this allows example to print last value +MyEnum.third_enum \ No newline at end of file diff --git a/examples/example_runner.zig b/examples/example_runner.zig new file mode 100644 index 0000000..0a9f0e1 --- /dev/null +++ b/examples/example_runner.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const luf = @import("luf"); + +const example = @import("build_options").example; + +pub fn main() !void { + if (example == null) + return std.debug.print( + "No example was provided, use -Dexample=example_name to run an example", + .{}, + ); + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + + const allocator = &gpa.allocator; + + const example_path = try std.fs.path.join(allocator, &[_][]const u8{ + "examples", + example.?, + }); + defer allocator.free(example_path); + + const example_file = try std.mem.concat(allocator, u8, &[_][]const u8{ + example_path, + ".luf", + }); + defer allocator.free(example_file); + + const file = std.fs.cwd().openFile(example_file, .{}) catch |_| { + return std.debug.print("Example does not exist", .{}); + }; + defer file.close(); + + const size = try file.getEndPos(); + + const source = try file.readAllAlloc(allocator, size, size); + defer allocator.free(source); + + const writer = std.io.getStdErr().writer(); + var vm = luf.Vm.init(allocator); + defer vm.deinit(); + vm.compileAndRun(source) catch |err| { + try vm.errors.write(source, writer); + return err; + }; + + try vm.peek().print(writer); + try writer.writeAll("\n"); +} diff --git a/examples/function.luf b/examples/function.luf new file mode 100644 index 0000000..ae6c9e4 --- /dev/null +++ b/examples/function.luf @@ -0,0 +1,8 @@ +const sum = fn(a:int, b:int) int { + return a + b +} + +const x: int = sum(5, 6) + +//this allows example to print last value +x \ No newline at end of file diff --git a/examples/if.luf b/examples/if.luf new file mode 100644 index 0000000..ee2333b --- /dev/null +++ b/examples/if.luf @@ -0,0 +1,12 @@ +mut x = 5 + +if (x == 4) { + x = 2 +} else if (x == 3){ + x = 1 +} else { + x = 0 +} + +//this allows example to print last value +x \ No newline at end of file diff --git a/examples/lists.luf b/examples/lists.luf new file mode 100644 index 0000000..b8abd5e --- /dev/null +++ b/examples/lists.luf @@ -0,0 +1,7 @@ +const list = []int{1,2,3} +const map = []string:int{"hello":1, "world":2} + +const sum = map["hello"] + map["world"] + +//this allows example to print last value +sum \ No newline at end of file diff --git a/examples/loops.luf b/examples/loops.luf new file mode 100644 index 0000000..5d0d6f8 --- /dev/null +++ b/examples/loops.luf @@ -0,0 +1,18 @@ +const list = []int{1,2,3} +mut sum = 0 +for(list) |element, index| { + sum += element + index +} + +for(20..50) |i| { + sum += i +} + +mut i = 0 +while(i<10) { + sum += i + i+=1 +} + +//this allows example to print last value +sum \ No newline at end of file diff --git a/src/luf.zig b/src/luf.zig index 3abd3bf..599814a 100644 --- a/src/luf.zig +++ b/src/luf.zig @@ -1,5 +1,6 @@ pub const Lexer = @import("lexer.zig").Lexer; pub const Token = @import("token.zig").Token; +pub const Vm = @import("vm.zig").Vm; test "All tests" { //_ = @import("eval.zig"); diff --git a/src/value.zig b/src/value.zig index 4d4d693..53cfa02 100644 --- a/src/value.zig +++ b/src/value.zig @@ -194,6 +194,47 @@ pub const Value = union(Type) { pub const List = std.ArrayListUnmanaged(*Value); pub const Map = std.HashMapUnmanaged(*const Value, *Value, hash, eql, true); pub const NativeFn = fn (vm: *@import("vm.zig").Vm, args: []*Value) anyerror!*Value; + + /// Prints a `Value` to the given `writer` + pub fn print(self: *const Value, writer: anytype) @TypeOf(writer).Error!void { + switch (self.*) { + .integer => |int| try writer.print("{}", .{int}), + .boolean => |boolean| try writer.print("{}", .{boolean}), + .string => |string| try writer.writeAll(string), + .nil => |nil| try writer.writeAll("nil"), + .list => |list| { + try writer.writeAll("["); + for (list.items) |item, i| { + try item.print(writer); + if (i != list.items.len - 1) + try writer.writeAll(",\n"); + } + try writer.writeAll("]\n"); + }, + .map => |map| { + try writer.writeAll("{"); + for (map.entries.items) |item, i| { + try item.key.print(writer); + try writer.writeAll(":"); + try item.value.print(writer); + if (i != map.items().len - 1) + try writer.writeAll(",\n"); + } + try writer.writeAll("}\n"); + }, + .range => |range| try writer.print("{}..{}", .{ range.start, range.end }), + ._enum => |enm| { + try writer.writeAll("{"); + for (enm) |item, i| { + try writer.writeAll(item); + if (i != enm.len - 1) + try writer.writeAll(",\n"); + } + try writer.writeAll("}\n"); + }, + else => try writer.writeAll("void"), + } + } }; /// Scope maps identifiers to their names and can be used diff --git a/src/vm.zig b/src/vm.zig index 427b4e4..4951689 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -212,7 +212,7 @@ pub const Vm = struct { /// Returns the previously popped `Value` /// Note that this results in UB if the stack is empty. - fn peek(self: *Vm) *Value { + pub fn peek(self: *Vm) *Value { return self.stack[self.sp]; } @@ -706,7 +706,6 @@ pub const Vm = struct { const last_ip = self.ip; const last_sp = self.sp; - try self.run(code); // Get all constants exposed by the source file @@ -742,7 +741,6 @@ pub const Vm = struct { fn loadModule(self: *Vm) Error!void { const val = self.pop().?; const name = val.unwrapAs(.string) orelse return self.fail("Expected a string"); - const mod = self.imports.get(name) orelse blk: { const imp = self.importModule(name) catch return self.fail("Could not import module"); try self.imports.put(name, imp);