From b311c73c438ffdf579a40842897feceed2ece172 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 22 Jul 2023 14:44:33 +0100 Subject: [PATCH 1/4] Add syntax error reporting --- debug/app.ona | 1 + source/coral/list.zig | 4 +- source/coral/math.zig | 6 ++- source/ona/kym.zig | 4 +- source/ona/kym/Ast.zig | 87 ++++++++++++++++++++++++------------------ source/ona/ona.zig | 6 +++ 6 files changed, 66 insertions(+), 42 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index 37c0243..7624b01 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,4 +1,5 @@ +// Test comment. @log_info("game is loading") return { diff --git a/source/coral/list.zig b/source/coral/list.zig index f6915c9..f02b6ee 100644 --- a/source/coral/list.zig +++ b/source/coral/list.zig @@ -118,5 +118,7 @@ pub fn stack_as_writer(self: *ByteStack) io.Writer { } fn write_stack(stack: *ByteStack, bytes: []const io.Byte) ?usize { - return stack.push_all(bytes) catch null; + stack.push_all(bytes) catch return null; + + return bytes.len; } diff --git a/source/coral/math.zig b/source/coral/math.zig index d5883cf..de8d37f 100644 --- a/source/coral/math.zig +++ b/source/coral/math.zig @@ -30,13 +30,15 @@ pub fn max_int(comptime int: std.builtin.Type.Int) comptime_int { } pub fn min_int(comptime int: std.builtin.Type.Int) comptime_int { - if (int.signedness == .unsigned) return 0; + if (int.signedness == .unsigned) { + return 0; + } const bit_count = int.bits; if (bit_count == 0) return 0; - return -(1 << (bit_count - 1)); + return -(1 << (bit_count - 1)); } pub fn wrap(value: anytype, lower: anytype, upper: anytype) @TypeOf(value, lower, upper) { diff --git a/source/ona/kym.zig b/source/ona/kym.zig index f21b08f..f4a3df6 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -200,7 +200,7 @@ pub const RuntimeEnv = struct { name: []const coral.io.Byte, data: []const coral.io.Byte, ) RuntimeError!?*RuntimeRef { - var ast = Ast.make(self.allocator); + var ast = Ast.make(self.allocator, name); defer ast.free(); @@ -208,7 +208,7 @@ pub const RuntimeEnv = struct { var tokenizer = tokens.Tokenizer{.source = data}; ast.parse(&tokenizer) catch |parse_error| switch (parse_error) { - error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message), + error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message()), error.OutOfMemory => return error.OutOfMemory, }; } diff --git a/source/ona/kym/Ast.zig b/source/ona/kym/Ast.zig index cb2855c..10a6e45 100755 --- a/source/ona/kym/Ast.zig +++ b/source/ona/kym/Ast.zig @@ -2,10 +2,11 @@ const coral = @import("coral"); const tokens = @import("./tokens.zig"); +name: []const coral.io.Byte, allocator: coral.io.Allocator, arena: coral.arena.Stacking, statements: Statement.List, -error_message: []const coral.io.Byte, +error_buffer: coral.list.ByteStack, pub const Expression = union (enum) { nil_literal, @@ -114,7 +115,7 @@ fn binary_operation_parser( tokenizer.step(); if (tokenizer.token == null) { - return self.raise("expected other half of expression after `" ++ comptime token.text() ++ "`"); + return self.report(tokenizer, "expected other half of expression after `" ++ comptime token.text() ++ "`"); } expression = .{ @@ -134,22 +135,32 @@ fn binary_operation_parser( return BinaryOperationParser.parse; } +pub fn error_message(self: Self) []const coral.io.Byte { + return self.error_buffer.values; +} + pub fn free(self: *Self) void { self.arena.free(); self.statements.free(); + self.error_buffer.free(); } -pub fn make(allocator: coral.io.Allocator) Self { +pub fn make(allocator: coral.io.Allocator, ast_name: []const coral.io.Byte) Self { return Self{ .arena = coral.arena.Stacking.make(allocator, 4096), - .allocator = allocator, + .error_buffer = coral.list.ByteStack.make(allocator), .statements = Statement.List.make(allocator), - .error_message = "", + .allocator = allocator, + .name = ast_name, }; } -fn raise(self: *Self, message: []const u8) ParseError { - self.error_message = message; +fn report(self: *Self, tokenizer: *tokens.Tokenizer, message: []const coral.io.Byte) ParseError { + coral.utf8.print_formatted(coral.list.stack_as_writer(&self.error_buffer), "{name}@{line}: {message}", .{ + .name = self.name, + .line = tokenizer.lines_stepped, + .message = message, + }) catch return error.OutOfMemory; return error.BadSyntax; } @@ -165,14 +176,12 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { var has_returned = false; while (true) { - const no_effect_message = "statement has no effect"; - tokenizer.skip(.newline); switch (tokenizer.token orelse return) { .keyword_return => { if (has_returned) { - return self.raise("multiple returns in function scope but expected only one"); + return self.report(tokenizer, "multiple returns in function scope but expected only one"); } try self.statements.push_one(get_statement: { @@ -183,7 +192,7 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { } if (!tokenizer.is_token_null_or(.newline)) { - return self.raise("unexpected token after return"); + return self.report(tokenizer, "unexpected token after return"); } break: get_statement .return_nothing; @@ -195,14 +204,16 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { .identifier => |identifier| { tokenizer.step(); - switch (tokenizer.token orelse return self.raise(no_effect_message)) { - .newline => return self.raise(no_effect_message), + const no_effect_message = "statement has no effect"; + + switch (tokenizer.token orelse return self.report(tokenizer, no_effect_message)) { + .newline => return self.report(tokenizer, no_effect_message), .symbol_equals => { tokenizer.step(); if (tokenizer.token == null) { - return self.raise("expected expression after `=`"); + return self.report(tokenizer, "expected expression after `=`"); } try self.statements.push_one(.{ @@ -213,19 +224,21 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { }); if (!tokenizer.is_token_null_or(.newline)) { - return self.raise("unexpected token after assignment"); + return self.report(tokenizer, "unexpected token after assignment"); } }, - else => return self.raise("expected `=` after local"), + else => return self.report(tokenizer, "expected `=` after local"), } }, .special_identifier => |identifier| { tokenizer.step(); - switch (tokenizer.token orelse return self.raise(no_effect_message)) { - .newline => return self.raise(no_effect_message), + const missing_arguments_message = "system call is missing arguments"; + + switch (tokenizer.token orelse return self.report(tokenizer, missing_arguments_message)) { + .newline => return self.report(tokenizer, missing_arguments_message), .symbol_paren_left => { tokenizer.step(); @@ -239,10 +252,10 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { try expressions_list.push_one(try self.parse_expression(tokenizer)); - switch (tokenizer.token orelse return self.raise("unexpected end after after `(`")) { + switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end after after `(`")) { .symbol_comma => continue, .symbol_paren_right => break, - else => return self.raise("expected `)` or argument after `(`"), + else => return self.report(tokenizer, "expected `)` or argument after `(`"), } } @@ -256,11 +269,11 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { }); }, - else => return self.raise("expected `=` after local"), + else => return self.report(tokenizer, "expected `=` after local"), } }, - else => return self.raise("invalid statement"), + else => return self.report(tokenizer, "invalid statement"), } } } @@ -284,18 +297,18 @@ const parse_expression = binary_operation_parser(parse_equality, &.{ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression { const allocator = self.arena.as_allocator(); - switch (tokenizer.token orelse return self.raise("expected operand after operator")) { + switch (tokenizer.token orelse return self.report(tokenizer, "expected operand after operator")) { .symbol_paren_left => { tokenizer.skip(.newline); if (tokenizer.token == null) { - return self.raise("expected an expression after `(`"); + return self.report(tokenizer, "expected an expression after `(`"); } const expression = try self.parse_expression(tokenizer); if (!tokenizer.is_token(.symbol_paren_right)) { - return self.raise("expected a closing `)` after expression"); + return self.report(tokenizer, "expected a closing `)` after expression"); } tokenizer.step(); @@ -339,7 +352,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression var expression_list = Expression.List.make(allocator); while (true) { - switch (tokenizer.token orelse return self.raise("expected expression or `)` after `(`")) { + switch (tokenizer.token orelse return self.report(tokenizer, "expected expression or `)` after `(`")) { .symbol_paren_right => { tokenizer.step(); @@ -354,7 +367,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression else => { try expression_list.push_one(try self.parse_expression(tokenizer)); - switch (tokenizer.token orelse return self.raise("expected `,` or `)` after argument")) { + switch (tokenizer.token orelse return self.report(tokenizer, "expected `,` or `)` after argument")) { .symbol_comma => continue, .symbol_paren_right => { @@ -368,7 +381,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression }; }, - else => return self.raise("expected `,` or `)` after argument"), + else => return self.report(tokenizer, "expected `,` or `)` after argument"), } }, } @@ -387,7 +400,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression tokenizer.skip(.newline); while (true) { - switch (tokenizer.token orelse return self.raise("unexpected end of table literal")) { + switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end of table literal")) { .symbol_brace_right => { tokenizer.step(); @@ -398,13 +411,13 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression tokenizer.skip(.newline); if (!tokenizer.is_token(.symbol_equals)) { - return self.raise("expected `=` after identifier"); + return self.report(tokenizer, "expected `=` after identifier"); } tokenizer.skip(.newline); if (tokenizer.token == null) { - return self.raise("unexpected end after `=`"); + return self.report(tokenizer, "unexpected end after `=`"); } try table_fields.push_one(.{ @@ -412,7 +425,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression .identifier = identifier, }); - switch (tokenizer.token orelse return self.raise("unexpected end of table")) { + switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end of table")) { .symbol_comma => tokenizer.skip(.newline), .symbol_brace_right => { @@ -421,11 +434,11 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression return Expression{.table_literal = table_fields}; }, - else => return self.raise("expected `,` or `}` after expression"), + else => return self.report(tokenizer, "expected `,` or `}` after expression"), } }, - else => return self.raise("expected `}` or fields in table literal"), + else => return self.report(tokenizer, "expected `}` or fields in table literal"), } } }, @@ -434,7 +447,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression tokenizer.skip(.newline); if (tokenizer.token == null) { - return self.raise("expected expression after numeric negation (`-`)"); + return self.report(tokenizer, "expected expression after numeric negation (`-`)"); } return Expression{ @@ -449,7 +462,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression tokenizer.skip(.newline); if (tokenizer.token == null) { - return self.raise("expected expression after boolean negation (`!`)"); + return self.report(tokenizer, "expected expression after boolean negation (`!`)"); } return Expression{ @@ -460,7 +473,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression }; }, - else => return self.raise("unexpected token in expression"), + else => return self.report(tokenizer, "unexpected token in expression"), } } diff --git a/source/ona/ona.zig b/source/ona/ona.zig index a7ef131..1cd6af5 100644 --- a/source/ona/ona.zig +++ b/source/ona/ona.zig @@ -11,6 +11,8 @@ const heap = @import("./heap.zig"); const kym = @import("./kym.zig"); fn kym_handle_errors(info: kym.ErrorInfo) void { + app.log_fail(info.message); + var remaining_frames = info.frames.len; while (remaining_frames != 0) { @@ -62,6 +64,10 @@ pub fn run_app(file_access: file.Access) void { .name = "log_info", .caller = kym.Caller.from(kym_log_info), }, + .{ + .name = "log_warn", + .caller = kym.Caller.from(kym_log_warn), + }, .{ .name = "log_fail", .caller = kym.Caller.from(kym_log_fail), -- 2.34.1 From 7c71e0528f0dd69145bf1b751c96f3ec3b285ea9 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 22 Jul 2023 14:46:35 +0100 Subject: [PATCH 2/4] Add line delimiter between error messages and traces --- source/ona/ona.zig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/ona/ona.zig b/source/ona/ona.zig index 1cd6af5..35a0445 100644 --- a/source/ona/ona.zig +++ b/source/ona/ona.zig @@ -15,10 +15,14 @@ fn kym_handle_errors(info: kym.ErrorInfo) void { var remaining_frames = info.frames.len; - while (remaining_frames != 0) { - remaining_frames -= 1; + if (remaining_frames != 0) { + app.log_fail("stack trace:"); - app.log_fail(info.frames[remaining_frames].name); + while (remaining_frames != 0) { + remaining_frames -= 1; + + app.log_fail(info.frames[remaining_frames].name); + } } } -- 2.34.1 From 62a318e69b3d696fabf592d65ac1b72dc8d48732 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 22 Jul 2023 14:56:57 +0100 Subject: [PATCH 3/4] Fix comments not being properly tokenized --- debug/app.ona | 2 +- source/ona/kym/tokens.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debug/app.ona b/debug/app.ona index 7624b01..042320c 100644 --- a/debug/app.ona +++ b/debug/app.ona @@ -1,5 +1,5 @@ -// Test comment. +# Test comment. @log_info("game is loading") return { diff --git a/source/ona/kym/tokens.zig b/source/ona/kym/tokens.zig index 49e8d0d..76fac23 100755 --- a/source/ona/kym/tokens.zig +++ b/source/ona/kym/tokens.zig @@ -119,7 +119,7 @@ pub const Tokenizer = struct { '#' => { cursor += 1; - while (cursor < self.source.len and self.source[cursor] == '\n') { + while (cursor < self.source.len and self.source[cursor] != '\n') { cursor += 1; } }, -- 2.34.1 From 51732a9bf5ea670ff5ac0b8f806fc2ac3c8f0ae8 Mon Sep 17 00:00:00 2001 From: kayomn Date: Sat, 22 Jul 2023 15:06:39 +0100 Subject: [PATCH 4/4] Change KYM tokenizer to be internal AST. --- source/ona/kym.zig | 14 +--- source/ona/kym/Ast.zig | 176 +++++++++++++++++++++-------------------- 2 files changed, 93 insertions(+), 97 deletions(-) diff --git a/source/ona/kym.zig b/source/ona/kym.zig index f4a3df6..0553851 100644 --- a/source/ona/kym.zig +++ b/source/ona/kym.zig @@ -8,8 +8,6 @@ const coral = @import("coral"); const file = @import("./file.zig"); -const tokens = @import("./kym/tokens.zig"); - pub const Any = union (enum) { nil, boolean: bool, @@ -204,14 +202,10 @@ pub const RuntimeEnv = struct { defer ast.free(); - { - var tokenizer = tokens.Tokenizer{.source = data}; - - ast.parse(&tokenizer) catch |parse_error| switch (parse_error) { - error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message()), - error.OutOfMemory => return error.OutOfMemory, - }; - } + ast.parse(data) catch |parse_error| switch (parse_error) { + error.BadSyntax => return self.raise(error.BadSyntax, ast.error_message()), + error.OutOfMemory => return error.OutOfMemory, + }; var chunk = Chunk.make(self); diff --git a/source/ona/kym/Ast.zig b/source/ona/kym/Ast.zig index 10a6e45..2580606 100755 --- a/source/ona/kym/Ast.zig +++ b/source/ona/kym/Ast.zig @@ -7,6 +7,7 @@ allocator: coral.io.Allocator, arena: coral.arena.Stacking, statements: Statement.List, error_buffer: coral.list.ByteStack, +tokenizer: tokens.Tokenizer, pub const Expression = union (enum) { nil_literal, @@ -68,7 +69,7 @@ pub const Expression = union (enum) { pub const List = coral.list.Stack(Expression); }; -const ExpressionParser = fn (self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression; +const ExpressionParser = fn (self: *Self) ParseError!Expression; pub const ParseError = error { OutOfMemory, @@ -104,25 +105,25 @@ fn binary_operation_parser( comptime operators: []const Expression.BinaryOperator) ExpressionParser { const BinaryOperationParser = struct { - fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression { + fn parse(self: *Self) ParseError!Expression { const allocator = self.arena.as_allocator(); - var expression = try parse_next(self, tokenizer); + var expression = try parse_next(self); inline for (operators) |operator| { const token = comptime operator.token(); - if (tokenizer.is_token(coral.io.tag_of(token))) { - tokenizer.step(); + if (self.tokenizer.is_token(coral.io.tag_of(token))) { + self.tokenizer.step(); - if (tokenizer.token == null) { - return self.report(tokenizer, "expected other half of expression after `" ++ comptime token.text() ++ "`"); + if (self.tokenizer.token == null) { + return self.report("expected other half of expression after `" ++ comptime token.text() ++ "`"); } expression = .{ .binary_operation = .{ .operator = operator, .lhs_expression = try coral.io.allocate_one(allocator, expression), - .rhs_expression = try coral.io.allocate_one(allocator, try parse_next(self, tokenizer)), + .rhs_expression = try coral.io.allocate_one(allocator, try parse_next(self)), }, }; } @@ -150,15 +151,16 @@ pub fn make(allocator: coral.io.Allocator, ast_name: []const coral.io.Byte) Self .arena = coral.arena.Stacking.make(allocator, 4096), .error_buffer = coral.list.ByteStack.make(allocator), .statements = Statement.List.make(allocator), + .tokenizer = .{.source = ""}, .allocator = allocator, .name = ast_name, }; } -fn report(self: *Self, tokenizer: *tokens.Tokenizer, message: []const coral.io.Byte) ParseError { +fn report(self: *Self, message: []const coral.io.Byte) ParseError { coral.utf8.print_formatted(coral.list.stack_as_writer(&self.error_buffer), "{name}@{line}: {message}", .{ .name = self.name, - .line = tokenizer.lines_stepped, + .line = self.tokenizer.lines_stepped, .message = message, }) catch return error.OutOfMemory; @@ -169,30 +171,30 @@ pub fn list_statements(self: Self) []const Statement { return self.statements.values; } -pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { - self.free(); +pub fn parse(self: *Self, data: []const coral.io.Byte) ParseError!void { + self.tokenizer = .{.source = data}; const allocator = self.arena.as_allocator(); var has_returned = false; while (true) { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - switch (tokenizer.token orelse return) { + switch (self.tokenizer.token orelse return) { .keyword_return => { if (has_returned) { - return self.report(tokenizer, "multiple returns in function scope but expected only one"); + return self.report("multiple returns in function scope but expected only one"); } try self.statements.push_one(get_statement: { - tokenizer.step(); + self.tokenizer.step(); - if (!tokenizer.is_token_null_or(.newline)) { - break: get_statement .{.return_expression = try self.parse_expression(tokenizer)}; + if (!self.tokenizer.is_token_null_or(.newline)) { + break: get_statement .{.return_expression = try self.parse_expression()}; } - if (!tokenizer.is_token_null_or(.newline)) { - return self.report(tokenizer, "unexpected token after return"); + if (!self.tokenizer.is_token_null_or(.newline)) { + return self.report("unexpected token after return"); } break: get_statement .return_nothing; @@ -202,64 +204,64 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { }, .identifier => |identifier| { - tokenizer.step(); + self.tokenizer.step(); const no_effect_message = "statement has no effect"; - switch (tokenizer.token orelse return self.report(tokenizer, no_effect_message)) { - .newline => return self.report(tokenizer, no_effect_message), + switch (self.tokenizer.token orelse return self.report(no_effect_message)) { + .newline => return self.report(no_effect_message), .symbol_equals => { - tokenizer.step(); + self.tokenizer.step(); - if (tokenizer.token == null) { - return self.report(tokenizer, "expected expression after `=`"); + if (self.tokenizer.token == null) { + return self.report("expected expression after `=`"); } try self.statements.push_one(.{ .set_local = .{ - .expression = try self.parse_expression(tokenizer), + .expression = try self.parse_expression(), .identifier = identifier, }, }); - if (!tokenizer.is_token_null_or(.newline)) { - return self.report(tokenizer, "unexpected token after assignment"); + if (!self.tokenizer.is_token_null_or(.newline)) { + return self.report("unexpected token after assignment"); } }, - else => return self.report(tokenizer, "expected `=` after local"), + else => return self.report("expected `=` after local"), } }, .special_identifier => |identifier| { - tokenizer.step(); + self.tokenizer.step(); const missing_arguments_message = "system call is missing arguments"; - switch (tokenizer.token orelse return self.report(tokenizer, missing_arguments_message)) { - .newline => return self.report(tokenizer, missing_arguments_message), + switch (self.tokenizer.token orelse return self.report(missing_arguments_message)) { + .newline => return self.report(missing_arguments_message), .symbol_paren_left => { - tokenizer.step(); + self.tokenizer.step(); var expressions_list = Expression.List.make(allocator); while (true) { - if (tokenizer.is_token(.symbol_paren_right)) { + if (self.tokenizer.is_token(.symbol_paren_right)) { break; } - try expressions_list.push_one(try self.parse_expression(tokenizer)); + try expressions_list.push_one(try self.parse_expression()); - switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end after after `(`")) { + switch (self.tokenizer.token orelse return self.report("unexpected end after after `(`")) { .symbol_comma => continue, .symbol_paren_right => break, - else => return self.report(tokenizer, "expected `)` or argument after `(`"), + else => return self.report("expected `)` or argument after `(`"), } } - tokenizer.step(); + self.tokenizer.step(); try self.statements.push_one(.{ .call_system = .{ @@ -269,11 +271,11 @@ pub fn parse(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!void { }); }, - else => return self.report(tokenizer, "expected `=` after local"), + else => return self.report("expected `=` after local"), } }, - else => return self.report(tokenizer, "invalid statement"), + else => return self.report("invalid statement"), } } } @@ -294,67 +296,67 @@ const parse_expression = binary_operation_parser(parse_equality, &.{ .subtraction, }); -fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression { +fn parse_factor(self: *Self) ParseError!Expression { const allocator = self.arena.as_allocator(); - switch (tokenizer.token orelse return self.report(tokenizer, "expected operand after operator")) { + switch (self.tokenizer.token orelse return self.report("expected operand after operator")) { .symbol_paren_left => { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - if (tokenizer.token == null) { - return self.report(tokenizer, "expected an expression after `(`"); + if (self.tokenizer.token == null) { + return self.report("expected an expression after `(`"); } - const expression = try self.parse_expression(tokenizer); + const expression = try self.parse_expression(); - if (!tokenizer.is_token(.symbol_paren_right)) { - return self.report(tokenizer, "expected a closing `)` after expression"); + if (!self.tokenizer.is_token(.symbol_paren_right)) { + return self.report("expected a closing `)` after expression"); } - tokenizer.step(); + self.tokenizer.step(); return Expression{.grouped_expression = try coral.io.allocate_one(allocator, expression)}; }, .keyword_nil => { - tokenizer.step(); + self.tokenizer.step(); return .nil_literal; }, .keyword_true => { - tokenizer.step(); + self.tokenizer.step(); return .true_literal; }, .keyword_false => { - tokenizer.step(); + self.tokenizer.step(); return .false_literal; }, .number => |value| { - tokenizer.step(); + self.tokenizer.step(); return Expression{.number_literal = value}; }, .string => |value| { - tokenizer.step(); + self.tokenizer.step(); return Expression{.string_literal = value}; }, .special_identifier => |identifier| { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); var expression_list = Expression.List.make(allocator); while (true) { - switch (tokenizer.token orelse return self.report(tokenizer, "expected expression or `)` after `(`")) { + switch (self.tokenizer.token orelse return self.report("expected expression or `)` after `(`")) { .symbol_paren_right => { - tokenizer.step(); + self.tokenizer.step(); return Expression{ .call_system = .{ @@ -365,13 +367,13 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression }, else => { - try expression_list.push_one(try self.parse_expression(tokenizer)); + try expression_list.push_one(try self.parse_expression()); - switch (tokenizer.token orelse return self.report(tokenizer, "expected `,` or `)` after argument")) { + switch (self.tokenizer.token orelse return self.report("expected `,` or `)` after argument")) { .symbol_comma => continue, .symbol_paren_right => { - tokenizer.step(); + self.tokenizer.step(); return Expression{ .call_system = .{ @@ -381,7 +383,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression }; }, - else => return self.report(tokenizer, "expected `,` or `)` after argument"), + else => return self.report("expected `,` or `)` after argument"), } }, } @@ -389,7 +391,7 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression }, .identifier => |identifier| { - tokenizer.step(); + self.tokenizer.step(); return Expression{.get_local = identifier}; }, @@ -397,83 +399,83 @@ fn parse_factor(self: *Self, tokenizer: *tokens.Tokenizer) ParseError!Expression .symbol_brace_left => { var table_fields = Expression.NamedList.make(allocator); - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); while (true) { - switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end of table literal")) { + switch (self.tokenizer.token orelse return self.report("unexpected end of table literal")) { .symbol_brace_right => { - tokenizer.step(); + self.tokenizer.step(); return Expression{.table_literal = table_fields}; }, .identifier => |identifier| { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - if (!tokenizer.is_token(.symbol_equals)) { - return self.report(tokenizer, "expected `=` after identifier"); + if (!self.tokenizer.is_token(.symbol_equals)) { + return self.report("expected `=` after identifier"); } - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - if (tokenizer.token == null) { - return self.report(tokenizer, "unexpected end after `=`"); + if (self.tokenizer.token == null) { + return self.report("unexpected end after `=`"); } try table_fields.push_one(.{ - .expression = try self.parse_expression(tokenizer), + .expression = try self.parse_expression(), .identifier = identifier, }); - switch (tokenizer.token orelse return self.report(tokenizer, "unexpected end of table")) { - .symbol_comma => tokenizer.skip(.newline), + switch (self.tokenizer.token orelse return self.report("unexpected end of table")) { + .symbol_comma => self.tokenizer.skip(.newline), .symbol_brace_right => { - tokenizer.step(); + self.tokenizer.step(); return Expression{.table_literal = table_fields}; }, - else => return self.report(tokenizer, "expected `,` or `}` after expression"), + else => return self.report("expected `,` or `}` after expression"), } }, - else => return self.report(tokenizer, "expected `}` or fields in table literal"), + else => return self.report("expected `}` or fields in table literal"), } } }, .symbol_minus => { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - if (tokenizer.token == null) { - return self.report(tokenizer, "expected expression after numeric negation (`-`)"); + if (self.tokenizer.token == null) { + return self.report("expected expression after numeric negation (`-`)"); } return Expression{ .unary_operation = .{ - .expression = try coral.io.allocate_one(allocator, try self.parse_factor(tokenizer)), + .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), .operator = .numeric_negation, }, }; }, .symbol_bang => { - tokenizer.skip(.newline); + self.tokenizer.skip(.newline); - if (tokenizer.token == null) { - return self.report(tokenizer, "expected expression after boolean negation (`!`)"); + if (self.tokenizer.token == null) { + return self.report("expected expression after boolean negation (`!`)"); } return Expression{ .unary_operation = .{ - .expression = try coral.io.allocate_one(allocator, try self.parse_factor(tokenizer)), + .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), .operator = .boolean_negation, }, }; }, - else => return self.report(tokenizer, "unexpected token in expression"), + else => return self.report("unexpected token in expression"), } } -- 2.34.1