/* * Copyright (c) 2016 Andrew Kelley * * This file is part of zig, which is MIT licensed. * See http://opensource.org/licenses/MIT */ #include "analyze.hpp" #include "ast_render.hpp" #include "os.hpp" #include static const char *bin_op_str(BinOpType bin_op) { switch (bin_op) { case BinOpTypeInvalid: return "(invalid)"; case BinOpTypeBoolOr: return "||"; case BinOpTypeBoolAnd: return "&&"; case BinOpTypeCmpEq: return "=="; case BinOpTypeCmpNotEq: return "!="; case BinOpTypeCmpLessThan: return "<"; case BinOpTypeCmpGreaterThan: return ">"; case BinOpTypeCmpLessOrEq: return "<="; case BinOpTypeCmpGreaterOrEq: return ">="; case BinOpTypeBinOr: return "|"; case BinOpTypeBinXor: return "^"; case BinOpTypeBinAnd: return "&"; case BinOpTypeBitShiftLeft: return "<<"; case BinOpTypeBitShiftLeftWrap: return "<<%"; case BinOpTypeBitShiftRight: return ">>"; case BinOpTypeAdd: return "+"; case BinOpTypeAddWrap: return "+%"; case BinOpTypeSub: return "-"; case BinOpTypeSubWrap: return "-%"; case BinOpTypeMult: return "*"; case BinOpTypeMultWrap: return "*%"; case BinOpTypeDiv: return "/"; case BinOpTypeMod: return "%"; case BinOpTypeAssign: return "="; case BinOpTypeAssignTimes: return "*="; case BinOpTypeAssignTimesWrap: return "*%="; case BinOpTypeAssignDiv: return "/="; case BinOpTypeAssignMod: return "%="; case BinOpTypeAssignPlus: return "+="; case BinOpTypeAssignPlusWrap: return "+%="; case BinOpTypeAssignMinus: return "-="; case BinOpTypeAssignMinusWrap: return "-%="; case BinOpTypeAssignBitShiftLeft: return "<<="; case BinOpTypeAssignBitShiftLeftWrap: return "<<%="; case BinOpTypeAssignBitShiftRight: return ">>="; case BinOpTypeAssignBitAnd: return "&="; case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignBoolAnd: return "&&="; case BinOpTypeAssignBoolOr: return "||="; case BinOpTypeUnwrapMaybe: return "??"; case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayMult: return "**"; } zig_unreachable(); } static const char *prefix_op_str(PrefixOp prefix_op) { switch (prefix_op) { case PrefixOpInvalid: return "(invalid)"; case PrefixOpNegation: return "-"; case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; case PrefixOpAddressOf: return "&"; case PrefixOpConstAddressOf: return "&const "; case PrefixOpVolatileAddressOf: return "&volatile "; case PrefixOpConstVolatileAddressOf: return "&const volatile "; case PrefixOpDereference: return "*"; case PrefixOpMaybe: return "?"; case PrefixOpError: return "%"; case PrefixOpUnwrapError: return "%%"; case PrefixOpUnwrapMaybe: return "??"; } zig_unreachable(); } static const char *visib_mod_string(VisibMod mod) { switch (mod) { case VisibModPub: return "pub "; case VisibModPrivate: return ""; case VisibModExport: return "export "; } zig_unreachable(); } static const char *return_string(ReturnKind kind) { switch (kind) { case ReturnKindUnconditional: return "return"; case ReturnKindError: return "%return"; } zig_unreachable(); } static const char *defer_string(ReturnKind kind) { switch (kind) { case ReturnKindUnconditional: return "defer"; case ReturnKindError: return "%defer"; } zig_unreachable(); } static const char *layout_string(ContainerLayout layout) { switch (layout) { case ContainerLayoutAuto: return ""; case ContainerLayoutExtern: return "extern "; case ContainerLayoutPacked: return "packed "; } zig_unreachable(); } static const char *extern_string(bool is_extern) { return is_extern ? "extern " : ""; } static const char *inline_string(bool is_inline) { return is_inline ? "inline " : ""; } static const char *const_or_var_string(bool is_const) { return is_const ? "const" : "var"; } const char *container_string(ContainerKind kind) { switch (kind) { case ContainerKindEnum: return "enum"; case ContainerKindStruct: return "struct"; case ContainerKindUnion: return "union"; } zig_unreachable(); } static const char *node_type_str(NodeType node_type) { switch (node_type) { case NodeTypeRoot: return "Root"; case NodeTypeFnDef: return "FnDef"; case NodeTypeFnDecl: return "FnDecl"; case NodeTypeFnProto: return "FnProto"; case NodeTypeParamDecl: return "ParamDecl"; case NodeTypeBlock: return "Block"; case NodeTypeGroupedExpr: return "Parens"; case NodeTypeBinOpExpr: return "BinOpExpr"; case NodeTypeUnwrapErrorExpr: return "UnwrapErrorExpr"; case NodeTypeFnCallExpr: return "FnCallExpr"; case NodeTypeArrayAccessExpr: return "ArrayAccessExpr"; case NodeTypeSliceExpr: return "SliceExpr"; case NodeTypeReturnExpr: return "ReturnExpr"; case NodeTypeDefer: return "Defer"; case NodeTypeVariableDeclaration: return "VariableDeclaration"; case NodeTypeErrorValueDecl: return "ErrorValueDecl"; case NodeTypeTestDecl: return "TestDecl"; case NodeTypeNumberLiteral: return "NumberLiteral"; case NodeTypeStringLiteral: return "StringLiteral"; case NodeTypeCharLiteral: return "CharLiteral"; case NodeTypeSymbol: return "Symbol"; case NodeTypePrefixOpExpr: return "PrefixOpExpr"; case NodeTypeUse: return "Use"; case NodeTypeBoolLiteral: return "BoolLiteral"; case NodeTypeNullLiteral: return "NullLiteral"; case NodeTypeUndefinedLiteral: return "UndefinedLiteral"; case NodeTypeThisLiteral: return "ThisLiteral"; case NodeTypeIfBoolExpr: return "IfBoolExpr"; case NodeTypeWhileExpr: return "WhileExpr"; case NodeTypeForExpr: return "ForExpr"; case NodeTypeSwitchExpr: return "SwitchExpr"; case NodeTypeSwitchProng: return "SwitchProng"; case NodeTypeSwitchRange: return "SwitchRange"; case NodeTypeLabel: return "Label"; case NodeTypeGoto: return "Goto"; case NodeTypeCompTime: return "CompTime"; case NodeTypeBreak: return "Break"; case NodeTypeContinue: return "Continue"; case NodeTypeUnreachable: return "Unreachable"; case NodeTypeAsmExpr: return "AsmExpr"; case NodeTypeFieldAccessExpr: return "FieldAccessExpr"; case NodeTypeContainerDecl: return "ContainerDecl"; case NodeTypeStructField: return "StructField"; case NodeTypeStructValueField: return "StructValueField"; case NodeTypeContainerInitExpr: return "ContainerInitExpr"; case NodeTypeArrayType: return "ArrayType"; case NodeTypeErrorType: return "ErrorType"; case NodeTypeVarLiteral: return "VarLiteral"; case NodeTypeTryExpr: return "TryExpr"; case NodeTypeTestExpr: return "TestExpr"; } zig_unreachable(); } struct AstPrint { int indent; FILE *f; }; static void ast_print_visit(AstNode **node_ptr, void *context) { AstNode *node = *node_ptr; AstPrint *ap = (AstPrint *)context; for (int i = 0; i < ap->indent; i += 1) { fprintf(ap->f, " "); } fprintf(ap->f, "%s\n", node_type_str(node->type)); AstPrint new_ap; new_ap.indent = ap->indent + 2; new_ap.f = ap->f; ast_visit_node_children(node, ast_print_visit, &new_ap); } void ast_print(FILE *f, AstNode *node, int indent) { AstPrint ap; ap.indent = indent; ap.f = f; ast_visit_node_children(node, ast_print_visit, &ap); } struct AstRender { CodeGen *codegen; int indent; int indent_size; FILE *f; }; static void print_indent(AstRender *ar) { for (int i = 0; i < ar->indent; i += 1) { fprintf(ar->f, " "); } } static bool is_alpha_under(uint8_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } static bool is_digit(uint8_t c) { return (c >= '0' && c <= '9'); } static bool is_printable(uint8_t c) { static const uint8_t printables[] = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.~`!@#$%^&*()_-+=\\{}[];'\"?/<>,"; for (size_t i = 0; i < array_length(printables); i += 1) { if (c == printables[i]) return true; } return false; } static void string_literal_escape(Buf *source, Buf *dest) { buf_resize(dest, 0); for (size_t i = 0; i < buf_len(source); i += 1) { uint8_t c = *((uint8_t*)buf_ptr(source) + i); if (is_printable(c)) { buf_append_char(dest, c); } else if (c == '\'') { buf_append_str(dest, "\\'"); } else if (c == '"') { buf_append_str(dest, "\\\""); } else if (c == '\\') { buf_append_str(dest, "\\\\"); } else if (c == '\a') { buf_append_str(dest, "\\a"); } else if (c == '\b') { buf_append_str(dest, "\\b"); } else if (c == '\f') { buf_append_str(dest, "\\f"); } else if (c == '\n') { buf_append_str(dest, "\\n"); } else if (c == '\r') { buf_append_str(dest, "\\r"); } else if (c == '\t') { buf_append_str(dest, "\\t"); } else if (c == '\v') { buf_append_str(dest, "\\v"); } else { buf_appendf(dest, "\\x%x", (int)c); } } } static bool is_valid_bare_symbol(Buf *symbol) { if (buf_len(symbol) == 0) { return false; } uint8_t first_char = *buf_ptr(symbol); if (!is_alpha_under(first_char)) { return false; } for (size_t i = 1; i < buf_len(symbol); i += 1) { uint8_t c = *((uint8_t*)buf_ptr(symbol) + i); if (!is_alpha_under(c) && !is_digit(c)) { return false; } } return true; } static void print_symbol(AstRender *ar, Buf *symbol) { if (is_zig_keyword(symbol)) { fprintf(ar->f, "@\"%s\"", buf_ptr(symbol)); return; } if (is_valid_bare_symbol(symbol)) { fprintf(ar->f, "%s", buf_ptr(symbol)); return; } Buf escaped = BUF_INIT; string_literal_escape(symbol, &escaped); fprintf(ar->f, "@\"%s\"", buf_ptr(&escaped)); } static void render_node_extra(AstRender *ar, AstNode *node, bool grouped); static void render_node_grouped(AstRender *ar, AstNode *node) { return render_node_extra(ar, node, true); } static void render_node_ungrouped(AstRender *ar, AstNode *node) { return render_node_extra(ar, node, false); } static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { switch (node->type) { case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeLabel: case NodeTypeStructValueField: zig_unreachable(); case NodeTypeRoot: for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); print_indent(ar); render_node_grouped(ar, child); if (child->type == NodeTypeUse || child->type == NodeTypeVariableDeclaration || child->type == NodeTypeErrorValueDecl || child->type == NodeTypeFnProto) { fprintf(ar->f, ";"); } fprintf(ar->f, "\n"); } break; case NodeTypeFnProto: { const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod); const char *extern_str = extern_string(node->data.fn_proto.is_extern); const char *inline_str = inline_string(node->data.fn_proto.is_inline); fprintf(ar->f, "%s%s%sfn ", pub_str, inline_str, extern_str); print_symbol(ar, node->data.fn_proto.name); fprintf(ar->f, "("); size_t arg_count = node->data.fn_proto.params.length; for (size_t arg_i = 0; arg_i < arg_count; arg_i += 1) { AstNode *param_decl = node->data.fn_proto.params.at(arg_i); assert(param_decl->type == NodeTypeParamDecl); if (buf_len(param_decl->data.param_decl.name) > 0) { const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : ""; const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : ""; fprintf(ar->f, "%s%s", noalias_str, inline_str); print_symbol(ar, param_decl->data.param_decl.name); fprintf(ar->f, ": "); } if (param_decl->data.param_decl.is_var_args) { fprintf(ar->f, "..."); } else { render_node_grouped(ar, param_decl->data.param_decl.type); } if (arg_i + 1 < arg_count) { fprintf(ar->f, ", "); } } fprintf(ar->f, ")"); AstNode *return_type_node = node->data.fn_proto.return_type; fprintf(ar->f, " -> "); render_node_grouped(ar, return_type_node); break; } case NodeTypeFnDef: { render_node_grouped(ar, node->data.fn_def.fn_proto); fprintf(ar->f, " "); render_node_grouped(ar, node->data.fn_def.body); break; } case NodeTypeBlock: if (node->data.block.statements.length == 0) { fprintf(ar->f, "{}"); break; } fprintf(ar->f, "{\n"); ar->indent += ar->indent_size; for (size_t i = 0; i < node->data.block.statements.length; i += 1) { AstNode *statement = node->data.block.statements.at(i); if (statement->type == NodeTypeLabel) { ar->indent -= ar->indent_size; print_indent(ar); fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name)); ar->indent += ar->indent_size; continue; } print_indent(ar); render_node_grouped(ar, statement); if (!(i == node->data.block.statements.length - 1 && node->data.block.last_statement_is_result_expression)) { fprintf(ar->f, ";"); } fprintf(ar->f, "\n"); } ar->indent -= ar->indent_size; print_indent(ar); fprintf(ar->f, "}"); break; case NodeTypeGroupedExpr: fprintf(ar->f, "("); render_node_ungrouped(ar, node->data.grouped_expr); fprintf(ar->f, ")"); break; case NodeTypeReturnExpr: { const char *return_str = return_string(node->data.return_expr.kind); fprintf(ar->f, "%s", return_str); if (node->data.return_expr.expr) { fprintf(ar->f, " "); render_node_grouped(ar, node->data.return_expr.expr); } break; } case NodeTypeBreak: { fprintf(ar->f, "break"); if (node->data.break_expr.expr) { fprintf(ar->f, " "); render_node_grouped(ar, node->data.break_expr.expr); } break; } case NodeTypeDefer: { const char *defer_str = defer_string(node->data.defer.kind); fprintf(ar->f, "%s ", defer_str); render_node_grouped(ar, node->data.return_expr.expr); break; } case NodeTypeVariableDeclaration: { const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); const char *extern_str = extern_string(node->data.variable_declaration.is_extern); const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const); fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var); print_symbol(ar, node->data.variable_declaration.symbol); if (node->data.variable_declaration.type) { fprintf(ar->f, ": "); render_node_grouped(ar, node->data.variable_declaration.type); } if (node->data.variable_declaration.expr) { fprintf(ar->f, " = "); render_node_grouped(ar, node->data.variable_declaration.expr); } break; } case NodeTypeBinOpExpr: if (!grouped) fprintf(ar->f, "("); render_node_ungrouped(ar, node->data.bin_op_expr.op1); fprintf(ar->f, " %s ", bin_op_str(node->data.bin_op_expr.bin_op)); render_node_ungrouped(ar, node->data.bin_op_expr.op2); if (!grouped) fprintf(ar->f, ")"); break; case NodeTypeNumberLiteral: switch (node->data.number_literal.bignum->kind) { case BigNumKindInt: { const char *negative_str = node->data.number_literal.bignum->is_negative ? "-" : ""; fprintf(ar->f, "%s%" ZIG_PRI_llu, negative_str, node->data.number_literal.bignum->data.x_uint); } break; case BigNumKindFloat: fprintf(ar->f, "%f", node->data.number_literal.bignum->data.x_float); break; } break; case NodeTypeStringLiteral: { if (node->data.string_literal.c) { fprintf(ar->f, "c"); } Buf tmp_buf = BUF_INIT; string_literal_escape(node->data.string_literal.buf, &tmp_buf); fprintf(ar->f, "\"%s\"", buf_ptr(&tmp_buf)); } break; case NodeTypeCharLiteral: { uint8_t c = node->data.char_literal.value; if (is_printable(c)) { fprintf(ar->f, "'%c'", c); } else { fprintf(ar->f, "'\\x%x'", (int)c); } break; } case NodeTypeSymbol: print_symbol(ar, node->data.symbol_expr.symbol); break; case NodeTypePrefixOpExpr: { PrefixOp op = node->data.prefix_op_expr.prefix_op; fprintf(ar->f, "%s", prefix_op_str(op)); render_node_ungrouped(ar, node->data.prefix_op_expr.primary_expr); break; } case NodeTypeFnCallExpr: { if (node->data.fn_call_expr.is_builtin) { fprintf(ar->f, "@"); } AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; bool grouped = (fn_ref_node->type != NodeTypeBinOpExpr); render_node_extra(ar, fn_ref_node, grouped); fprintf(ar->f, "("); for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) { AstNode *param = node->data.fn_call_expr.params.at(i); if (i != 0) { fprintf(ar->f, ", "); } render_node_grouped(ar, param); } fprintf(ar->f, ")"); break; } case NodeTypeArrayAccessExpr: render_node_ungrouped(ar, node->data.array_access_expr.array_ref_expr); fprintf(ar->f, "["); render_node_grouped(ar, node->data.array_access_expr.subscript); fprintf(ar->f, "]"); break; case NodeTypeFieldAccessExpr: { AstNode *lhs = node->data.field_access_expr.struct_expr; Buf *rhs = node->data.field_access_expr.field_name; render_node_ungrouped(ar, lhs); fprintf(ar->f, "."); print_symbol(ar, rhs); break; } case NodeTypeUndefinedLiteral: fprintf(ar->f, "undefined"); break; case NodeTypeContainerDecl: { const char *container_str = container_string(node->data.container_decl.kind); fprintf(ar->f, "%s {\n", container_str); ar->indent += ar->indent_size; for (size_t field_i = 0; field_i < node->data.container_decl.fields.length; field_i += 1) { AstNode *field_node = node->data.container_decl.fields.at(field_i); assert(field_node->type == NodeTypeStructField); print_indent(ar); print_symbol(ar, field_node->data.struct_field.name); fprintf(ar->f, ": "); render_node_grouped(ar, field_node->data.struct_field.type); fprintf(ar->f, ",\n"); } ar->indent -= ar->indent_size; print_indent(ar); fprintf(ar->f, "}"); break; } case NodeTypeContainerInitExpr: render_node_ungrouped(ar, node->data.container_init_expr.type); if (node->data.container_init_expr.kind == ContainerInitKindStruct) { fprintf(ar->f, "{\n"); ar->indent += ar->indent_size; } else { fprintf(ar->f, "{"); } for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) { AstNode *entry = node->data.container_init_expr.entries.at(i); if (entry->type == NodeTypeStructValueField) { Buf *name = entry->data.struct_val_field.name; AstNode *expr = entry->data.struct_val_field.expr; print_indent(ar); fprintf(ar->f, ".%s = ", buf_ptr(name)); render_node_grouped(ar, expr); fprintf(ar->f, ",\n"); } else { if (i != 0) fprintf(ar->f, ", "); render_node_grouped(ar, entry); } } if (node->data.container_init_expr.kind == ContainerInitKindStruct) { ar->indent -= ar->indent_size; } print_indent(ar); fprintf(ar->f, "}"); break; case NodeTypeArrayType: { fprintf(ar->f, "["); if (node->data.array_type.size) { render_node_grouped(ar, node->data.array_type.size); } fprintf(ar->f, "]"); if (node->data.array_type.is_const) { fprintf(ar->f, "const "); } render_node_ungrouped(ar, node->data.array_type.child_type); break; } case NodeTypeErrorType: fprintf(ar->f, "error"); break; case NodeTypeVarLiteral: fprintf(ar->f, "var"); break; case NodeTypeAsmExpr: { AstNodeAsmExpr *asm_expr = &node->data.asm_expr; const char *volatile_str = asm_expr->is_volatile ? " volatile" : ""; fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(asm_expr->asm_template)); print_indent(ar); fprintf(ar->f, ": "); for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { AsmOutput *asm_output = asm_expr->output_list.at(i); if (i != 0) { fprintf(ar->f, ",\n"); print_indent(ar); } fprintf(ar->f, "[%s] \"%s\" (", buf_ptr(asm_output->asm_symbolic_name), buf_ptr(asm_output->constraint)); if (asm_output->return_type) { fprintf(ar->f, "-> "); render_node_grouped(ar, asm_output->return_type); } else { fprintf(ar->f, "%s", buf_ptr(asm_output->variable_name)); } fprintf(ar->f, ")"); } fprintf(ar->f, "\n"); print_indent(ar); fprintf(ar->f, ": "); for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { AsmInput *asm_input = asm_expr->input_list.at(i); if (i != 0) { fprintf(ar->f, ",\n"); print_indent(ar); } fprintf(ar->f, "[%s] \"%s\" (", buf_ptr(asm_input->asm_symbolic_name), buf_ptr(asm_input->constraint)); render_node_grouped(ar, asm_input->expr); fprintf(ar->f, ")"); } fprintf(ar->f, "\n"); print_indent(ar); fprintf(ar->f, ": "); for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1) { Buf *reg_name = asm_expr->clobber_list.at(i); if (i != 0) fprintf(ar->f, ", "); fprintf(ar->f, "\"%s\"", buf_ptr(reg_name)); } fprintf(ar->f, ")"); break; } case NodeTypeWhileExpr: { const char *inline_str = node->data.while_expr.is_inline ? "inline " : ""; fprintf(ar->f, "%swhile (", inline_str); render_node_grouped(ar, node->data.while_expr.condition); fprintf(ar->f, ") "); if (node->data.while_expr.var_symbol) { fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.var_symbol)); } if (node->data.while_expr.continue_expr) { fprintf(ar->f, ": ("); render_node_grouped(ar, node->data.while_expr.continue_expr); fprintf(ar->f, ") "); } render_node_grouped(ar, node->data.while_expr.body); if (node->data.while_expr.else_node) { fprintf(ar->f, " else "); if (node->data.while_expr.err_symbol) { fprintf(ar->f, "|%s| ", buf_ptr(node->data.while_expr.err_symbol)); } render_node_grouped(ar, node->data.while_expr.else_node); } break; } case NodeTypeThisLiteral: { fprintf(ar->f, "this"); break; } case NodeTypeBoolLiteral: { const char *bool_str = node->data.bool_literal.value ? "true" : "false"; fprintf(ar->f, "%s", bool_str); break; } case NodeTypeIfBoolExpr: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.if_bool_expr.condition); fprintf(ar->f, ") "); render_node_grouped(ar, node->data.if_bool_expr.then_block); if (node->data.if_bool_expr.else_node) { fprintf(ar->f, " else "); render_node_grouped(ar, node->data.if_bool_expr.else_node); } break; } case NodeTypeNullLiteral: { fprintf(ar->f, "null"); break; } case NodeTypeTryExpr: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.try_expr.target_node); fprintf(ar->f, ") "); if (node->data.try_expr.var_symbol) { const char *ptr_str = node->data.try_expr.var_is_ptr ? "*" : ""; const char *var_name = buf_ptr(node->data.try_expr.var_symbol); fprintf(ar->f, "|%s%s| ", ptr_str, var_name); } render_node_grouped(ar, node->data.try_expr.then_node); if (node->data.try_expr.else_node) { fprintf(ar->f, " else "); if (node->data.try_expr.err_symbol) { fprintf(ar->f, "|%s| ", buf_ptr(node->data.try_expr.err_symbol)); } render_node_grouped(ar, node->data.try_expr.else_node); } break; } case NodeTypeTestExpr: { fprintf(ar->f, "if ("); render_node_grouped(ar, node->data.test_expr.target_node); fprintf(ar->f, ") "); if (node->data.test_expr.var_symbol) { const char *ptr_str = node->data.test_expr.var_is_ptr ? "*" : ""; const char *var_name = buf_ptr(node->data.test_expr.var_symbol); fprintf(ar->f, "|%s%s| ", ptr_str, var_name); } render_node_grouped(ar, node->data.test_expr.then_node); if (node->data.test_expr.else_node) { fprintf(ar->f, " else "); render_node_grouped(ar, node->data.test_expr.else_node); } break; } case NodeTypeSwitchExpr: { AstNodeSwitchExpr *switch_expr = &node->data.switch_expr; fprintf(ar->f, "switch ("); render_node_grouped(ar, switch_expr->expr); fprintf(ar->f, ") {\n"); ar->indent += ar->indent_size; for (size_t prong_i = 0; prong_i < switch_expr->prongs.length; prong_i += 1) { AstNode *prong_node = switch_expr->prongs.at(prong_i); AstNodeSwitchProng *switch_prong = &prong_node->data.switch_prong; print_indent(ar); for (size_t item_i = 0; item_i < switch_prong->items.length; item_i += 1) { AstNode *item_node = switch_prong->items.at(item_i); if (item_i != 0) fprintf(ar->f, ", "); if (item_node->type == NodeTypeSwitchRange) { AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; render_node_grouped(ar, start_node); fprintf(ar->f, "..."); render_node_grouped(ar, end_node); } else { render_node_grouped(ar, item_node); } } const char *else_str = (switch_prong->items.length == 0) ? "else" : ""; fprintf(ar->f, "%s => ", else_str); if (switch_prong->var_symbol) { const char *star_str = switch_prong->var_is_ptr ? "*" : ""; Buf *var_name = switch_prong->var_symbol->data.symbol_expr.symbol; fprintf(ar->f, "|%s%s| ", star_str, buf_ptr(var_name)); } render_node_grouped(ar, switch_prong->expr); fprintf(ar->f, ",\n"); } ar->indent -= ar->indent_size; print_indent(ar); fprintf(ar->f, "}"); break; } case NodeTypeGoto: { fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name)); break; } case NodeTypeCompTime: { fprintf(ar->f, "comptime "); render_node_grouped(ar, node->data.comptime_expr.expr); break; } case NodeTypeForExpr: { const char *inline_str = node->data.for_expr.is_inline ? "inline " : ""; fprintf(ar->f, "%sfor (", inline_str); render_node_grouped(ar, node->data.for_expr.array_expr); fprintf(ar->f, ") "); if (node->data.for_expr.elem_node) { fprintf(ar->f, "|"); if (node->data.for_expr.elem_is_ptr) fprintf(ar->f, "*"); render_node_grouped(ar, node->data.for_expr.elem_node); if (node->data.for_expr.index_node) { fprintf(ar->f, ", "); render_node_grouped(ar, node->data.for_expr.index_node); } fprintf(ar->f, "| "); } render_node_grouped(ar, node->data.for_expr.body); if (node->data.for_expr.else_node) { fprintf(ar->f, " else"); render_node_grouped(ar, node->data.for_expr.else_node); } break; } case NodeTypeContinue: { fprintf(ar->f, "continue"); break; } case NodeTypeUnreachable: { fprintf(ar->f, "unreachable"); break; } case NodeTypeSliceExpr: { render_node_ungrouped(ar, node->data.slice_expr.array_ref_expr); fprintf(ar->f, "["); render_node_grouped(ar, node->data.slice_expr.start); fprintf(ar->f, "..."); if (node->data.slice_expr.end) render_node_grouped(ar, node->data.slice_expr.end); fprintf(ar->f, "]"); break; } case NodeTypeUnwrapErrorExpr: { render_node_ungrouped(ar, node->data.unwrap_err_expr.op1); fprintf(ar->f, " %%%% "); if (node->data.unwrap_err_expr.symbol) { Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol; fprintf(ar->f, "|%s| ", buf_ptr(var_name)); } render_node_ungrouped(ar, node->data.unwrap_err_expr.op2); break; } case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeErrorValueDecl: case NodeTypeTestDecl: case NodeTypeStructField: case NodeTypeUse: zig_panic("TODO more ast rendering"); } } void ast_render(CodeGen *codegen, FILE *f, AstNode *node, int indent_size) { AstRender ar = {0}; ar.codegen = codegen; ar.f = f; ar.indent_size = indent_size; ar.indent = 0; render_node_grouped(&ar, node); } static void ast_render_tld_fn(AstRender *ar, Buf *name, TldFn *tld_fn) { FnTableEntry *fn_entry = tld_fn->fn_entry; FnTypeId *fn_type_id = &fn_entry->type_entry->data.fn.fn_type_id; const char *visib_mod_str = visib_mod_string(tld_fn->base.visib_mod); const char *extern_str = extern_string(fn_type_id->is_extern); const char *coldcc_str = fn_type_id->is_cold ? "coldcc " : ""; const char *nakedcc_str = fn_type_id->is_naked ? "nakedcc " : ""; fprintf(ar->f, "%s%s%s%sfn %s(", visib_mod_str, extern_str, coldcc_str, nakedcc_str, buf_ptr(&fn_entry->symbol_name)); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; if (i != 0) { fprintf(ar->f, ", "); } if (param_info->is_noalias) { fprintf(ar->f, "noalias "); } Buf *param_name = tld_fn->fn_entry->param_names ? tld_fn->fn_entry->param_names[i] : buf_sprintf("arg%" ZIG_PRI_usize "", i); fprintf(ar->f, "%s: %s", buf_ptr(param_name), buf_ptr(¶m_info->type->name)); } if (fn_type_id->return_type->id == TypeTableEntryIdVoid) { fprintf(ar->f, ");\n"); } else { fprintf(ar->f, ") -> %s;\n", buf_ptr(&fn_type_id->return_type->name)); } } static void ast_render_tld_var(AstRender *ar, Buf *name, TldVar *tld_var) { VariableTableEntry *var = tld_var->var; const char *visib_mod_str = visib_mod_string(tld_var->base.visib_mod); const char *const_or_var = const_or_var_string(var->src_is_const); const char *extern_str = extern_string(var->linkage == VarLinkageExternal); fprintf(ar->f, "%s%s%s %s", visib_mod_str, extern_str, const_or_var, buf_ptr(name)); if (var->value->type->id == TypeTableEntryIdNumLitFloat || var->value->type->id == TypeTableEntryIdNumLitInt || var->value->type->id == TypeTableEntryIdMetaType) { // skip type } else { fprintf(ar->f, ": %s", buf_ptr(&var->value->type->name)); } if (var->value->special == ConstValSpecialRuntime) { fprintf(ar->f, ";\n"); return; } fprintf(ar->f, " = "); if (var->value->special == ConstValSpecialStatic && var->value->type->id == TypeTableEntryIdMetaType) { TypeTableEntry *type_entry = var->value->data.x_type; if (type_entry->id == TypeTableEntryIdStruct) { const char *layout_str = layout_string(type_entry->data.structure.layout); fprintf(ar->f, "%sstruct {\n", layout_str); if (type_entry->data.structure.complete) { for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { TypeStructField *field = &type_entry->data.structure.fields[i]; fprintf(ar->f, " "); print_symbol(ar, field->name); fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name)); } } fprintf(ar->f, "}"); } else if (type_entry->id == TypeTableEntryIdEnum) { const char *layout_str = layout_string(type_entry->data.enumeration.layout); fprintf(ar->f, "%senum {\n", layout_str); if (type_entry->data.enumeration.complete) { for (size_t i = 0; i < type_entry->data.enumeration.src_field_count; i += 1) { TypeEnumField *field = &type_entry->data.enumeration.fields[i]; fprintf(ar->f, " "); print_symbol(ar, field->name); if (field->type_entry->id == TypeTableEntryIdVoid) { fprintf(ar->f, ",\n"); } else { fprintf(ar->f, ": %s,\n", buf_ptr(&field->type_entry->name)); } } } fprintf(ar->f, "}"); } else if (type_entry->id == TypeTableEntryIdUnion) { fprintf(ar->f, "union {"); fprintf(ar->f, "TODO"); fprintf(ar->f, "}"); } else if (type_entry->id == TypeTableEntryIdOpaque) { if (buf_eql_buf(&type_entry->name, name)) { fprintf(ar->f, "@OpaqueType()"); } else { fprintf(ar->f, "%s", buf_ptr(&type_entry->name)); } } else { fprintf(ar->f, "%s", buf_ptr(&type_entry->name)); } } else { Buf buf = BUF_INIT; buf_resize(&buf, 0); render_const_value(ar->codegen, &buf, var->value); fprintf(ar->f, "%s", buf_ptr(&buf)); } fprintf(ar->f, ";\n"); } void ast_render_decls(CodeGen *codegen, FILE *f, int indent_size, ImportTableEntry *import) { AstRender ar = {0}; ar.codegen = codegen; ar.f = f; ar.indent_size = indent_size; ar.indent = 0; auto it = import->decls_scope->decl_table.entry_iterator(); for (;;) { auto *entry = it.next(); if (!entry) break; Tld *tld = entry->value; if (tld->name != nullptr && !buf_eql_buf(entry->key, tld->name)) { fprintf(ar.f, "pub const "); print_symbol(&ar, entry->key); fprintf(ar.f, " = %s;\n", buf_ptr(tld->name)); continue; } switch (tld->id) { case TldIdVar: ast_render_tld_var(&ar, entry->key, (TldVar *)tld); break; case TldIdFn: ast_render_tld_fn(&ar, entry->key, (TldFn *)tld); break; case TldIdContainer: fprintf(stdout, "container\n"); break; case TldIdCompTime: fprintf(stdout, "comptime\n"); break; } } }