Make arguments read-only
This commit is contained in:
		
							parent
							
								
									880761a722
								
							
						
					
					
						commit
						88ff2a64b6
					
				| @ -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) { | ||||
| 										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 { | ||||
| 		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); | ||||
| 
 | ||||
| 	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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user