Add Function Literal Syntax to Ona Script #39
| @ -1,18 +1,22 @@ | ||||
| 
 | ||||
| var i = 0 | ||||
| 
 | ||||
| let pr = lambda (): | ||||
| 	@print("foo") | ||||
| end | ||||
| 
 | ||||
| while i < 5: | ||||
| 	@print("hello, world") | ||||
| 	pr("hello, world") | ||||
| 
 | ||||
| 	i = i + 1 | ||||
| end | ||||
| 
 | ||||
| if i > 6: | ||||
| 	@print("`i` greater than `6`") | ||||
| 	pr("`i` greater than `6`") | ||||
| elif i == 4: | ||||
| 	@print("`i` is equal to `4`") | ||||
| 	pr("`i` is equal to `4`") | ||||
| else: | ||||
| 	@print("i'unno") | ||||
| 	pr("i'unno") | ||||
| end | ||||
| 
 | ||||
| return { | ||||
|  | ||||
| @ -33,6 +33,7 @@ pub const RuntimeEnv = struct { | ||||
| 			float: Float, | ||||
| 			string: []const coral.io.Byte, | ||||
| 			symbol: []const coral.io.Byte, | ||||
| 			chunk: Chunk, | ||||
| 		}; | ||||
| 
 | ||||
| 		const Opcode = union (enum) { | ||||
| @ -124,17 +125,35 @@ pub const RuntimeEnv = struct { | ||||
| 						}); | ||||
| 					}, | ||||
| 
 | ||||
| 					.table_literal => |fields| { | ||||
| 						if (fields.values.len > coral.math.max_int(@typeInfo(u32).Int)) { | ||||
| 					.table_literal => |literal| { | ||||
| 						if (literal.values.len > coral.math.max_int(@typeInfo(u32).Int)) { | ||||
| 							return error.OutOfMemory; | ||||
| 						} | ||||
| 
 | ||||
| 						for (fields.values) |field| { | ||||
| 						for (literal.values) |field| { | ||||
| 							try self.compile_expression(chunk, field.value_expression); | ||||
| 							try self.compile_expression(chunk, field.key_expression); | ||||
| 						} | ||||
| 
 | ||||
| 						try chunk.opcodes.push_one(.{.push_table = @intCast(fields.values.len)}); | ||||
| 						try chunk.opcodes.push_one(.{.push_table = @intCast(literal.values.len)}); | ||||
| 					}, | ||||
| 
 | ||||
| 					.lambda_literal => |literal| { | ||||
| 						if (literal.argument_identifiers.values.len > coral.math.max_int(@typeInfo(u32).Int)) { | ||||
| 							return error.OutOfMemory; | ||||
| 						} | ||||
| 
 | ||||
| 						var lambda_chunk = try Chunk.make(chunk.env, "<lambda>"); | ||||
| 
 | ||||
| 						errdefer lambda_chunk.free(); | ||||
| 
 | ||||
| 						try lambda_chunk.compile(literal.block_statements.values); | ||||
| 
 | ||||
| 						try chunk.opcodes.push_one(.{ | ||||
| 							.push_const = try chunk.declare_constant(.{ | ||||
| 								.chunk = lambda_chunk, | ||||
| 							}), | ||||
| 						}); | ||||
| 					}, | ||||
| 
 | ||||
| 					.binary_operation => |operation| { | ||||
| @ -374,9 +393,18 @@ pub const RuntimeEnv = struct { | ||||
| 
 | ||||
| 		fn compile(self: *Chunk, statements: []const ast.Statement) RuntimeError!void { | ||||
| 			var unit = CompilationUnit{}; | ||||
| 			var has_returned = false; | ||||
| 
 | ||||
| 			for (statements) |statement| { | ||||
| 				try unit.compile_statement(self, statement); | ||||
| 
 | ||||
| 				if (statement == .@"return") { | ||||
| 					has_returned = true; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			if (!has_returned) { | ||||
| 				try self.opcodes.push_one(.push_nil); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -385,27 +413,27 @@ pub const RuntimeEnv = struct { | ||||
| 				return self.env.raise(error.BadSyntax, "chunks cannot contain more than 65,535 constants"); | ||||
| 			} | ||||
| 
 | ||||
| 			const constant_index = self.constants.values.len; | ||||
| 
 | ||||
| 			try self.constants.push_one(try switch (constant) { | ||||
| 			const constant_object = try switch (constant) { | ||||
| 				.fixed => |fixed| self.env.new_fixed(fixed), | ||||
| 				.float => |float| self.env.new_float(float), | ||||
| 				.string => |string| self.env.new_string(string), | ||||
| 				.symbol => |symbol| self.env.new_symbol(symbol), | ||||
| 			}); | ||||
| 
 | ||||
| 			return @intCast(constant_index); | ||||
| 				.chunk => |chunk| self.env.new_dynamic(coral.io.bytes_of(&chunk).ptr, &.{ | ||||
| 					.size = @sizeOf(Chunk), | ||||
| 					.name = "lambda", | ||||
| 					.destruct = typeinfo_destruct, | ||||
| 					.call = typeinfo_call, | ||||
| 				}), | ||||
| 			}; | ||||
| 
 | ||||
| 			errdefer self.env.discard(constant_object); | ||||
| 			try self.constants.push_one(constant_object); | ||||
| 
 | ||||
| 			return @intCast(self.constants.values.len - 1); | ||||
| 		} | ||||
| 
 | ||||
| 		fn execute(self: *Chunk) RuntimeError!?*RuntimeRef { | ||||
| 			try self.env.frames.push_one(.{ | ||||
| 				.arg_count = 0, | ||||
| 				.locals_top = self.env.locals.values.len, | ||||
| 				.name = self.name, | ||||
| 			}); | ||||
| 
 | ||||
| 			defer coral.debug.assert(self.env.frames.pop() != null); | ||||
| 
 | ||||
| 			var opcode_cursor = @as(u32, 0); | ||||
| 
 | ||||
| 			while (opcode_cursor < self.opcodes.values.len) : (opcode_cursor += 1) { | ||||
| @ -547,14 +575,16 @@ pub const RuntimeEnv = struct { | ||||
| 							defer self.env.discard(callable); | ||||
| 
 | ||||
| 							try self.env.frames.push_one(.{ | ||||
| 								.name = "<lambda>", | ||||
| 								.name = "<chunk>", | ||||
| 								.arg_count = object_call, | ||||
| 								.locals_top = self.env.locals.values.len, | ||||
| 							}); | ||||
| 
 | ||||
| 							defer coral.debug.assert(self.env.frames.pop() != null); | ||||
| 
 | ||||
| 							break: call try switch (callable.object().payload) { | ||||
| 							const payload = callable.object().payload; | ||||
| 
 | ||||
| 							break: call try switch (payload) { | ||||
| 								.syscall => |syscall| syscall(self.env), | ||||
| 
 | ||||
| 								.dynamic => |dynamic| dynamic.typeinfo().call(.{ | ||||
| @ -566,18 +596,18 @@ pub const RuntimeEnv = struct { | ||||
| 							}; | ||||
| 						}; | ||||
| 
 | ||||
| 						for (0 .. object_call) |_| { | ||||
| 							if (try self.pop_local()) |popped_arg| { | ||||
| 								self.env.discard(popped_arg); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						errdefer { | ||||
| 							if (result) |ref| { | ||||
| 								self.env.discard(ref); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						for (0 .. object_call) |_| { | ||||
| 							if (try self.pop_local()) |popped_arg| { | ||||
| 								self.env.discard(popped_arg); | ||||
| 							} | ||||
| 						} | ||||
| 
 | ||||
| 						try self.env.locals.push_one(result); | ||||
| 					}, | ||||
| 
 | ||||
| @ -934,6 +964,14 @@ pub const RuntimeEnv = struct { | ||||
| 		fn pop_local(self: *Chunk) RuntimeError!?*RuntimeRef { | ||||
| 			return self.env.locals.pop() orelse self.env.raise(error.IllegalState, "stack underflow"); | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_call(method: Typeinfo.Method) RuntimeError!?*RuntimeRef { | ||||
| 			return @as(*Chunk, @ptrCast(@alignCast(method.userdata))).execute(); | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_destruct(method: Typeinfo.Method) void { | ||||
| 			@as(*Chunk, @ptrCast(@alignCast(method.userdata))).free(); | ||||
| 		} | ||||
| 	}; | ||||
| 
 | ||||
| 	const ConstList = coral.list.Stack(*RuntimeRef); | ||||
| @ -967,7 +1005,7 @@ pub const RuntimeEnv = struct { | ||||
| 		contiguous: RefList, | ||||
| 
 | ||||
| 		fn typeinfo_destruct(method: Typeinfo.Method) void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr))); | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 
 | ||||
| 			{ | ||||
| 				var field_iterable = table.associative.as_iterable(); | ||||
| @ -990,7 +1028,7 @@ pub const RuntimeEnv = struct { | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_get(method: Typeinfo.Method, index: *const RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr))); | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 			const acquired_index = try method.env.acquire(index); | ||||
| 
 | ||||
| 			defer method.env.discard(acquired_index); | ||||
| @ -1014,7 +1052,7 @@ pub const RuntimeEnv = struct { | ||||
| 		} | ||||
| 
 | ||||
| 		fn typeinfo_set(method: Typeinfo.Method, index: *const RuntimeRef, value: ?*const RuntimeRef) RuntimeError!void { | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata.ptr))); | ||||
| 			const table = @as(*Table, @ptrCast(@alignCast(method.userdata))); | ||||
| 			const acquired_index = try method.env.acquire(index); | ||||
| 
 | ||||
| 			errdefer method.env.discard(acquired_index); | ||||
| @ -1079,19 +1117,14 @@ pub const RuntimeEnv = struct { | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn bind_system(self: *RuntimeEnv, name: []const coral.io.Byte, value: *const RuntimeRef) RuntimeError!void { | ||||
| 		const name_symbol = try self.new_symbol(name); | ||||
| 	pub fn acquire_args(self: *RuntimeEnv) RuntimeError!*RuntimeRef { | ||||
| 		const frame = self.frames.peek() orelse return self.raise(error.IllegalState, "stack underflow"); | ||||
| 
 | ||||
| 		errdefer self.discard(name_symbol); | ||||
| 		_ = frame; | ||||
| 
 | ||||
| 		const acquired_value = try self.acquire(value); | ||||
| 		const args_table = self.new_table(); | ||||
| 
 | ||||
| 		errdefer self.discard(acquired_value); | ||||
| 
 | ||||
| 		if (try self.system_bindings.replace(name_symbol, acquired_value)) |replaced| { | ||||
| 			self.discard(replaced.key); | ||||
| 			self.discard(replaced.value); | ||||
| 		} | ||||
| 		return args_table; | ||||
| 	} | ||||
| 
 | ||||
| 	pub fn call(self: *RuntimeEnv, callable: *RuntimeRef, args: []const *RuntimeRef) RuntimeError!?*RuntimeRef { | ||||
| @ -1172,6 +1205,14 @@ pub const RuntimeEnv = struct { | ||||
| 			error.OutOfMemory => error.OutOfMemory, | ||||
| 		}); | ||||
| 
 | ||||
| 		try self.frames.push_one(.{ | ||||
| 			.name = file_name, | ||||
| 			.arg_count = 0, | ||||
| 			.locals_top = self.locals.values.len, | ||||
| 		}); | ||||
| 
 | ||||
| 		defer coral.debug.assert(self.frames.pop() != null); | ||||
| 
 | ||||
| 		return chunk.execute(); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -74,6 +74,11 @@ pub const Expression = union (enum) { | ||||
| 	table_literal: TableLiteral, | ||||
| 	grouped_expression: *Expression, | ||||
| 
 | ||||
| 	lambda_literal: struct { | ||||
| 		argument_identifiers: IdentifierList, | ||||
| 		block_statements: Statement.List, | ||||
| 	}, | ||||
| 
 | ||||
| 	local_get: struct { | ||||
| 		identifier: []const coral.io.Byte, | ||||
| 	}, | ||||
| @ -136,6 +141,8 @@ pub const DeclarationStorage = enum { | ||||
| 
 | ||||
| const ExpressionBuilder = fn (self: *Tree) ParseError!Expression; | ||||
| 
 | ||||
| const IdentifierList = coral.list.Stack([]const coral.io.Byte); | ||||
| 
 | ||||
| pub const ParseError = error { | ||||
| 	OutOfMemory, | ||||
| 	BadSyntax, | ||||
| @ -221,8 +228,6 @@ pub const Tree = struct { | ||||
| 	fn parse_branch(self: *Tree) ParseError!Statement { | ||||
| 		const allocator = self.arena.as_allocator(); | ||||
| 
 | ||||
| 		defer self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 		self.tokenizer.step(); | ||||
| 
 | ||||
| 		const condition_expression = try self.parse_expression(); | ||||
| @ -238,6 +243,8 @@ pub const Tree = struct { | ||||
| 		while (true) { | ||||
| 			switch (self.tokenizer.token) { | ||||
| 				.keyword_end => { | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					return .{ | ||||
| 						.@"if" = .{ | ||||
| 							.condition_expression = condition_expression, | ||||
| @ -262,6 +269,8 @@ pub const Tree = struct { | ||||
| 						try else_statements.push_one(try self.parse_statement()); | ||||
| 					} | ||||
| 
 | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					return .{ | ||||
| 						.@"if" = .{ | ||||
| 							.else_statement = try coral.io.allocate_one(allocator, Statement{.block = else_statements}), | ||||
| @ -404,6 +413,57 @@ pub const Tree = struct { | ||||
| 					break: parse .{.builtin = builtin}; | ||||
| 				}, | ||||
| 
 | ||||
| 				.keyword_lambda => { | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					if (self.tokenizer.token != .symbol_paren_left) { | ||||
| 						return self.report("expected `(` after opening lambda block"); | ||||
| 					} | ||||
| 
 | ||||
| 					var argument_identifiers = IdentifierList.make(allocator); | ||||
| 
 | ||||
| 					while (true) { | ||||
| 						self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 						switch (self.tokenizer.token) { | ||||
| 							.identifier => |identifier| try argument_identifiers.push_one(identifier), | ||||
| 							.symbol_paren_right => break, | ||||
| 							else => return self.report("expected identifier or closing `)` in argument list"), | ||||
| 						} | ||||
| 
 | ||||
| 						self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 						switch (self.tokenizer.token) { | ||||
| 							.symbol_comma => continue, | ||||
| 							.symbol_paren_right => break, | ||||
| 							else => return self.report("expected `,` or closing `)` after identifier in argument list"), | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					if (self.tokenizer.token != .symbol_colon) { | ||||
| 						return self.report("expected `:` after closing `)` of lambda block argument list"); | ||||
| 					} | ||||
| 
 | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					var block_statements = Statement.List.make(allocator); | ||||
| 
 | ||||
| 					while (self.tokenizer.token != .keyword_end) { | ||||
| 						try block_statements.push_one(try self.parse_statement()); | ||||
| 					} | ||||
| 
 | ||||
| 					self.tokenizer.skip_newlines(); | ||||
| 
 | ||||
| 					break: parse .{ | ||||
| 						.lambda_literal = .{ | ||||
| 							.argument_identifiers = argument_identifiers, | ||||
| 							.block_statements = block_statements, | ||||
| 						}, | ||||
| 					}; | ||||
| 				}, | ||||
| 
 | ||||
| 				.symbol_brace_left => { | ||||
| 					var table_literal = Expression.TableLiteral.make(allocator); | ||||
| 
 | ||||
|  | ||||
| @ -46,6 +46,7 @@ pub const Token = union(enum) { | ||||
| 	keyword_elif, | ||||
| 	keyword_var, | ||||
| 	keyword_let, | ||||
| 	keyword_lambda, | ||||
| 
 | ||||
| 	pub fn text(self: Token) []const coral.io.Byte { | ||||
| 		return switch (self) { | ||||
| @ -95,6 +96,7 @@ pub const Token = union(enum) { | ||||
| 			.keyword_else => "else", | ||||
| 			.keyword_var => "var", | ||||
| 			.keyword_let => "let", | ||||
| 			.keyword_lambda => "lambda", | ||||
| 		}; | ||||
| 	} | ||||
| }; | ||||
| @ -234,6 +236,12 @@ pub const Tokenizer = struct { | ||||
| 						}, | ||||
| 
 | ||||
| 						'l' => { | ||||
| 							if (coral.io.ends_with(identifier, "ambda")) { | ||||
| 								self.token = .keyword_lambda; | ||||
| 
 | ||||
| 								return; | ||||
| 							} | ||||
| 
 | ||||
| 							if (coral.io.ends_with(identifier, "et")) { | ||||
| 								self.token = .keyword_let; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user