diff --git a/source/ona/kym/Ast.zig b/source/ona/kym/Ast.zig index 3890f54..8faa1b2 100755 --- a/source/ona/kym/Ast.zig +++ b/source/ona/kym/Ast.zig @@ -270,63 +270,168 @@ pub fn parse_expression(self: *Self) ParseError!Expression { fn parse_factor(self: *Self) ParseError!Expression { const allocator = self.arena.as_allocator(); - switch (self.tokenizer.token) { - .symbol_paren_left => { - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token == .end) { - return self.report("expected an expression after `(`"); - } - - const expression = try self.parse_expression(); - - if (self.tokenizer.token != .symbol_paren_right) { - return self.report("expected a closing `)` after expression"); - } - - self.tokenizer.skip_newlines(); - - return Expression{.grouped_expression = try coral.io.allocate_one(allocator, expression)}; - }, - - .keyword_nil => { - self.tokenizer.skip_newlines(); - - return .nil_literal; - }, - - .keyword_true => { - self.tokenizer.skip_newlines(); - - return .true_literal; - }, - - .keyword_false => { - self.tokenizer.skip_newlines(); - - return .false_literal; - }, - - .number => |value| { - self.tokenizer.skip_newlines(); - - return .{.number_literal = value}; - }, - - .string => |value| { - self.tokenizer.skip_newlines(); - - return .{.string_literal = value}; - }, - - .identifier => |local_identifier| { - var expression = Expression{.get_local = local_identifier}; - - self.tokenizer.skip_newlines(); - - while (self.tokenizer.token == .symbol_period) { + var expression = @as(Expression, parse: { + switch (self.tokenizer.token) { + .symbol_paren_left => { self.tokenizer.skip_newlines(); + if (self.tokenizer.token == .end) { + return self.report("expected an expression after `(`"); + } + + const expression = try self.parse_expression(); + + if (self.tokenizer.token != .symbol_paren_right) { + return self.report("expected a closing `)` after expression"); + } + + self.tokenizer.skip_newlines(); + + break: parse .{.grouped_expression = try coral.io.allocate_one(allocator, expression)}; + }, + + .keyword_nil => { + self.tokenizer.skip_newlines(); + + break: parse .nil_literal; + }, + + .keyword_true => { + self.tokenizer.skip_newlines(); + + break: parse .true_literal; + }, + + .keyword_false => { + self.tokenizer.skip_newlines(); + + break: parse .false_literal; + }, + + .number => |value| { + self.tokenizer.skip_newlines(); + + break: parse .{.number_literal = value}; + }, + + .string => |value| { + self.tokenizer.skip_newlines(); + + break: parse .{.string_literal = value}; + }, + + .identifier => |local_identifier| { + self.tokenizer.skip_newlines(); + + break: parse .{.get_local = local_identifier}; + }, + + .symbol_brace_left => { + var table_literal = Expression.TableLiteral.make(allocator); + + self.tokenizer.skip_newlines(); + + while (true) { + switch (self.tokenizer.token) { + .symbol_brace_right => { + self.tokenizer.skip_newlines(); + + break: parse .{.table_literal = table_literal}; + }, + + .symbol_bracket_left => { + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token != .symbol_equals) { + return self.report("expected expression after identifier"); + } + }, + + .symbol_period => { + self.tokenizer.step(); + + const identifier = switch (self.tokenizer.token) { + .identifier => |identifier| identifier, + else => return self.report("expected identifier after `.`"), + }; + + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token != .symbol_equals) { + return self.report("expected `=` after key"); + } + + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token == .end) { + return self.report("unexpected end after `=`"); + } + + try table_literal.push_one(.{ + .value_expression = try self.parse_expression(), + .key_expression = .{.symbol_literal = identifier}, + }); + + switch (self.tokenizer.token) { + .symbol_comma => self.tokenizer.skip_newlines(), + + .symbol_brace_right => { + self.tokenizer.skip_newlines(); + + break: parse .{.table_literal = table_literal}; + }, + + else => return self.report("expected `,` or `}` after expression"), + } + }, + + else => return self.report("expected `}` or fields in table literal"), + } + } + }, + + .symbol_minus => { + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token == .end) { + return self.report("expected expression after numeric negation (`-`)"); + } + + break: parse .{ + .unary_operation = .{ + .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), + .operator = .numeric_negation, + }, + }; + }, + + .symbol_bang => { + self.tokenizer.skip_newlines(); + + if (self.tokenizer.token == .end) { + return self.report("expected expression after boolean negation (`!`)"); + } + + break: parse .{ + .unary_operation = .{ + .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), + .operator = .boolean_negation, + }, + }; + }, + + else => return self.report("unexpected token in expression"), + } + }); + + while (true) { + switch (self.tokenizer.token) { + .symbol_period => { + self.tokenizer.skip_newlines(); + + // TODO: Remove when Zig fixes miscompilation with in-place struct re-assignment. + const unnecessary_temp = try coral.io.allocate_one(allocator, expression); + expression = .{ .get_field = .{ .identifier = switch (self.tokenizer.token) { @@ -334,112 +439,18 @@ fn parse_factor(self: *Self) ParseError!Expression { else => return self.report("expected identifier after `.`"), }, - .object_expression = try coral.io.allocate_one(allocator, expression), + .object_expression = unnecessary_temp, }, }; self.tokenizer.skip_newlines(); - } + }, - return expression; - }, - - .symbol_brace_left => { - var table_literal = Expression.TableLiteral.make(allocator); - - self.tokenizer.skip_newlines(); - - while (true) { - switch (self.tokenizer.token) { - .symbol_brace_right => { - self.tokenizer.skip_newlines(); - - return .{.table_literal = table_literal}; - }, - - .symbol_bracket_left => { - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token != .symbol_equals) { - return self.report("expected expression after identifier"); - } - }, - - .symbol_period => { - self.tokenizer.step(); - - const identifier = switch (self.tokenizer.token) { - .identifier => |identifier| identifier, - else => return self.report("expected identifier after `.`"), - }; - - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token != .symbol_equals) { - return self.report("expected `=` after key"); - } - - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token == .end) { - return self.report("unexpected end after `=`"); - } - - try table_literal.push_one(.{ - .value_expression = try self.parse_expression(), - .key_expression = .{.symbol_literal = identifier}, - }); - - switch (self.tokenizer.token) { - .symbol_comma => self.tokenizer.skip_newlines(), - - .symbol_brace_right => { - self.tokenizer.skip_newlines(); - - return .{.table_literal = table_literal}; - }, - - else => return self.report("expected `,` or `}` after expression"), - } - }, - - else => return self.report("expected `}` or fields in table literal"), - } - } - }, - - .symbol_minus => { - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token == .end) { - return self.report("expected expression after numeric negation (`-`)"); - } - - return .{ - .unary_operation = .{ - .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), - .operator = .numeric_negation, - }, - }; - }, - - .symbol_bang => { - self.tokenizer.skip_newlines(); - - if (self.tokenizer.token == .end) { - return self.report("expected expression after boolean negation (`!`)"); - } - - return .{ - .unary_operation = .{ - .expression = try coral.io.allocate_one(allocator, try self.parse_factor()), - .operator = .boolean_negation, - }, - }; - }, - - else => return self.report("unexpected token in expression"), + else => break, + } } + + return expression; } const parse_term = binary_operation_parser(parse_factor, &.{