From 329fbee0df485b75204c433e51c51e38b91a3e87 Mon Sep 17 00:00:00 2001 From: Will Medrano Date: Sat, 31 Aug 2024 22:48:52 -0700 Subject: [PATCH] Implement shorter function syntax. --- site/fizz-language-reference.md | 11 ++++--- site/index.md | 2 +- src/MemoryManager.zig | 21 ++++++------ src/Vm.zig | 4 +-- src/builtins.zig | 24 +++++++------- src/golden_test.fizz | 15 ++++----- src/ir.zig | 57 +++++++++++++++++++++++++++------ src/val.zig | 11 +++++-- 8 files changed, 95 insertions(+), 50 deletions(-) diff --git a/site/fizz-language-reference.md b/site/fizz-language-reference.md index bfa4245..49b74f4 100644 --- a/site/fizz-language-reference.md +++ b/site/fizz-language-reference.md @@ -11,14 +11,15 @@ nav_order: 2 ### define -`(define )` +Values - `(define )` +Functions - `(define ( ) ...)` Define a value within the current module that can be referenced. ```lisp >> (define x 12) ->> (define square ->> (lambda (x) (* x x))) +>> (define (square x) +>> (* x x)) >> (square x) $1 = 144 ``` @@ -27,12 +28,12 @@ $1 = 144 `(lambda () )` -Lambda is used to define a function. +Define a function. ```lisp >> (define my-plus-func (lambda (a b) (+ a b))) >> my-plus-func -$1 = +$1 = >> (my-plus-func 2 2) $2 = 4 >> ((lambda (a b) (- a b)) 2 2) diff --git a/site/index.md b/site/index.md index 390d5d5..2af658b 100644 --- a/site/index.md +++ b/site/index.md @@ -69,7 +69,7 @@ It should be easy to get started writing Fizz. Fizz supports the following: > ```lisp > >> (define pi 3.14) > >> (* 2 pi) ;; 6.28 - > >> (define plus (lambda (a b) (+ a b))) + > >> (define (plus a b) (+ a b)) > >> (plus 2 2) ;; 4 > >> (if (< 1 2) "red" "blue") ;; "red" > ``` diff --git a/src/MemoryManager.zig b/src/MemoryManager.zig index f81e200..c3b2511 100644 --- a/src/MemoryManager.zig +++ b/src/MemoryManager.zig @@ -1,6 +1,7 @@ const MemoryManager = @This(); const Val = @import("val.zig").Val; const Symbol = Val.Symbol; +const StructVal = Val.StructVal; const ByteCode = @import("ByteCode.zig"); const Module = @import("Module.zig"); const StringInterner = @import("datastructures/string_interner.zig").StringInterner; @@ -19,14 +20,14 @@ symbols: StringInterner(Symbol) = .{}, /// A map from an owned string to its reachable color. strings: std.StringHashMapUnmanaged(Color) = .{}, /// A map from a struct pointer to its Color. -structs: std.AutoHashMapUnmanaged(*std.AutoHashMapUnmanaged(Symbol, Val), Color) = .{}, +structs: std.AutoHashMapUnmanaged(*StructVal, Color) = .{}, /// A map from an owned list pointer to its length and color. lists: std.AutoHashMapUnmanaged([*]Val, LenAndColor) = .{}, /// A map from a bytecode pointer to its color. bytecode: std.AutoHashMapUnmanaged(*ByteCode, Color) = .{}, keep_alive_strings: std.StringHashMapUnmanaged(usize) = .{}, -keep_alive_structs: std.AutoHashMapUnmanaged(*std.AutoHashMapUnmanaged(Symbol, Val), usize) = .{}, +keep_alive_structs: std.AutoHashMapUnmanaged(*StructVal, usize) = .{}, keep_alive_lists: std.AutoHashMapUnmanaged([*]Val, usize) = .{}, keep_alive_bytecode: std.AutoHashMapUnmanaged(*ByteCode, usize) = .{}, @@ -154,12 +155,12 @@ pub fn allocateByteCode(self: *MemoryManager, module: *Module) !*ByteCode { } /// Allocate a new empty struct. -pub fn allocateStruct(self: *MemoryManager) !*std.AutoHashMapUnmanaged(Symbol, Val) { - const m = try self.allocator.create(std.AutoHashMapUnmanaged(Symbol, Val)); - errdefer self.allocator.destroy(m); - m.* = .{}; - try self.structs.put(self.allocator, m, self.reachable_color.swap()); - return m; +pub fn allocateStruct(self: *MemoryManager) !*StructVal { + const new_struct = try self.allocator.create(StructVal); + errdefer self.allocator.destroy(new_struct); + new_struct.* = .{ .map = .{} }; + try self.structs.put(self.allocator, new_struct, self.reachable_color.swap()); + return new_struct; } /// Mark a single value as reachable. @@ -177,7 +178,7 @@ pub fn markVal(self: *MemoryManager, v: Val) !void { try self.structs.put(self.allocator, s, self.reachable_color); } // Mark any child values. - var iter = s.valueIterator(); + var iter = s.map.valueIterator(); while (iter.next()) |structVal| { try self.markVal(structVal.*); } @@ -239,7 +240,7 @@ pub fn sweep(self: *MemoryManager) !void { for (list_free_targets.items) |t| _ = self.lists.remove(t); _ = tmp_arena.reset(.retain_capacity); - var struct_free_targets = std.ArrayList(*std.AutoHashMapUnmanaged(Symbol, Val)).init(tmp_arena.allocator()); + var struct_free_targets = std.ArrayList(*StructVal).init(tmp_arena.allocator()); var structsIter = self.structs.iterator(); while (structsIter.next()) |entry| { if (entry.value_ptr.* != self.reachable_color) { diff --git a/src/Vm.zig b/src/Vm.zig index 56178a9..5b01fca 100644 --- a/src/Vm.zig +++ b/src/Vm.zig @@ -111,7 +111,7 @@ pub fn toZig(self: *Vm, T: type, alloc: std.mem.Allocator, val: Val) !T { } }, .Struct => |info| { - const map = switch (val) { + const struct_val = switch (val) { .structV => |m| m, else => return error.TypeError, }; @@ -128,7 +128,7 @@ pub fn toZig(self: *Vm, T: type, alloc: std.mem.Allocator, val: Val) !T { @memcpy(&fizz_field_name, field.name); makeKebabCase(&fizz_field_name); if (self.env.memory_manager.symbols.getId(&fizz_field_name)) |sym| { - const field_val = map.get(sym) orelse + const field_val = struct_val.map.get(sym) orelse return Error.TypeError; const field_zig_val = try self.toZig(field.type, alloc, field_val); errdefer toZigClean(field.type, alloc, field_zig_val, null); diff --git a/src/builtins.zig b/src/builtins.zig index 1463871..03a8dbf 100644 --- a/src/builtins.zig +++ b/src/builtins.zig @@ -53,10 +53,10 @@ fn equalImpl(a: Val, b: Val) Error!bool { return true; }, .structV => |x| { - if (x.count() != b.structV.count()) return false; - var iter = x.iterator(); + if (x.map.count() != b.structV.map.count()) return false; + var iter = x.map.iterator(); while (iter.next()) |a_entry| { - const b_val = b.structV.get(a_entry.key_ptr.*) orelse return false; + const b_val = b.structV.map.get(a_entry.key_ptr.*) orelse return false; if (!try equalImpl(a_entry.value_ptr.*, b_val)) return false; } return true; @@ -180,24 +180,24 @@ fn makeStruct(vm: *Vm, vals: []const Val) Error!Val { else => return Error.TypeError, } } - const fields = vm.env.memory_manager.allocateStruct() catch return Error.RuntimeError; - fields.ensureTotalCapacity(vm.valAllocator(), @intCast(field_count)) catch return Error.RuntimeError; + const new_struct = vm.env.memory_manager.allocateStruct() catch return Error.RuntimeError; + new_struct.map.ensureTotalCapacity(vm.valAllocator(), @intCast(field_count)) catch return Error.RuntimeError; for (0..field_count) |idx| { const name_idx = idx * 2; const val_idx = name_idx + 1; switch (vals[name_idx]) { .symbol => |k| { - fields.put(vm.valAllocator(), k, vals[val_idx]) catch return Error.RuntimeError; + new_struct.map.put(vm.valAllocator(), k, vals[val_idx]) catch return Error.RuntimeError; }, else => unreachable, } } - return .{ .structV = fields }; + return .{ .structV = new_struct }; } fn structSet(vm: *Vm, vals: []const Val) Error!Val { if (vals.len != 3) return Error.ArrityError; - const struct_map = switch (vals[0]) { + const struct_val = switch (vals[0]) { .structV => |m| m, else => return Error.TypeError, }; @@ -205,17 +205,17 @@ fn structSet(vm: *Vm, vals: []const Val) Error!Val { .symbol => |s| s, else => return Error.TypeError, }; - if (struct_map.getEntry(sym)) |entry| { + if (struct_val.map.getEntry(sym)) |entry| { entry.value_ptr.* = vals[2]; return .none; } - struct_map.put(vm.valAllocator(), sym, vals[2]) catch return Error.RuntimeError; + struct_val.map.put(vm.valAllocator(), sym, vals[2]) catch return Error.RuntimeError; return .none; } fn structGet(_: *Vm, vals: []const Val) Error!Val { if (vals.len != 2) return Error.ArrityError; - const struct_map = switch (vals[0]) { + const struct_val = switch (vals[0]) { .structV => |m| m, else => return Error.TypeError, }; @@ -223,7 +223,7 @@ fn structGet(_: *Vm, vals: []const Val) Error!Val { .symbol => |s| s, else => return Error.TypeError, }; - const v = struct_map.get(sym) orelse return Error.RuntimeError; + const v = struct_val.map.get(sym) orelse return Error.RuntimeError; return v; } diff --git a/src/golden_test.fizz b/src/golden_test.fizz index df5c3b9..2dba770 100644 --- a/src/golden_test.fizz +++ b/src/golden_test.fizz @@ -51,14 +51,13 @@ test-struct "------------------------------------------------------------" "test fib" -(define fib - (lambda (n) - (define too-small? (< n 2)) - (if too-small? n - (do - (define fib-1 (fib (- n 1))) - (define fib-2 (fib (- n 2))) - (+ fib-1 fib-2))))) +(define (fib n) + (define too-small? (< n 2)) + (if too-small? n + (do + (define fib-1 (fib (- n 1))) + (define fib-2 (fib (- n 2))) + (+ fib-1 fib-2)))) fib (fib 25) diff --git a/src/ir.zig b/src/ir.zig index e852373..43a0ab2 100644 --- a/src/ir.zig +++ b/src/ir.zig @@ -255,11 +255,7 @@ const IrBuilder = struct { try self.errors.addError(.{ .msg = "define expected form (define )" }); return Error.SyntaxError; }, - 2 => return self.buildDefine(&rest[0], &rest[1]), - else => { - try self.errors.addError(.{ .msg = "define expected form (define )" }); - return Error.SyntaxError; - }, + else => return self.buildDefine(&rest[0], rest[1..]), } }, .import => { @@ -314,11 +310,10 @@ const IrBuilder = struct { return ret; } - fn buildDefine(self: *IrBuilder, sym: *const Ast.Node, def: *const Ast.Node) Error!*Ir { + fn buildDefine(self: *IrBuilder, sym: *const Ast.Node, exprs: []const Ast.Node) Error!*Ir { const name = switch (sym.*) { - .tree => { - try self.errors.addError(.{ .msg = "define expected form (define ) but was malformed" }); - return Error.SyntaxError; + .tree => |name_and_args| { + return self.buildDefineLambda(name_and_args, exprs); }, .leaf => |l| switch (l) { .identifier => |ident| ident, @@ -328,7 +323,9 @@ const IrBuilder = struct { }, }, }; - var expr = try self.build(name, def); + if (exprs.len == 0) return Error.SyntaxError; + if (exprs.len > 1) return Error.SyntaxError; + var expr = try self.build(name, &exprs[0]); errdefer expr.deinit(self.allocator); const ret = try self.allocator.create(Ir); errdefer ret.deinit(self.allocator); @@ -341,6 +338,46 @@ const IrBuilder = struct { return ret; } + fn buildDefineLambda(self: *IrBuilder, name_and_args: []const Ast.Node, exprs: []const Ast.Node) Error!*Ir { + if (name_and_args.len == 0) { + try self.errors.addError(.{ + .msg = "define expected form (define ( ...) ) but was not found", + }); + return Error.SyntaxError; + } + const name = switch (name_and_args[0]) { + .tree => { + return Error.SyntaxError; + }, + .leaf => |l| switch (l) { + .identifier => |ident| ident, + else => { + return Error.SyntaxError; + }, + }, + }; + const args = Ast.Node{ .tree = name_and_args[1..] }; + for (args.tree) |arg| { + switch (arg) { + .tree => return Error.SyntaxError, + .leaf => |l| switch (l) { + .identifier => {}, + else => return Error.SyntaxError, + }, + } + } + const lambda_ir = try self.buildLambdaExpr(name, &args, exprs); + const ret = try self.allocator.create(Ir); + errdefer ret.deinit(self.allocator); + ret.* = .{ + .define = .{ + .name = name, + .expr = lambda_ir, + }, + }; + return ret; + } + fn buildImportModule(self: *IrBuilder, path_expr: *const Ast.Node) Error!*Ir { const path = switch (path_expr.*) { .tree => { diff --git a/src/val.zig b/src/val.zig index a03a817..053473d 100644 --- a/src/val.zig +++ b/src/val.zig @@ -11,7 +11,7 @@ pub const Val = union(enum) { string: []const u8, symbol: Symbol, list: []Val, - structV: *std.AutoHashMapUnmanaged(Symbol, Val), + structV: *StructVal, bytecode: *ByteCode, native_fn: NativeFn, @@ -19,6 +19,13 @@ pub const Val = union(enum) { pub const Symbol = struct { id: usize }; + pub const StructVal = struct { + map: std.AutoHashMapUnmanaged(Symbol, Val), + pub fn deinit(self: *StructVal, allocator: std.mem.Allocator) void { + self.map.deinit(allocator); + } + }; + pub const NativeFn = struct { pub const Error = error{ ArrityError, @@ -66,7 +73,7 @@ pub const Val = union(enum) { try writer.writeAll(")"); }, .structV => |struct_map| { - var iter = struct_map.iterator(); + var iter = struct_map.map.iterator(); try writer.writeAll("(struct"); while (iter.next()) |v| { try writer.writeAll(" ");