Closures, Higher-Order Functions, and Everything in Between #44
|
@ -1,9 +1,7 @@
|
|||
|
||||
let printer = lambda (pfx):
|
||||
let prefix = pfx
|
||||
|
||||
return lambda (msg):
|
||||
@print(prefix)
|
||||
@print(pfx)
|
||||
@print(msg)
|
||||
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);
|
||||
|
||||
_ = 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) {
|
||||
_ = 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);
|
||||
|
||||
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);
|
||||
|
||||
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| {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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.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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
var remaining = self.local_declaration_count;
|
||||
var remaining = self.declaration_count;
|
||||
|
||||
while (remaining != 0) {
|
||||
remaining -= 1;
|
||||
|
||||
if (&self.local_declarations[remaining] == declaration) {
|
||||
if (&self.declarations[remaining] == declaration) {
|
||||
return remaining;
|
||||
}
|
||||
}
|
||||
|
||||
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| {
|
||||
stream.skip_newlines();
|
||||
|
||||
const declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||
return root.report_error(stream, "undefined 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}},
|
||||
.kind = .{
|
||||
.declaration_get = .{
|
||||
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||
return root.report_error(stream, "undefined identifier `{identifier}`", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -452,16 +449,9 @@ fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.En
|
|||
else => return root.report_error(stream, "expected identifier", .{}),
|
||||
};
|
||||
|
||||
if (try lambda_environment.declare(.{
|
||||
.identifier = identifier,
|
||||
.is = .{.argument = true},
|
||||
}) == null) {
|
||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
}
|
||||
|
||||
lambda_environment.argument_count += 1;
|
||||
_ = lambda_environment.declare_argument(identifier) catch |declare_error| {
|
||||
return root.report_declare_error(stream, identifier, declare_error);
|
||||
};
|
||||
|
||||
stream.skip_newlines();
|
||||
|
||||
|
|
|
@ -93,18 +93,13 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
|||
},
|
||||
|
||||
.keyword_var, .keyword_let => {
|
||||
const storage_token = stream.token;
|
||||
const is_constant = stream.token == .keyword_let;
|
||||
|
||||
stream.skip_newlines();
|
||||
|
||||
const identifier = switch (stream.token) {
|
||||
.identifier => |identifier| identifier,
|
||||
|
||||
else => {
|
||||
return root.report_error(stream, "expected identifier after `{storage}` declaration statement", .{
|
||||
.storage = storage_token.text()
|
||||
});
|
||||
},
|
||||
else => return root.report_error(stream, "expected identifier after declaration", .{}),
|
||||
};
|
||||
|
||||
stream.skip_newlines();
|
||||
|
@ -122,10 +117,30 @@ pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Enviro
|
|||
.declare = .{
|
||||
.initial_expression = try Expr.parse(root, stream, environment),
|
||||
|
||||
.declaration = (try environment.declare(.{.identifier = identifier})) orelse {
|
||||
return root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||
.identifier = identifier,
|
||||
});
|
||||
.declaration = declare: {
|
||||
if (is_constant) {
|
||||
break: declare environment.declare_constant(identifier) catch |declaration_error| {
|
||||
return switch (declaration_error) {
|
||||
kayomn marked this conversation as resolved
Outdated
|
||||
error.OutOfMemory => error.OutOfMemory,
|
||||
|
||||
error.DeclarationExists =>
|
||||
root.report_error(stream, "declaration `{identifier}` already exists", .{
|
||||
.identifier = identifier,
|
||||
}),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
break: declare environment.declare_variable(identifier) catch |declaration_error| {
|
||||
return switch (declaration_error) {
|
||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Shorten to use Shorten to use `root.report_declare_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 {
|
||||
readonly: bool = false,
|
||||
captured: bool = false,
|
||||
argument: bool = false,
|
||||
} = .{},
|
||||
};
|
||||
|
||||
pub const Environment = struct {
|
||||
captures: [capture_max]u8 = [_]u8{0} ** capture_max,
|
||||
capture_count: u8 = 0,
|
||||
local_declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
||||
local_declaration_count: u8 = 0,
|
||||
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
||||
declaration_count: u8 = 0,
|
||||
argument_count: u8 = 0,
|
||||
statement: ?*const Stmt = null,
|
||||
enclosing: ?*Environment = null,
|
||||
|
||||
const DeclareError = coral.io.AllocationError || error {
|
||||
DeclarationExists,
|
||||
};
|
||||
|
||||
const capture_max = coral.math.max_int(@typeInfo(u8).Int);
|
||||
|
||||
kayomn marked this conversation as resolved
Outdated
kayomn
commented
Should ideally be marked pub as is used on public interface for struct. Should ideally be marked pub as is used on public interface for struct.
|
||||
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 {
|
||||
if (self.local_declaration_count == self.local_declarations.len) {
|
||||
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration {
|
||||
if (self.declaration_count == self.declarations.len) {
|
||||
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;
|
||||
self.local_declaration_count += 1;
|
||||
self.declaration_count += 1;
|
||||
|
||||
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 {
|
||||
var environment = self;
|
||||
var ancestry = @as(usize, 0);
|
||||
|
||||
while (true) : (ancestry += 1) {
|
||||
var remaining_count = environment.local_declaration_count;
|
||||
var remaining_count = environment.declaration_count;
|
||||
|
||||
while (remaining_count != 0) {
|
||||
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 (ancestry != 0) {
|
||||
|
@ -110,6 +153,16 @@ pub const Root = struct {
|
|||
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 {
|
||||
return coral.io.allocate_one(self.arena.as_allocator(), expr);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Shorten to use
root.report_declare_error
.