From 83fa162ba8cc067447a44e5ad4b0b20442311221 Mon Sep 17 00:00:00 2001 From: kayomn Date: Fri, 25 Aug 2023 23:40:17 +0100 Subject: [PATCH 1/2] Make local declarations require "var" prefix --- debug/app.ona | 2 +- source/ona/kym.zig | 26 ++++++++++++++++---------- source/ona/kym/ast.zig | 29 +++++++++++++++++++++++++++++ source/ona/kym/tokens.zig | 10 ++++++++++ 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index f787fc7..48b8ee8 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,5 +1,5 @@ -i = 0 +var i = 0 while i < 5: @print("hello, world") diff --git a/source/ona/kym.zig b/source/ona/kym.zig index 59803b9..c30c806 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -213,16 +213,11 @@ pub const RuntimeEnv = struct { .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"); - } - - self.local_identifiers_buffer[self.local_identifiers_count] = local_set.identifier; - self.local_identifiers_count += 1; - } + try chunk.opcodes.push_one(.{ + .local_set = self.resolve_local(local_set.identifier) orelse { + return chunk.env.raise(error.BadSyntax, "undefined local"); + }, + }); }, .field_get => |field_get| { @@ -271,6 +266,17 @@ pub const RuntimeEnv = struct { } }, + .local_var => |local_var| { + try self.compile_expression(chunk, local_var.assigned_expression); + + if (self.local_identifiers_count == self.local_identifiers_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; + }, + .block => |block| { for (block.values) |block_statement| { try self.compile_statement(chunk, block_statement); diff --git a/source/ona/kym/ast.zig b/source/ona/kym/ast.zig index 9afb040..dc347a3 100644 --- a/source/ona/kym/ast.zig +++ b/source/ona/kym/ast.zig @@ -139,6 +139,11 @@ pub const ParseError = error { pub const Statement = union (enum) { @"return": ?Expression, + local_var: struct { + identifier: []const coral.io.Byte, + assigned_expression: Expression, + }, + @"if": struct { condition_expression: Expression, block_statements: List, @@ -650,6 +655,30 @@ 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 .{ + .local_var = .{ + .assigned_expression = try self.parse_expression(), + .identifier = identifier, + }, + }; + }, + .keyword_if => return self.parse_branch(), else => return .{.expression = try self.parse_expression()}, } diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index 3869cf1..2ff609d 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -44,6 +44,7 @@ pub const Token = union(enum) { keyword_while, keyword_else, keyword_elif, + keyword_var, pub fn text(self: Token) []const coral.io.Byte { return switch (self) { @@ -91,6 +92,7 @@ pub const Token = union(enum) { .keyword_while => "while", .keyword_elif => "elif", .keyword_else => "else", + .keyword_var => "var", }; } }; @@ -261,6 +263,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; -- 2.34.1 From 960d022d031533879f388fbc51c64268942b90be Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 26 Aug 2023 00:11:37 +0100 Subject: [PATCH 2/2] 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; -- 2.34.1