Make Local Declarations Explicit #38
| @ -70,8 +70,18 @@ pub const RuntimeEnv = struct { | ||||
| 		const OpcodeList = coral.list.Stack(Opcode); | ||||
| 
 | ||||
| 		const CompilationUnit = struct { | ||||
| 			local_identifiers_buffer: [255][]const coral.io.Byte = [_][]const coral.io.Byte{""} ** 255, | ||||
| 			local_identifiers_count: u8 = 0, | ||||
| 			locals_buffer: [255]Local = [_]Local{.{}} ** 255, | ||||
| 			locals_count: u8 = 0, | ||||
| 
 | ||||
| 			const ResolvedLocal = struct { | ||||
| 				index: u8, | ||||
| 				is_readonly: bool = false, | ||||
| 			}; | ||||
| 
 | ||||
| 			const Local =  struct { | ||||
| 				identifier: []const coral.io.Byte = "", | ||||
| 				is_readonly: bool = false, | ||||
| 			}; | ||||
| 
 | ||||
| 			fn compile_expression(self: *CompilationUnit, chunk: *Chunk, expression: ast.Expression) RuntimeError!void { | ||||
| 				const number_format = coral.utf8.DecimalFormat{ | ||||
| @ -204,20 +214,24 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 					.local_get => |local_get| { | ||||
| 						try chunk.opcodes.push_one(.{ | ||||
| 							.push_local = self.resolve_local(local_get.identifier) orelse { | ||||
| 							.push_local = (self.resolve_local(local_get.identifier) orelse { | ||||
| 								return chunk.env.raise(error.OutOfMemory, "undefined local"); | ||||
| 							}, | ||||
| 							}).index, | ||||
| 						}); | ||||
| 					}, | ||||
| 
 | ||||
| 					.local_set => |local_set| { | ||||
| 						try self.compile_expression(chunk, local_set.value_expression.*); | ||||
| 
 | ||||
| 						try chunk.opcodes.push_one(.{ | ||||
| 							.local_set = self.resolve_local(local_set.identifier) orelse { | ||||
| 								return chunk.env.raise(error.BadSyntax, "undefined local"); | ||||
| 							}, | ||||
| 						}); | ||||
| 						const resolved_local = self.resolve_local(local_set.identifier) orelse { | ||||
| 							return chunk.env.raise(error.BadSyntax, "undefined local"); | ||||
| 						}; | ||||
| 
 | ||||
| 						if (resolved_local.is_readonly) { | ||||
| 							return chunk.env.raise(error.BadSyntax, "cannot set a read-only declaration"); | ||||
| 						} | ||||
| 
 | ||||
| 						try chunk.opcodes.push_one(.{.local_set = resolved_local.index}); | ||||
| 					}, | ||||
| 
 | ||||
| 					.field_get => |field_get| { | ||||
| @ -266,15 +280,23 @@ pub const RuntimeEnv = struct { | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.local_var => |local_var| { | ||||
| 						try self.compile_expression(chunk, local_var.assigned_expression); | ||||
| 					.declare => |declare| { | ||||
| 						if (self.resolve_local(declare.identifier) != null) { | ||||
| 							return chunk.env.raise(error.BadSyntax, "declaration shadows existing one"); | ||||
| 						} | ||||
| 
 | ||||
| 						if (self.local_identifiers_count == self.local_identifiers_buffer.len) { | ||||
| 						try self.compile_expression(chunk, declare.assigned_expression); | ||||
| 
 | ||||
| 						if (self.locals_count == self.locals_buffer.len) { | ||||
| 							return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals"); | ||||
| 						} | ||||
| 
 | ||||
| 						self.local_identifiers_buffer[self.local_identifiers_count] = local_var.identifier; | ||||
| 						self.local_identifiers_count += 1; | ||||
| 						self.locals_buffer[self.locals_count] = .{ | ||||
| 							.identifier = declare.identifier, | ||||
| 							.is_readonly = declare.storage != .variant, | ||||
| 						}; | ||||
| 
 | ||||
| 						self.locals_count += 1; | ||||
| 					}, | ||||
| 
 | ||||
| 					.block => |block| { | ||||
| @ -326,16 +348,21 @@ pub const RuntimeEnv = struct { | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?u8 { | ||||
| 				if (self.local_identifiers_count == 0) { | ||||
| 			fn resolve_local(self: *CompilationUnit, local_identifier: []const coral.io.Byte) ?ResolvedLocal { | ||||
| 				if (self.locals_count == 0) { | ||||
| 					return null; | ||||
| 				} | ||||
| 
 | ||||
| 				var index = @as(u8, self.local_identifiers_count - 1); | ||||
| 				var index = @as(u8, self.locals_count - 1); | ||||
| 
 | ||||
| 				while (true) : (index -= 1) { | ||||
| 					if (coral.io.are_equal(local_identifier, self.local_identifiers_buffer[index])) { | ||||
| 						return index; | ||||
| 					const local = &self.locals_buffer[index]; | ||||
| 
 | ||||
| 					if (coral.io.are_equal(local_identifier, local.identifier)) { | ||||
| 						return .{ | ||||
| 							.index = index, | ||||
| 							.is_readonly = local.is_readonly, | ||||
| 						}; | ||||
| 					} | ||||
| 
 | ||||
| 					if (index == 0) { | ||||
|  | ||||
| @ -129,6 +129,11 @@ pub const Expression = union (enum) { | ||||
| 	}); | ||||
| }; | ||||
| 
 | ||||
| pub const DeclarationStorage = enum { | ||||
| 	variant, | ||||
| 	readonly, | ||||
| }; | ||||
| 
 | ||||
| const ExpressionBuilder = fn (self: *Tree) ParseError!Expression; | ||||
| 
 | ||||
| pub const ParseError = error { | ||||
| @ -139,7 +144,8 @@ pub const ParseError = error { | ||||
| pub const Statement = union (enum) { | ||||
| 	@"return": ?Expression, | ||||
| 
 | ||||
| 	local_var: struct { | ||||
| 	declare: struct { | ||||
| 		storage: DeclarationStorage, | ||||
| 		identifier: []const coral.io.Byte, | ||||
| 		assigned_expression: Expression, | ||||
| 	}, | ||||
| @ -672,8 +678,34 @@ pub const Tree = struct { | ||||
| 				self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 				return .{ | ||||
| 					.local_var = .{ | ||||
| 					.declare = .{ | ||||
| 						.assigned_expression = try self.parse_expression(), | ||||
| 						.storage = .variant, | ||||
| 						.identifier = identifier, | ||||
| 					}, | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.keyword_let => { | ||||
| 				self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 				const identifier = switch (self.tokenizer.token) { | ||||
| 					.identifier => |identifier| identifier, | ||||
| 					else => return self.report("expected identifier after `let` declaration statement"), | ||||
| 				}; | ||||
| 
 | ||||
| 				self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 				if (self.tokenizer.token != .symbol_equals) { | ||||
| 					return self.report("expected `=` after declaration identifier"); | ||||
| 				} | ||||
| 
 | ||||
| 				self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 				return .{ | ||||
| 					.declare = .{ | ||||
| 						.assigned_expression = try self.parse_expression(), | ||||
| 						.storage = .readonly, | ||||
| 						.identifier = identifier, | ||||
| 					}, | ||||
| 				}; | ||||
|  | ||||
| @ -45,6 +45,7 @@ pub const Token = union(enum) { | ||||
| 	keyword_else, | ||||
| 	keyword_elif, | ||||
| 	keyword_var, | ||||
| 	keyword_let, | ||||
| 
 | ||||
| 	pub fn text(self: Token) []const coral.io.Byte { | ||||
| 		return switch (self) { | ||||
| @ -93,6 +94,7 @@ pub const Token = union(enum) { | ||||
| 			.keyword_elif => "elif", | ||||
| 			.keyword_else => "else", | ||||
| 			.keyword_var => "var", | ||||
| 			.keyword_let => "let", | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
| @ -231,6 +233,14 @@ pub const Tokenizer = struct { | ||||
| 							} | ||||
| 						}, | ||||
| 
 | ||||
| 						'l' => { | ||||
| 							if (coral.io.ends_with(identifier, "et")) { | ||||
| 								self.token = .keyword_let; | ||||
| 
 | ||||
| 								return; | ||||
| 							} | ||||
| 						}, | ||||
| 
 | ||||
| 						'n' => { | ||||
| 							if (coral.io.ends_with(identifier, "il")) { | ||||
| 								self.token = .keyword_nil; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user