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.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(.{
 | 
								return root.create_expr(.{
 | 
				
			||||||
				.next = null,
 | 
									.kind = .{
 | 
				
			||||||
				.kind = .{.declaration_get = .{.declaration = declaration}},
 | 
										.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", .{}),
 | 
										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) {
 | 
				
			||||||
								.identifier = identifier,
 | 
													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,
 | 
				
			||||||
 | 
																}),
 | 
				
			||||||
 | 
														};
 | 
				
			||||||
 | 
													};
 | 
				
			||||||
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user