import "@std" import "@std/mem" import "@std/arraylist" import "@source/lexer" import "@source/parser" import "@source/ast" @hide:file TokenId :: alias lexer.TokenId @hide:file Token :: alias lexer.Token @hide:file Node :: alias ast.Node @hide:file NodeId :: alias ast.NodeId @hide:file ParseState :: alias parser.State Renderer :: struct { tokens std.ArrayList(Token) } init proc(allocator mem.Allocator) Renderer { return Renderer{ tokens = arraylist.init(allocator) } } deinit proc(renderer @mut Renderer) void { arraylist.deinit(&renderer.tokens) } render_token_stream proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token ) void ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] match node.kind { .literal_int, .literal_float, .literal_string: _ = try render_lit(renderer, node_id, parse_state, tokens) .stmt_decl: try render_stmt_decl(renderer, node_id, parse_state, tokens) .func_decl: try render_func_decl(renderer, node_id, parse_state, tokens) .package: try render_pkg(renderer, node_id, parse_state, tokens) else: unreachable } } @hide:file render_pkg proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) void ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] extra_start :: usize(node.data0.extra_id) extra_end :: usize(node.data1.extra_id) for extra_start..extra_end |i| { stmt_node_id :: ast.node_id(parse_state.extra.items[i]) stmt_node :: parse_state.nodes.items[usize(stmt_node_id)] match stmt_node.kind { .stmt_decl: try render_stmt_decl(renderer, stmt_node_id, parse_state, tokens) .func_decl: try render_func_decl(renderer, stmt_node_id, parse_state, tokens) else: unreachable } } } @hide:file render_stmt_decl proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) void ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] tok_ident :: tokens[usize(node.main_token)] try arraylist.append(&renderer.tokens, tok_ident) raw_node_id_type :: parse_state.extra.items[usize(node.data0.extra_id)] node_id_type :: ast.maybe_node_id(raw_node_id_type) if (node_id_type) |id| try render_type(renderer, id, parse_state, tokens) tok_assign :: tokens[usize(node.data1.token_id)] try arraylist.append(&renderer.tokens, tok_assign) node_id_expr :: ast.node_id(parse_state.extra.items[usize(node.data0.extra_id + 1)]) last_expr_token :: try render_expr(renderer, node_id_expr, parse_state, tokens) terminator_idx :: usize(last_expr_token) + 1 tok_terminator :: if (terminator_idx < tokens.len) tokens[terminator_idx] else return if (tok_terminator.kind != .newline and tok_terminator.kind != .eof) return try arraylist.append(&renderer.tokens, tok_terminator) eof_idx :: terminator_idx + 1 if (tok_terminator.kind == .newline and eof_idx < tokens.len and tokens[eof_idx].kind == .eof ) try arraylist.append(&renderer.tokens, tokens[eof_idx]) } @hide:file render_func_decl proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) void ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] ident_idx :: usize(node.main_token) try arraylist.append(&renderer.tokens, tokens[ident_idx]) try arraylist.append(&renderer.tokens, tokens[ident_idx + 1]) try arraylist.append(&renderer.tokens, tokens[ident_idx + 2]) payload_start :: usize(node.data1.extra_id) param_count :: usize(parse_state.extra.items[payload_start + 1]) first_param :: payload_start + 2 for 0..param_count |i| { param_id :: ast.node_id(parse_state.extra.items[first_param + i]) param_node :: parse_state.nodes.items[usize(param_id)] type_id :: param_node.data0.node_id type_node :: parse_state.nodes.items[usize(type_id)] try arraylist.append(&renderer.tokens, tokens[usize(param_node.main_token)]) has_next :: i + 1 < param_count if has_next { next_param_id :: ast.node_id(parse_state.extra.items[first_param + i + 1]) next_param :: parse_state.nodes.items[usize(next_param_id)] if (next_param.data0.node_id == type_id) { try arraylist.append( &renderer.tokens, tokens[usize(param_node.main_token) + 1], ) continue } } try render_type(renderer, type_id, parse_state, tokens) if has_next { try arraylist.append( &renderer.tokens, tokens[usize(type_node.main_token) + 1], ) } } return_type_id :: ast.node_id(parse_state.extra.items[payload_start]) return_type_node :: parse_state.nodes.items[usize(return_type_id)] try arraylist.append( &renderer.tokens, tokens[usize(return_type_node.main_token) - 1], ) try render_type(renderer, return_type_id, parse_state, tokens) open_curly_id :: TokenId(usize(return_type_node.main_token) + 1) close_curly_id :: try render_block( renderer, node.data0.node_id, open_curly_id, parse_state, tokens, ) terminator_idx :: usize(close_curly_id) + 1 if (terminator_idx >= tokens.len) return tok_terminator :: tokens[terminator_idx] if (tok_terminator.kind != .newline and tok_terminator.kind != .eof) return try arraylist.append(&renderer.tokens, tok_terminator) eof_idx :: terminator_idx + 1 if (tok_terminator.kind == .newline and eof_idx < tokens.len and tokens[eof_idx].kind == .eof ) try arraylist.append(&renderer.tokens, tokens[eof_idx]) } @hide:file render_block proc( renderer @mut Renderer, node_id NodeId, open_curly_id TokenId, parse_state @ParseState, tokens []Token, ) TokenId ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] open_curly_idx :: usize(open_curly_id) try arraylist.append(&renderer.tokens, tokens[open_curly_idx]) leading_token := open_curly_idx + 1 while (leading_token < tokens.len and tokens[leading_token].kind == .newline) { try arraylist.append(&renderer.tokens, tokens[leading_token]) leading_token += 1 } extra_start :: usize(node.data0.extra_id) extra_end :: usize(node.data1.extra_id) for extra_start..extra_end |i| { stmt_id :: ast.node_id(parse_state.extra.items[i]) stmt :: parse_state.nodes.items[usize(stmt_id)] match stmt.kind { .stmt_decl: try render_stmt_decl(renderer, stmt_id, parse_state, tokens) else: unreachable } } # ponytail: linear delimiter scan; store brace tokens if nested blocks make rendering hot. depth usize := 1 close_curly_idx := open_curly_idx + 1 while close_curly_idx < tokens.len { match tokens[close_curly_idx].kind { .open_curly: depth += 1 .close_curly: { depth -= 1 if (depth == 0) { try arraylist.append(&renderer.tokens, tokens[close_curly_idx]) return TokenId(close_curly_idx) } } else: {} } close_curly_idx += 1 } unreachable } @hide:file render_expr proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) TokenId ! mem.AllocError { # todo: support unary expressions node :: parse_state.nodes.items[usize(node_id)] return match node.kind { .expr_identifier: { tok :: tokens[usize(node.main_token)] 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: try render_lit(renderer, node_id, parse_state, tokens) else: unreachable } } @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, node_id NodeId, parse_state @ParseState, tokens []Token, ) TokenId ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] operator :: node.main_token lhs :: node.data0.node_id rhs :: node.data1.node_id # render lhs operator rhs _ = try render_expr(renderer, lhs, parse_state, tokens) try arraylist.append(&renderer.tokens, tokens[usize(operator)]) last_token :: try render_expr(renderer, rhs, parse_state, tokens) return last_token } @hide:file render_type proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) void ! mem.AllocError { # todo: support more expressive type expressions... node :: parse_state.nodes.items[usize(node_id)] match node.kind { .expr_identifier: { tok :: tokens[usize(node.main_token)] try arraylist.append(&renderer.tokens, tok) } else: unreachable } } @hide:file render_lit proc( renderer @mut Renderer, node_id NodeId, parse_state @ParseState, tokens []Token, ) TokenId ! mem.AllocError { node :: parse_state.nodes.items[usize(node_id)] tok :: tokens[usize(node.main_token)] try arraylist.append(&renderer.tokens, tok) return node.main_token }