Make Local Declarations Explicit #38
| @ -1,5 +1,5 @@ | ||||
| 
 | ||||
| i = 0 | ||||
| var i = 0 | ||||
| 
 | ||||
| while i < 5: | ||||
| 	@print("hello, world") | ||||
|  | ||||
| @ -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,25 +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.*); | ||||
| 
 | ||||
| 						if (self.resolve_local(local_set.identifier)) |index| { | ||||
| 							try chunk.opcodes.push_one(.{.local_set = index}); | ||||
| 						} else { | ||||
| 							if (self.local_identifiers_count == self.local_identifiers_buffer.len) { | ||||
| 								return chunk.env.raise(error.BadSyntax, "chunks may have a maximum of 255 locals"); | ||||
| 							} | ||||
| 						const resolved_local = self.resolve_local(local_set.identifier) orelse { | ||||
| 							return chunk.env.raise(error.BadSyntax, "undefined local"); | ||||
| 						}; | ||||
| 
 | ||||
| 							self.local_identifiers_buffer[self.local_identifiers_count] = local_set.identifier; | ||||
| 							self.local_identifiers_count += 1; | ||||
| 						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| { | ||||
| @ -271,6 +280,25 @@ pub const RuntimeEnv = struct { | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.declare => |declare| { | ||||
| 						if (self.resolve_local(declare.identifier) != null) { | ||||
| 							return chunk.env.raise(error.BadSyntax, "declaration shadows existing one"); | ||||
| 						} | ||||
| 
 | ||||
| 						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.locals_buffer[self.locals_count] = .{ | ||||
| 							.identifier = declare.identifier, | ||||
| 							.is_readonly = declare.storage != .variant, | ||||
| 						}; | ||||
| 
 | ||||
| 						self.locals_count += 1; | ||||
| 					}, | ||||
| 
 | ||||
| 					.block => |block| { | ||||
| 						for (block.values) |block_statement| { | ||||
| 							try self.compile_statement(chunk, block_statement); | ||||
| @ -320,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,6 +144,12 @@ pub const ParseError = error { | ||||
| pub const Statement = union (enum) { | ||||
| 	@"return": ?Expression, | ||||
| 
 | ||||
| 	declare: struct { | ||||
| 		storage: DeclarationStorage, | ||||
| 		identifier: []const coral.io.Byte, | ||||
| 		assigned_expression: Expression, | ||||
| 	}, | ||||
| 
 | ||||
| 	@"if": struct { | ||||
| 		condition_expression: Expression, | ||||
| 		block_statements: List, | ||||
| @ -650,6 +661,56 @@ pub const Tree = struct { | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.keyword_var => { | ||||
| 				self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 				const identifier = switch (self.tokenizer.token) { | ||||
| 					.identifier => |identifier| identifier, | ||||
| 					else => return self.report("expected identifier after `var` 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 = .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, | ||||
| 					}, | ||||
| 				}; | ||||
| 			}, | ||||
| 
 | ||||
| 			.keyword_if => return self.parse_branch(), | ||||
| 			else => return .{.expression = try self.parse_expression()}, | ||||
| 		} | ||||
|  | ||||
| @ -44,6 +44,8 @@ pub const Token = union(enum) { | ||||
| 	keyword_while, | ||||
| 	keyword_else, | ||||
| 	keyword_elif, | ||||
| 	keyword_var, | ||||
| 	keyword_let, | ||||
| 
 | ||||
| 	pub fn text(self: Token) []const coral.io.Byte { | ||||
| 		return switch (self) { | ||||
| @ -91,6 +93,8 @@ pub const Token = union(enum) { | ||||
| 			.keyword_while => "while", | ||||
| 			.keyword_elif => "elif", | ||||
| 			.keyword_else => "else", | ||||
| 			.keyword_var => "var", | ||||
| 			.keyword_let => "let", | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
| @ -229,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; | ||||
| @ -261,6 +273,14 @@ pub const Tokenizer = struct { | ||||
| 							} | ||||
| 						}, | ||||
| 
 | ||||
| 						'v' => { | ||||
| 							if (coral.io.ends_with(identifier, "ar")) { | ||||
| 								self.token = .keyword_var; | ||||
| 
 | ||||
| 								return; | ||||
| 							} | ||||
| 						}, | ||||
| 
 | ||||
| 						'w' => { | ||||
| 							if (coral.io.ends_with(identifier, "hile")) { | ||||
| 								self.token = .keyword_while; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user