From 8fbf137c6d6a0162c0d87bea557831c898d029b6 Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Wed, 12 Aug 2026 20:50:51 +0200 Subject: [PATCH] parse unary prefix and postfix expressions --- source/ast/renderer/renderer.hon | 20 +++++++++ source/lexer/token.hon | 1 - source/main.hon | 1 + source/parser/parser.hon | 76 +++++++++++++++++++++++++++----- std/debug/debug.hon | 6 +++ 5 files changed, 93 insertions(+), 11 deletions(-) diff --git a/source/ast/renderer/renderer.hon b/source/ast/renderer/renderer.hon index 773b7a1..2c2b10e 100644 --- a/source/ast/renderer/renderer.hon +++ b/source/ast/renderer/renderer.hon @@ -116,6 +116,8 @@ render_expr proc(renderer @mut Renderer, try arraylist.append(&renderer.tokens, tok) yield node.main_token } + .expr_unary: + try render_expr_unary(renderer, node_id, parse_state, tokens) .expr_binary: try render_expr_binary(renderer, node_id, parse_state, tokens) .literal_int, .literal_float, .literal_string: @@ -124,6 +126,24 @@ render_expr proc(renderer @mut Renderer, } } +@hide:file +render_expr_unary proc( + renderer @mut Renderer, + node_id NodeId, + parse_state @ParseState, + tokens []Token, +) TokenId ! mem.AllocError { + node :: parse_state.nodes.items[usize(node_id)] + operator :: node.main_token + operand :: node.data0.node_id + + # render operator operand + try arraylist.append(&renderer.tokens, tokens[usize(operator)]) + last_token :: try render_expr(renderer, operand, parse_state, tokens) + + return last_token +} + @hide:file render_expr_binary proc( renderer @mut Renderer, diff --git a/source/lexer/token.hon b/source/lexer/token.hon index 14092ce..2b29f30 100644 --- a/source/lexer/token.hon +++ b/source/lexer/token.hon @@ -75,7 +75,6 @@ render_token proc(tok Token, program []u8) void { } } -@hide token_id proc(idx uint) TokenId { debug.assert(u64(idx) < u64(NO_TOKEN)) return TokenId(idx) diff --git a/source/main.hon b/source/main.hon index 10dc76f..d5fc7de 100644 --- a/source/main.hon +++ b/source/main.hon @@ -24,6 +24,7 @@ program :: `z :: "hello" ` `a := 1 + 2 * 3 + `b :: -3 / 2 + 1 main proc() void! { strpool.STRINGS = strpool.init(mem.c_allocator) diff --git a/source/parser/parser.hon b/source/parser/parser.hon index fe1af21..405b42d 100644 --- a/source/parser/parser.hon +++ b/source/parser/parser.hon @@ -2,6 +2,7 @@ import "@std" import "@std/mem" import "@std/arraylist" import "@std/enums/enummap" +import "@std/debug" import "@source/lexer" import "@source/ast" @@ -58,6 +59,9 @@ NO_NODE :: alias ast.NO_NODE @hide:file NO_EXTRA :: alias ast.NO_EXTRA +@hide:file +token_id :: alias lexer.token_id + BindingPowerScalar :: distinct u32 BindingPower :: struct { @@ -65,6 +69,13 @@ BindingPower :: struct { right BindingPowerScalar } +prefix_binding_power proc(token_kind TokenKind) ?BindingPowerScalar { + return match token_kind { + .minus: BindingPowerScalar(30) + else: null + } +} + infix_binding_power proc(token_kind TokenKind) ?BindingPower { return match token_kind { .plus, .minus: BindingPower{ left = BindingPowerScalar(10), right = BindingPowerScalar(11) } @@ -73,6 +84,13 @@ infix_binding_power proc(token_kind TokenKind) ?BindingPower { } } +postfix_binding_power proc(token_kind TokenKind) ?BindingPowerScalar { + return match token_kind { + .open_paren, .open_bracket: BindingPowerScalar(40) + else: null + } +} + State :: struct { nodes std.ArrayList(Node) @@ -170,21 +188,59 @@ parse_expr proc(state @mut State) NodeId ! mem.AllocError { } parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) NodeId ! mem.AllocError { - lhs := try parse_primary(state) + # parse left-hand side + kind := state.tokens[state.next_token].kind - while infix_binding_power(state.tokens[state.next_token].kind) |bp| { - if (bp.left < min_bp) break - - operator :: TokenId(state.next_token) + lhs := if (prefix_binding_power(kind)) |right_bp| { + operator :: token_id(state.next_token) state.next_token += 1 - rhs :: try parse_expr_bp(state, bp.right) - lhs = try add_node(state, Node{ - kind = .expr_binary, + operand :: try parse_expr_bp(state, right_bp) + + yield try add_node(state, Node{ + kind = .expr_unary, main_token = operator, - data0 = NodeData{ node_id = lhs }, - data1 = NodeData{ node_id = rhs }, + data0 = NodeData{ node_id = operand }, }) + } else { + yield try parse_primary(state) + } + + # parse right-hand side + while true { + kind = state.tokens[state.next_token].kind + + # try postfix (higher precedence) + if postfix_binding_power(kind) |left_bp| { + if (left_bp < min_bp) break + + lhs = match state.tokens[state.next_token].kind { + .open_paren: debug.unimplemented() # todo: parse call + .open_bracket: debug.unimplemented() # todo: parse index + else: unreachable + } + continue + } + + # try infex (lower precedence) + if infix_binding_power(kind) |bp| { + if (bp.left < min_bp) break + + operator :: token_id(state.next_token) + state.next_token += 1 + + rhs :: try parse_expr_bp(state, bp.right) + lhs = try add_node(state, Node{ + kind = .expr_binary, + main_token = operator, + data0 = NodeData{ node_id = lhs }, + data1 = NodeData{ node_id = rhs }, + }) + + continue + } + + break } return lhs diff --git a/std/debug/debug.hon b/std/debug/debug.hon index 0869182..431c752 100644 --- a/std/debug/debug.hon +++ b/std/debug/debug.hon @@ -5,6 +5,12 @@ assert proc(ok bool) void { if (!ok) unreachable } +unimplemented proc() noreturn { + print("not yet implemented\n", {}) + c.abort() + unreachable +} + print proc($format []u8, $Args type, args Args) void { writer :: io.Writer{ context = null,