From 88ff2a64b66e83e0c4d8400ed23b7e4670e46a68 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 4 Nov 2023 13:41:39 +0000 Subject: [PATCH] Make arguments read-only --- debug/app.ona | 4 +-- source/ona/kym/Chunk.zig | 10 ++++-- source/ona/kym/Compiler.zig | 16 +++++---- source/ona/kym/Expr.zig | 34 +++++++----------- source/ona/kym/Stmt.zig | 37 +++++++++++++------ source/ona/kym/tree.zig | 71 ++++++++++++++++++++++++++++++++----- 6 files changed, 118 insertions(+), 54 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index 9407ffe..1cea8e2 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,9 +1,7 @@ let printer = lambda (pfx): - let prefix = pfx - return lambda (msg): - @print(prefix) + @print(pfx) @print(msg) end end diff --git a/source/ona/kym/Chunk.zig b/source/ona/kym/Chunk.zig index 758fb9e..a127f2e 100644 --- a/source/ona/kym/Chunk.zig +++ b/source/ona/kym/Chunk.zig @@ -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); diff --git a/source/ona/kym/Compiler.zig b/source/ona/kym/Compiler.zig index 6fb8321..ed2dcfb 100644 --- a/source/ona/kym/Compiler.zig +++ b/source/ona/kym/Compiler.zig @@ -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; +} diff --git a/source/ona/kym/Expr.zig b/source/ona/kym/Expr.zig index 799013a..5a84539 100644 --- a/source/ona/kym/Expr.zig +++ b/source/ona/kym/Expr.zig @@ -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(); diff --git a/source/ona/kym/Stmt.zig b/source/ona/kym/Stmt.zig index 158d21e..ae8f226 100644 --- a/source/ona/kym/Stmt.zig +++ b/source/ona/kym/Stmt.zig @@ -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, + }), + }; + }; }, }, }, diff --git a/source/ona/kym/tree.zig b/source/ona/kym/tree.zig index 9421e17..1221899 100644 --- a/source/ona/kym/tree.zig +++ b/source/ona/kym/tree.zig @@ -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); }