Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement shorter function syntax. #16

Merged
merged 1 commit into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions site/fizz-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ nav_order: 2

### define

`(define <name> <value>)`
Values - `(define <name> <value>)`
Functions - `(define (<name> <args...>) <expr>...)`

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
```
Expand All @@ -27,12 +28,12 @@ $1 = 144

`(lambda (<args...>) <exprs...>)`

Lambda is used to define a function.
Define a function.

```lisp
>> (define my-plus-func (lambda (a b) (+ a b)))
>> my-plus-func
$1 = <function >
$1 = <function _>
>> (my-plus-func 2 2)
$2 = 4
>> ((lambda (a b) (- a b)) 2 2)
Expand Down
2 changes: 1 addition & 1 deletion site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
> ```
Expand Down
21 changes: 11 additions & 10 deletions src/MemoryManager.zig
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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) = .{},

Expand Down Expand Up @@ -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.
Expand All @@ -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.*);
}
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/Vm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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);
Expand Down
24 changes: 12 additions & 12 deletions src/builtins.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -180,50 +180,50 @@ 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,
};
const sym = switch (vals[1]) {
.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,
};
const sym = switch (vals[1]) {
.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;
}

Expand Down
15 changes: 7 additions & 8 deletions src/golden_test.fizz
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
57 changes: 47 additions & 10 deletions src/ir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -255,11 +255,7 @@ const IrBuilder = struct {
try self.errors.addError(.{ .msg = "define expected form (define <ident> <expr>)" });
return Error.SyntaxError;
},
2 => return self.buildDefine(&rest[0], &rest[1]),
else => {
try self.errors.addError(.{ .msg = "define expected form (define <ident> <expr>)" });
return Error.SyntaxError;
},
else => return self.buildDefine(&rest[0], rest[1..]),
}
},
.import => {
Expand Down Expand Up @@ -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 <ident> <expr>) but <ident> was malformed" });
return Error.SyntaxError;
.tree => |name_and_args| {
return self.buildDefineLambda(name_and_args, exprs);
},
.leaf => |l| switch (l) {
.identifier => |ident| ident,
Expand All @@ -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);
Expand All @@ -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 (<ident> <args>...) <expr>) but <ident> 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 => {
Expand Down
11 changes: 9 additions & 2 deletions src/val.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@ pub const Val = union(enum) {
string: []const u8,
symbol: Symbol,
list: []Val,
structV: *std.AutoHashMapUnmanaged(Symbol, Val),
structV: *StructVal,
bytecode: *ByteCode,
native_fn: NativeFn,

const Tag = std.meta.Tag(Val);

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,
Expand Down Expand Up @@ -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(" ");
Expand Down