From 960d022d031533879f388fbc51c64268942b90be Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 26 Aug 2023 00:11:37 +0100 Subject: [PATCH] Add read-only storage class for locals --- source/ona/kym.zig | 65 +++++++++++++++++++++++++++------------ source/ona/kym/ast.zig | 36 ++++++++++++++++++++-- source/ona/kym/tokens.zig | 10 ++++++ 3 files changed, 90 insertions(+), 21 deletions(-) diff --git a/source/ona/kym.zig b/source/ona/kym.zig index c30c806..5415f5b 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -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) { diff --git a/source/ona/kym/ast.zig b/source/ona/kym/ast.zig index dc347a3..c95bce2 100644 --- a/source/ona/kym/ast.zig +++ b/source/ona/kym/ast.zig @@ -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, }, }; diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index 2ff609d..3f1aa53 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -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;