Implement Control Flow Statements in Kym #37
| @ -1,12 +1,20 @@ | ||||
| 
 | ||||
| i = 0 | ||||
| 
 | ||||
| while i < 5 do | ||||
| while i < 5: | ||||
| 	@print("hello, world") | ||||
| 
 | ||||
| 	i = i + 1 | ||||
| end | ||||
| 
 | ||||
| if i > 6: | ||||
| 	@print("`i` greater than `6`") | ||||
| elif i == 4: | ||||
| 	@print("`i` is equal to `4`") | ||||
| else: | ||||
| 	@print("i'unno") | ||||
| end | ||||
| 
 | ||||
| return { | ||||
| 	.title = "Game", | ||||
| 	.width = 1280, | ||||
|  | ||||
| @ -36,6 +36,7 @@ pub const RuntimeEnv = struct { | ||||
| 		}; | ||||
| 
 | ||||
| 		const Opcode = union (enum) { | ||||
| 			pop, | ||||
| 			push_nil, | ||||
| 			push_true, | ||||
| 			push_false, | ||||
| @ -263,13 +264,19 @@ pub const RuntimeEnv = struct { | ||||
| 			fn compile_statement(self: *CompilationUnit, chunk: *Chunk, statement: ast.Statement) RuntimeError!void { | ||||
| 				switch (statement) { | ||||
| 					.@"return" => |@"return"| { | ||||
| 						if (@"return".expression) |expression| { | ||||
| 						if (@"return") |expression| { | ||||
| 							try self.compile_expression(chunk, expression); | ||||
| 						} else { | ||||
| 							try chunk.opcodes.push_one(.push_nil); | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.block => |block| { | ||||
| 						for (block.values) |block_statement| { | ||||
| 							try self.compile_statement(chunk, block_statement); | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.@"while" => |@"while"| { | ||||
| 						try self.compile_expression(chunk, @"while".condition_expression); | ||||
| 						try chunk.opcodes.push_one(.{.jf = 0}); | ||||
| @ -280,8 +287,6 @@ pub const RuntimeEnv = struct { | ||||
| 							try self.compile_statement(chunk, block_statement); | ||||
| 						} | ||||
| 
 | ||||
| 						coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int)); | ||||
| 
 | ||||
| 						chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1); | ||||
| 
 | ||||
| 						try self.compile_expression(chunk, @"while".condition_expression); | ||||
| @ -289,21 +294,42 @@ pub const RuntimeEnv = struct { | ||||
| 					}, | ||||
| 
 | ||||
| 					.@"if" => |@"if"| { | ||||
| 						try self.compile_expression(chunk, @"if".condition_expression); | ||||
| 						try self.compile_expression(chunk, @"if".then_block.condition_expression); | ||||
| 						try chunk.opcodes.push_one(.{.jf = 0}); | ||||
| 
 | ||||
| 						const origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1)); | ||||
| 						const then_origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1)); | ||||
| 
 | ||||
| 						for (@"if".block_statements.values) |block_statement| { | ||||
| 						for (@"if".then_block.block_statements.values) |block_statement| { | ||||
| 							try self.compile_statement(chunk, block_statement); | ||||
| 						} | ||||
| 
 | ||||
| 						coral.debug.assert(chunk.opcodes.values.len < coral.math.max_int(@typeInfo(u32).Int)); | ||||
| 						chunk.opcodes.values[then_origin_index].jf = @intCast(chunk.opcodes.values.len - 1); | ||||
| 
 | ||||
| 						chunk.opcodes.values[origin_index].jf = @intCast(chunk.opcodes.values.len - 1); | ||||
| 						if (@"if".else_block) |else_block| { | ||||
| 							if (else_block.condition_expression) |condition_expression| { | ||||
| 								try self.compile_expression(chunk, condition_expression); | ||||
| 								try chunk.opcodes.push_one(.{.jf = 0}); | ||||
| 							} | ||||
| 
 | ||||
| 							const else_origin_index = @as(u32, @intCast(chunk.opcodes.values.len - 1)); | ||||
| 
 | ||||
| 							for (else_block.block_statements.values) |block_statement| { | ||||
| 								try self.compile_statement(chunk, block_statement); | ||||
| 							} | ||||
| 
 | ||||
| 							if (else_block.condition_expression != null) { | ||||
| 								chunk.opcodes.values[else_origin_index].jf = @intCast(chunk.opcodes.values.len - 1); | ||||
| 							} | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.expression => |expression| try self.compile_expression(chunk, expression), | ||||
| 					.expression => |expression| { | ||||
| 						try self.compile_expression(chunk, expression); | ||||
| 
 | ||||
| 						if (expression == .invoke) { | ||||
| 							try chunk.opcodes.push_one(.pop); | ||||
| 						} | ||||
| 					}, | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -364,6 +390,12 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 			while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { | ||||
| 				switch (self.opcodes.values[opcode_cursor]) { | ||||
| 					.pop => { | ||||
| 						if (try self.pop_local()) |ref| { | ||||
| 							self.env.discard(ref); | ||||
| 						} | ||||
| 					}, | ||||
| 
 | ||||
| 					.push_nil => try self.env.locals.push_one(null), | ||||
| 					.push_true => try self.env.locals.push_one(try self.env.new_boolean(true)), | ||||
| 					.push_false => try self.env.locals.push_one(try self.env.new_boolean(false)), | ||||
| @ -855,6 +887,12 @@ pub const RuntimeEnv = struct { | ||||
| 						} | ||||
| 					}, | ||||
| 				} | ||||
| 
 | ||||
| 				// for (self.env.locals.values) |local| { | ||||
| 				// 	self.env.print(if (local) |ref| ref.typename() else "nil"); | ||||
| 				// } | ||||
| 
 | ||||
| 				// self.env.print("------------"); | ||||
| 			} | ||||
| 
 | ||||
| 			return self.pop_local(); | ||||
|  | ||||
| @ -137,12 +137,19 @@ pub const ParseError = error { | ||||
| }; | ||||
| 
 | ||||
| pub const Statement = union (enum) { | ||||
| 	@"return": struct { | ||||
| 		expression: ?Expression, | ||||
| 	@"return": ?Expression, | ||||
| 
 | ||||
| 	@"if": struct { | ||||
| 		then_block: ConditionalBlock, | ||||
| 
 | ||||
| 		else_block: ?struct { | ||||
| 			condition_expression: ?Expression, | ||||
| 			block_statements: StatementList, | ||||
| 		}, | ||||
| 	}, | ||||
| 
 | ||||
| 	@"if": ConditionalBlock, | ||||
| 	@"while": ConditionalBlock, | ||||
| 	block: StatementList, | ||||
| 	expression: Expression, | ||||
| 
 | ||||
| 	const ConditionalBlock = struct { | ||||
| @ -527,14 +534,15 @@ pub const Tree = struct { | ||||
| 	} | ||||
| 
 | ||||
| 	fn parse_statements(self: *Tree) ParseError!StatementList { | ||||
| 		var statements = StatementList.make(self.arena.as_allocator()); | ||||
| 		const allocator = self.arena.as_allocator(); | ||||
| 		var statements = StatementList.make(allocator); | ||||
| 
 | ||||
| 		self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 		while (true) { | ||||
| 			try statements.push_one(parse_statement: { | ||||
| 				switch (self.tokenizer.token) { | ||||
| 					.end, .keyword_end => return statements, | ||||
| 					.end, .keyword_end, .keyword_else, .keyword_elif => return statements, | ||||
| 
 | ||||
| 					.keyword_return => { | ||||
| 						if (self.has_returned) { | ||||
| @ -544,7 +552,7 @@ pub const Tree = struct { | ||||
| 						self.tokenizer.step(); | ||||
| 
 | ||||
| 						if (self.tokenizer.token != .end and self.tokenizer.token != .newline) { | ||||
| 							break: parse_statement .{.@"return" = .{.expression = try self.parse_expression()}}; | ||||
| 							break: parse_statement .{.@"return" = try self.parse_expression()}; | ||||
| 						} | ||||
| 
 | ||||
| 						if (self.tokenizer.token != .end and self.tokenizer.token != .newline) { | ||||
| @ -553,7 +561,7 @@ pub const Tree = struct { | ||||
| 
 | ||||
| 						self.has_returned = true; | ||||
| 
 | ||||
| 						break: parse_statement .{.@"return" = .{.expression = null}}; | ||||
| 						break: parse_statement .{.@"return" = null}; | ||||
| 					}, | ||||
| 
 | ||||
| 					.keyword_while => { | ||||
| @ -561,12 +569,10 @@ pub const Tree = struct { | ||||
| 
 | ||||
| 						const condition_expression = try self.parse_expression(); | ||||
| 
 | ||||
| 						if (self.tokenizer.token != .keyword_do) { | ||||
| 							return self.report("expected `do` block after `while` statement"); | ||||
| 						if (self.tokenizer.token != .symbol_colon) { | ||||
| 							return self.report("expected `:` after `while` statement"); | ||||
| 						} | ||||
| 
 | ||||
| 						self.tokenizer.step(); | ||||
| 
 | ||||
| 						const while_statement = Statement{ | ||||
| 							.@"while" = .{ | ||||
| 								.block_statements = try self.parse_statements(), | ||||
| @ -586,23 +592,55 @@ pub const Tree = struct { | ||||
| 					.keyword_if => { | ||||
| 						self.tokenizer.step(); | ||||
| 
 | ||||
| 						const condition_expression = try self.parse_expression(); | ||||
| 						const then_condition_expression = try self.parse_expression(); | ||||
| 
 | ||||
| 						if (self.tokenizer.token != .keyword_do) { | ||||
| 							return self.report("expected `do` block after `if` statement"); | ||||
| 						if (self.tokenizer.token != .symbol_colon) { | ||||
| 							return self.report("expected `:` after `if` statement condition"); | ||||
| 						} | ||||
| 
 | ||||
| 						self.tokenizer.step(); | ||||
| 
 | ||||
| 						const if_statement = Statement{ | ||||
| 						var if_statement = Statement{ | ||||
| 							.@"if" = .{ | ||||
| 								.block_statements = try self.parse_statements(), | ||||
| 								.condition_expression = condition_expression, | ||||
| 								.then_block = .{ | ||||
| 									.block_statements = try self.parse_statements(), | ||||
| 									.condition_expression = then_condition_expression, | ||||
| 								}, | ||||
| 
 | ||||
| 								.else_block = null, | ||||
| 							}, | ||||
| 						}; | ||||
| 
 | ||||
| 						if (self.tokenizer.token != .keyword_end) { | ||||
| 							return self.report("expected `end` after block"); | ||||
| 						switch (self.tokenizer.token) { | ||||
| 							.keyword_end => {}, | ||||
| 
 | ||||
| 							.keyword_else => { | ||||
| 								self.tokenizer.step(); | ||||
| 
 | ||||
| 								if (self.tokenizer.token != .symbol_colon) { | ||||
| 									return self.report("expected newline after `else` statement"); | ||||
| 								} | ||||
| 
 | ||||
| 								if_statement.@"if".else_block = .{ | ||||
| 									.condition_expression = null, | ||||
| 									.block_statements = try self.parse_statements(), | ||||
| 								}; | ||||
| 							}, | ||||
| 
 | ||||
| 							.keyword_elif => { | ||||
| 								self.tokenizer.step(); | ||||
| 
 | ||||
| 								const else_condition_expression = try self.parse_expression(); | ||||
| 
 | ||||
| 								if (self.tokenizer.token != .symbol_colon) { | ||||
| 									return self.report("expected newline after `elif` statement condition"); | ||||
| 								} | ||||
| 
 | ||||
| 								if_statement.@"if".else_block = .{ | ||||
| 									.condition_expression = else_condition_expression, | ||||
| 									.block_statements = try self.parse_statements(), | ||||
| 								}; | ||||
| 							}, | ||||
| 
 | ||||
| 							else => return self.report("expected closing `end`, `elif`, or `else` statement on if block"), | ||||
| 						} | ||||
| 
 | ||||
| 						self.tokenizer.skip_newlines(); | ||||
|  | ||||
| @ -21,7 +21,7 @@ pub const Token = union(enum) { | ||||
| 	symbol_bracket_left, | ||||
| 	symbol_bracket_right, | ||||
| 	symbol_period, | ||||
| 	symbol_lambda, | ||||
| 	symbol_colon, | ||||
| 	symbol_less_than, | ||||
| 	symbol_less_equals, | ||||
| 	symbol_greater_than, | ||||
| @ -42,6 +42,8 @@ pub const Token = union(enum) { | ||||
| 	keyword_do, | ||||
| 	keyword_end, | ||||
| 	keyword_while, | ||||
| 	keyword_else, | ||||
| 	keyword_elif, | ||||
| 
 | ||||
| 	pub fn text(self: Token) []const coral.io.Byte { | ||||
| 		return switch (self) { | ||||
| @ -66,7 +68,7 @@ pub const Token = union(enum) { | ||||
| 			.symbol_bracket_left => "[", | ||||
| 			.symbol_bracket_right => "]", | ||||
| 			.symbol_period => ".", | ||||
| 			.symbol_lambda => "=>", | ||||
| 			.symbol_colon => ":", | ||||
| 			.symbol_less_than => "<", | ||||
| 			.symbol_less_equals => "<=", | ||||
| 			.symbol_greater_than => ">", | ||||
| @ -87,6 +89,8 @@ pub const Token = union(enum) { | ||||
| 			.keyword_do => "do", | ||||
| 			.keyword_end => "end", | ||||
| 			.keyword_while => "while", | ||||
| 			.keyword_elif => "elif", | ||||
| 			.keyword_else => "else", | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
| @ -190,6 +194,18 @@ pub const Tokenizer = struct { | ||||
| 						}, | ||||
| 
 | ||||
| 						'e' => { | ||||
| 							if (coral.io.ends_with(identifier, "lse")) { | ||||
| 								self.token = .keyword_else; | ||||
| 
 | ||||
| 								return; | ||||
| 							} | ||||
| 
 | ||||
| 							if (coral.io.ends_with(identifier, "lif")) { | ||||
| 								self.token = .keyword_elif; | ||||
| 
 | ||||
| 								return; | ||||
| 							} | ||||
| 
 | ||||
| 							if (coral.io.ends_with(identifier, "nd")) { | ||||
| 								self.token = .keyword_end; | ||||
| 
 | ||||
| @ -378,6 +394,13 @@ pub const Tokenizer = struct { | ||||
| 					return; | ||||
| 				}, | ||||
| 
 | ||||
| 				':' => { | ||||
| 					self.token = .symbol_colon; | ||||
| 					cursor += 1; | ||||
| 
 | ||||
| 					return; | ||||
| 				}, | ||||
| 
 | ||||
| 				'=' => { | ||||
| 					cursor += 1; | ||||
| 
 | ||||
| @ -390,13 +413,6 @@ pub const Tokenizer = struct { | ||||
| 								return; | ||||
| 							}, | ||||
| 
 | ||||
| 							'>' => { | ||||
| 								cursor += 1; | ||||
| 								self.token = .symbol_lambda; | ||||
| 
 | ||||
| 								return; | ||||
| 							}, | ||||
| 
 | ||||
| 							else => {}, | ||||
| 						} | ||||
| 					} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user