Closures, Higher-Order Functions, and Everything in Between #44
|
@ -1,9 +1,7 @@
|
||||||
|
|
||||||
let printer = lambda (pfx):
|
let printer = lambda (pfx):
|
||||||
let prefix = pfx
|
|
||||||
|
|
||||||
return lambda (msg):
|
return lambda (msg):
|
||||||
@print(prefix)
|
@print(pfx)
|
||||||
@print(msg)
|
@print(msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -79,9 +79,7 @@ pub fn dump(chunk: Self, env: *kym.RuntimeEnv) kym.RuntimeError!*kym.RuntimeRef
|
||||||
|
|
||||||
const writer = coral.list.stack_as_writer(&buffer);
|
const writer = coral.list.stack_as_writer(&buffer);
|
||||||
|
|
||||||
_ = coral.utf8.print_string(writer, coral.io.slice_sentineled(@as(coral.io.Byte, 0), try env.unbox_symbol(chunk.name)));
|
_ = coral.utf8.print_string(writer, "\n");
|
||||||
|
|
||||||
_ = coral.utf8.print_string(writer, ":\n");
|
|
||||||
|
|
||||||
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
||||||
_ = coral.utf8.print_formatted(writer, "[{instruction}]: ", .{.instruction = opcode_cursor});
|
_ = coral.utf8.print_formatted(writer, "[{instruction}]: ", .{.instruction = opcode_cursor});
|
||||||
|
@ -613,6 +611,12 @@ pub fn make(env: *kym.RuntimeEnv, name: []const coral.io.Byte, environment: *con
|
||||||
try compiler.compile_environment(environment);
|
try compiler.compile_environment(environment);
|
||||||
|
|
||||||
if (builtin.mode == .Debug) {
|
if (builtin.mode == .Debug) {
|
||||||
|
const allocation = try coral.utf8.alloc_formatted(env.allocator, "compiled {name}...", .{.name = name});
|
||||||
|
|
||||||
|
defer env.allocator.deallocate(allocation);
|
||||||
|
|
||||||
|
app.log_info(allocation);
|
||||||
|
|
||||||
const string_ref = try chunk.dump(env);
|
const string_ref = try chunk.dump(env);
|
||||||
|
|
||||||
defer env.discard(string_ref);
|
defer env.discard(string_ref);
|
||||||
|
|
|
@ -152,7 +152,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
if (get_capture_index(environment, declaration_get.declaration)) |index| {
|
if (get_capture_index(environment, declaration_get.declaration)) |index| {
|
||||||
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||||
|
|
||||||
if (declaration_get.declaration.is.captured) {
|
if (is_declaration_boxed(declaration_get.declaration)) {
|
||||||
try self.chunk.opcodes.push_one(.get_box);
|
try self.chunk.opcodes.push_one(.get_box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ fn compile_expression(self: Self, environment: *const tree.Environment, expressi
|
||||||
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
try self.chunk.opcodes.push_one(.{.push_binding = index});
|
||||||
try self.compile_expression(environment, declaration_set.assign);
|
try self.compile_expression(environment, declaration_set.assign);
|
||||||
|
|
||||||
if (declaration_set.declaration.is.captured) {
|
if (is_declaration_boxed(declaration_set.declaration)) {
|
||||||
try self.chunk.opcodes.push_one(.set_box);
|
try self.chunk.opcodes.push_one(.set_box);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,9 +359,9 @@ pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree
|
||||||
while (capture_index < self.capture_count) : (capture_index += 1) {
|
while (capture_index < self.capture_count) : (capture_index += 1) {
|
||||||
const captured_declaration_index = self.captures[capture_index];
|
const captured_declaration_index = self.captures[capture_index];
|
||||||
|
|
||||||
coral.debug.assert(captured_declaration_index < enclosing_environment.local_declaration_count);
|
coral.debug.assert(captured_declaration_index < enclosing_environment.declaration_count);
|
||||||
|
|
||||||
if (&enclosing_environment.local_declarations[captured_declaration_index] == declaration) {
|
if (&enclosing_environment.declarations[captured_declaration_index] == declaration) {
|
||||||
return capture_index;
|
return capture_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -371,15 +371,19 @@ pub fn get_capture_index(self: *const tree.Environment, declaration: *const tree
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_local_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
pub fn get_local_index(self: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
||||||
var remaining = self.local_declaration_count;
|
var remaining = self.declaration_count;
|
||||||
|
|
||||||
while (remaining != 0) {
|
while (remaining != 0) {
|
||||||
remaining -= 1;
|
remaining -= 1;
|
||||||
|
|
||||||
if (&self.local_declarations[remaining] == declaration) {
|
if (&self.declarations[remaining] == declaration) {
|
||||||
return remaining;
|
return remaining;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_declaration_boxed(declaration: *const tree.Declaration) bool {
|
||||||
|
return declaration.is.captured and !declaration.is.readonly;
|
||||||
|
}
|
||||||
|
|
|
@ -419,19 +419,16 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
||||||
.identifier => |identifier| {
|
.identifier => |identifier| {
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
const declaration = (try environment.resolve_declaration(identifier)) orelse {
|
return root.create_expr(.{
|
||||||
|
.kind = .{
|
||||||
|
.declaration_get = .{
|
||||||
|
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||||
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
if (declaration.is.argument and declaration.is.captured) {
|
|
||||||
return root.report_error(stream, "arguments cannot be directly captured, create a declaration", .{});
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
return root.create_expr(.{
|
},
|
||||||
.next = null,
|
|
||||||
.kind = .{.declaration_get = .{.declaration = declaration}},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -452,16 +449,9 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
||||||
else => return root.report_error(stream, "expected identifier", .{}),
|
else => return root.report_error(stream, "expected identifier", .{}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (try lambda_environment.declare(.{
|
_ = lambda_environment.declare_argument(identifier) catch |declare_error| {
|
||||||
.identifier = identifier,
|
return root.report_declare_error(stream, identifier, declare_error);
|
||||||
.is = .{.argument = true},
|
};
|
||||||
}) == null) {
|
|
||||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
|
||||||
.identifier = identifier,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
lambda_environment.argument_count += 1;
|
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
|
|
@ -93,18 +93,13 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
||||||
},
|
},
|
||||||
|
|
||||||
.keyword_var, .keyword_let => {
|
.keyword_var, .keyword_let => {
|
||||||
const storage_token = stream.token;
|
const is_constant = stream.token == .keyword_let;
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
|
||||||
const identifier = switch (stream.token) {
|
const identifier = switch (stream.token) {
|
||||||
.identifier => |identifier| identifier,
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream, "expected identifier after declaration", .{}),
|
||||||
else => {
|
|
||||||
return root.report_error(stream, "expected identifier after `{storage}` declaration statement", .{
|
|
||||||
.storage = storage_token.text()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
stream.skip_newlines();
|
stream.skip_newlines();
|
||||||
|
@ -122,10 +117,30 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
||||||
.declare = .{
|
.declare = .{
|
||||||
.initial_expression = try Expr.parse(root, stream, environment),
|
.initial_expression = try Expr.parse(root, stream, environment),
|
||||||
|
|
||||||
.declaration = (try environment.declare(.{.identifier = identifier})) orelse {
|
.declaration = declare: {
|
||||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
if (is_constant) {
|
||||||
|
break: declare environment.declare_constant(identifier) catch |declaration_error| {
|
||||||
|
return switch (declaration_error) {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
|
||||||
|
error.DeclarationExists =>
|
||||||
|
root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||||
.identifier = identifier,
|
.identifier = identifier,
|
||||||
});
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break: declare environment.declare_variable(identifier) catch |declaration_error| {
|
||||||
|
return switch (declaration_error) {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
|
||||||
|
error.DeclarationExists =>
|
||||||
|
root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,19 +12,22 @@ pub const Declaration = struct {
|
||||||
is: packed struct {
|
is: packed struct {
|
||||||
readonly: bool = false,
|
readonly: bool = false,
|
||||||
captured: bool = false,
|
captured: bool = false,
|
||||||
argument: bool = false,
|
|
||||||
} = .{},
|
} = .{},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Environment = struct {
|
pub const Environment = struct {
|
||||||
captures: [capture_max]u8 = [_]u8{0} ** capture_max,
|
captures: [capture_max]u8 = [_]u8{0} ** capture_max,
|
||||||
capture_count: u8 = 0,
|
capture_count: u8 = 0,
|
||||||
local_declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
||||||
local_declaration_count: u8 = 0,
|
declaration_count: u8 = 0,
|
||||||
argument_count: u8 = 0,
|
argument_count: u8 = 0,
|
||||||
statement: ?*const Stmt = null,
|
statement: ?*const Stmt = null,
|
||||||
enclosing: ?*Environment = null,
|
enclosing: ?*Environment = null,
|
||||||
|
|
||||||
|
const DeclareError = coral.io.AllocationError || error {
|
||||||
|
DeclarationExists,
|
||||||
|
};
|
||||||
|
|
||||||
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
||||||
|
|
||||||
const declaration_max = coral.math.max_int(@typeInfo(u8).Int);
|
const declaration_max = coral.math.max_int(@typeInfo(u8).Int);
|
||||||
|
@ -35,30 +38,70 @@ pub const Environment = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare(self: *Environment, declaration: Declaration) coral.io.AllocationError!?*const Declaration {
|
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration {
|
||||||
if (self.local_declaration_count == self.local_declarations.len) {
|
if (self.declaration_count == self.declarations.len) {
|
||||||
return error.OutOfMemory;
|
return error.OutOfMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
const declaration_slot = &self.local_declarations[self.local_declaration_count];
|
{
|
||||||
|
var environment = self;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
|
while (remaining_count != 0) {
|
||||||
|
remaining_count -= 1;
|
||||||
|
|
||||||
|
if (coral.io.are_equal(environment.declarations[remaining_count].identifier, declaration.identifier)) {
|
||||||
|
return error.DeclarationExists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment = environment.enclosing orelse break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const declaration_slot = &self.declarations[self.declaration_count];
|
||||||
|
|
||||||
declaration_slot.* = declaration;
|
declaration_slot.* = declaration;
|
||||||
self.local_declaration_count += 1;
|
self.declaration_count += 1;
|
||||||
|
|
||||||
return declaration_slot;
|
return declaration_slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn declare_argument(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
coral.debug.assert(self.declaration_count <= self.argument_count);
|
||||||
|
|
||||||
|
defer self.argument_count += 1;
|
||||||
|
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_constant(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_variable(self: *Environment, identifier: []const coral.io.Byte) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{.identifier = identifier});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
pub fn resolve_declaration(self: *Environment, identifier: []const coral.io.Byte) coral.io.AllocationError!?*const Declaration {
|
||||||
var environment = self;
|
var environment = self;
|
||||||
var ancestry = @as(usize, 0);
|
var ancestry = @as(usize, 0);
|
||||||
|
|
||||||
while (true) : (ancestry += 1) {
|
while (true) : (ancestry += 1) {
|
||||||
var remaining_count = environment.local_declaration_count;
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
while (remaining_count != 0) {
|
while (remaining_count != 0) {
|
||||||
remaining_count -= 1;
|
remaining_count -= 1;
|
||||||
|
|
||||||
const declaration = &environment.local_declarations[remaining_count];
|
const declaration = &environment.declarations[remaining_count];
|
||||||
|
|
||||||
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
if (coral.io.are_equal(declaration.identifier, identifier)) {
|
||||||
if (ancestry != 0) {
|
if (ancestry != 0) {
|
||||||
|
@ -110,6 +153,16 @@ pub const Root = struct {
|
||||||
return error.BadSyntax;
|
return error.BadSyntax;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn report_declare_error(self: *Root, stream: *tokens.Stream, identifier: []const coral.io.Byte, @"error": Environment.DeclareError) ParseError {
|
||||||
|
return switch (@"error") {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
|
||||||
|
error.DeclarationExists => self.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_expr(self: *Root, expr: Expr) coral.io.AllocationError!*Expr {
|
pub fn create_expr(self: *Root, expr: Expr) coral.io.AllocationError!*Expr {
|
||||||
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue