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) {
|
||||||
kayomn marked this conversation as resolved
Outdated
|
|||||||
|
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) {
|
||||||
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 {
|
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);
|
||||||
|
|
||||||
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);
|
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
Shorten to use
root.report_declare_error
.