zig/src/ast_render.cpp
Andrew Kelley 15c316b0d8
add docs for assembly and fix global assembly parsing
Previously, global assembly was parsed expecting it to have
the template syntax. However global assembly has no inputs,
outputs, or clobbers, and thus does not have template syntax.
This is now fixed.

This commit also adds a compile error for using volatile
on global assembly, since it is meaningless.

closes #1515
2019-03-20 19:00:23 -04:00

1175 lines
46 KiB
C++

/*
* 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 <stdio.h>
static const char *bin_op_str(BinOpType bin_op) {
switch (bin_op) {
case BinOpTypeInvalid: return "(invalid)";
case BinOpTypeBoolOr: return "or";
case BinOpTypeBoolAnd: return "and";
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 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 BinOpTypeAssignBitShiftRight: return ">>=";
case BinOpTypeAssignBitAnd: return "&=";
case BinOpTypeAssignBitXor: return "^=";
case BinOpTypeAssignBitOr: return "|=";
case BinOpTypeAssignMergeErrorSets: return "||=";
case BinOpTypeUnwrapOptional: return "orelse";
case BinOpTypeArrayCat: return "++";
case BinOpTypeArrayMult: return "**";
case BinOpTypeErrorUnion: return "!";
case BinOpTypeMergeErrorSets: 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 PrefixOpOptional: return "?";
case PrefixOpAddrOf: return "&";
}
zig_unreachable();
}
static const char *visib_mod_string(VisibMod mod) {
switch (mod) {
case VisibModPub: return "pub ";
case VisibModPrivate: return "";
}
zig_unreachable();
}
static const char *return_string(ReturnKind kind) {
switch (kind) {
case ReturnKindUnconditional: return "return";
case ReturnKindError: return "try";
}
zig_unreachable();
}
static const char *defer_string(ReturnKind kind) {
switch (kind) {
case ReturnKindUnconditional: return "defer";
case ReturnKindError: return "errdefer";
}
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 *export_string(bool is_export) {
return is_export ? "export " : "";
}
//static const char *calling_convention_string(CallingConvention cc) {
// switch (cc) {
// case CallingConventionUnspecified: return "";
// case CallingConventionC: return "extern ";
// case CallingConventionCold: return "coldcc ";
// case CallingConventionNaked: return "nakedcc ";
// case CallingConventionStdcall: return "stdcallcc ";
// }
// zig_unreachable();
//}
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";
}
static const char *thread_local_string(Token *tok) {
return (tok == nullptr) ? "" : "threadlocal ";
}
static const char *token_to_ptr_len_str(Token *tok) {
assert(tok != nullptr);
switch (tok->id) {
case TokenIdStar:
case TokenIdStarStar:
return "*";
case TokenIdBracketStarBracket:
return "[*]";
case TokenIdBracketStarCBracket:
return "[*c]";
default:
zig_unreachable();
}
}
static const char *node_type_str(NodeType node_type) {
switch (node_type) {
case NodeTypeFnDef:
return "FnDef";
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 NodeTypeTestDecl:
return "TestDecl";
case NodeTypeIntLiteral:
return "IntLiteral";
case NodeTypeFloatLiteral:
return "FloatLiteral";
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 NodeTypeIfBoolExpr:
return "IfBoolExpr";
case NodeTypeWhileExpr:
return "WhileExpr";
case NodeTypeForExpr:
return "ForExpr";
case NodeTypeSwitchExpr:
return "SwitchExpr";
case NodeTypeSwitchProng:
return "SwitchProng";
case NodeTypeSwitchRange:
return "SwitchRange";
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 NodeTypePtrDeref:
return "PtrDerefExpr";
case NodeTypeUnwrapOptional:
return "UnwrapOptional";
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 NodeTypeIfErrorExpr:
return "IfErrorExpr";
case NodeTypeIfOptional:
return "IfOptional";
case NodeTypeErrorSetDecl:
return "ErrorSetDecl";
case NodeTypeCancel:
return "Cancel";
case NodeTypeResume:
return "Resume";
case NodeTypeAwaitExpr:
return "AwaitExpr";
case NodeTypeSuspend:
return "Suspend";
case NodeTypePromiseType:
return "PromiseType";
case NodeTypePointerType:
return "PointerType";
}
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 (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 if (is_printable(c)) {
buf_append_char(dest, c);
} 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 bool statement_terminates_without_semicolon(AstNode *node) {
switch (node->type) {
case NodeTypeIfBoolExpr:
if (node->data.if_bool_expr.else_node)
return statement_terminates_without_semicolon(node->data.if_bool_expr.else_node);
return node->data.if_bool_expr.then_block->type == NodeTypeBlock;
case NodeTypeIfErrorExpr:
if (node->data.if_err_expr.else_node)
return statement_terminates_without_semicolon(node->data.if_err_expr.else_node);
return node->data.if_err_expr.then_node->type == NodeTypeBlock;
case NodeTypeIfOptional:
if (node->data.test_expr.else_node)
return statement_terminates_without_semicolon(node->data.test_expr.else_node);
return node->data.test_expr.then_node->type == NodeTypeBlock;
case NodeTypeWhileExpr:
return node->data.while_expr.body->type == NodeTypeBlock;
case NodeTypeForExpr:
return node->data.for_expr.body->type == NodeTypeBlock;
case NodeTypeCompTime:
return node->data.comptime_expr.expr->type == NodeTypeBlock;
case NodeTypeDefer:
return node->data.defer.expr->type == NodeTypeBlock;
case NodeTypeSuspend:
return node->data.suspend.block != nullptr && node->data.suspend.block->type == NodeTypeBlock;
case NodeTypeSwitchExpr:
case NodeTypeBlock:
return true;
default:
return false;
}
}
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 NodeTypeStructValueField:
zig_unreachable();
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 *export_str = export_string(node->data.fn_proto.is_export);
const char *inline_str = inline_string(node->data.fn_proto.is_inline);
fprintf(ar->f, "%s%s%s%sfn", pub_str, inline_str, export_str, extern_str);
if (node->data.fn_proto.name != nullptr) {
fprintf(ar->f, " ");
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 (param_decl->data.param_decl.name != nullptr) {
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 if (param_decl->data.param_decl.var_token != nullptr) {
fprintf(ar->f, "var");
} else {
render_node_grouped(ar, param_decl->data.param_decl.type);
}
if (arg_i + 1 < arg_count) {
fprintf(ar->f, ", ");
}
}
if (node->data.fn_proto.is_var_args) {
fprintf(ar->f, ", ...");
}
fprintf(ar->f, ")");
if (node->data.fn_proto.align_expr) {
fprintf(ar->f, " align(");
render_node_grouped(ar, node->data.fn_proto.align_expr);
fprintf(ar->f, ")");
}
if (node->data.fn_proto.section_expr) {
fprintf(ar->f, " section(");
render_node_grouped(ar, node->data.fn_proto.section_expr);
fprintf(ar->f, ")");
}
if (node->data.fn_proto.return_var_token != nullptr) {
fprintf(ar->f, "var");
} else {
AstNode *return_type_node = node->data.fn_proto.return_type;
assert(return_type_node != nullptr);
fprintf(ar->f, " ");
if (node->data.fn_proto.auto_err_set) {
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.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
}
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);
print_indent(ar);
render_node_grouped(ar, statement);
if (!statement_terminates_without_semicolon(statement))
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.name != nullptr) {
fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
}
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 *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_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.align_expr) {
fprintf(ar->f, "align(");
render_node_grouped(ar, node->data.variable_declaration.align_expr);
fprintf(ar->f, ") ");
}
if (node->data.variable_declaration.section_expr) {
fprintf(ar->f, "section(");
render_node_grouped(ar, node->data.variable_declaration.section_expr);
fprintf(ar->f, ") ");
}
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 NodeTypeFloatLiteral:
{
Buf rendered_buf = BUF_INIT;
buf_resize(&rendered_buf, 0);
bigfloat_append_buf(&rendered_buf, node->data.float_literal.bigfloat);
fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
}
break;
case NodeTypeIntLiteral:
{
Buf rendered_buf = BUF_INIT;
buf_resize(&rendered_buf, 0);
bigint_append_buf(&rendered_buf, node->data.int_literal.bigint, 10);
fprintf(ar->f, "%s", buf_ptr(&rendered_buf));
}
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:
{
if (!grouped) fprintf(ar->f, "(");
PrefixOp op = node->data.prefix_op_expr.prefix_op;
fprintf(ar->f, "%s", prefix_op_str(op));
AstNode *child_node = node->data.prefix_op_expr.primary_expr;
bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypePointerType;
render_node_extra(ar, child_node, new_grouped);
if (!grouped) fprintf(ar->f, ")");
break;
}
case NodeTypePointerType:
{
if (!grouped) fprintf(ar->f, "(");
const char *ptr_len_str = token_to_ptr_len_str(node->data.pointer_type.star_token);
fprintf(ar->f, "%s", ptr_len_str);
if (node->data.pointer_type.align_expr != nullptr) {
fprintf(ar->f, "align(");
render_node_grouped(ar, node->data.pointer_type.align_expr);
if (node->data.pointer_type.bit_offset_start != nullptr) {
assert(node->data.pointer_type.host_int_bytes != nullptr);
Buf offset_start_buf = BUF_INIT;
buf_resize(&offset_start_buf, 0);
bigint_append_buf(&offset_start_buf, node->data.pointer_type.bit_offset_start, 10);
Buf offset_end_buf = BUF_INIT;
buf_resize(&offset_end_buf, 0);
bigint_append_buf(&offset_end_buf, node->data.pointer_type.host_int_bytes, 10);
fprintf(ar->f, ":%s:%s ", buf_ptr(&offset_start_buf), buf_ptr(&offset_end_buf));
}
fprintf(ar->f, ") ");
}
if (node->data.pointer_type.is_const) {
fprintf(ar->f, "const ");
}
if (node->data.pointer_type.is_volatile) {
fprintf(ar->f, "volatile ");
}
render_node_ungrouped(ar, node->data.pointer_type.op_expr);
if (!grouped) fprintf(ar->f, ")");
break;
}
case NodeTypeFnCallExpr:
{
if (node->data.fn_call_expr.is_builtin) {
fprintf(ar->f, "@");
}
if (node->data.fn_call_expr.is_async) {
fprintf(ar->f, "async");
if (node->data.fn_call_expr.async_allocator != nullptr) {
fprintf(ar->f, "<");
render_node_extra(ar, node->data.fn_call_expr.async_allocator, true);
fprintf(ar->f, ">");
}
fprintf(ar->f, " ");
}
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypePointerType);
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;
if (lhs->type == NodeTypeErrorType) {
fprintf(ar->f, "error");
} else {
render_node_ungrouped(ar, lhs);
}
fprintf(ar->f, ".");
print_symbol(ar, rhs);
break;
}
case NodeTypePtrDeref:
{
AstNode *lhs = node->data.ptr_deref_expr.target;
render_node_ungrouped(ar, lhs);
fprintf(ar->f, ".*");
break;
}
case NodeTypeUnwrapOptional:
{
AstNode *lhs = node->data.unwrap_optional.expr;
render_node_ungrouped(ar, lhs);
fprintf(ar->f, ".?");
break;
}
case NodeTypeUndefinedLiteral:
fprintf(ar->f, "undefined");
break;
case NodeTypeContainerDecl:
{
if (!node->data.container_decl.is_root) {
const char *layout_str = layout_string(node->data.container_decl.layout);
const char *container_str = container_string(node->data.container_decl.kind);
fprintf(ar->f, "%s%s", layout_str, container_str);
if (node->data.container_decl.auto_enum) {
fprintf(ar->f, "(enum");
}
if (node->data.container_decl.init_arg_expr != nullptr) {
fprintf(ar->f, "(");
render_node_grouped(ar, node->data.container_decl.init_arg_expr);
fprintf(ar->f, ")");
}
if (node->data.container_decl.auto_enum) {
fprintf(ar->f, ")");
}
fprintf(ar->f, " {\n");
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);
if (field_node->data.struct_field.type != nullptr) {
fprintf(ar->f, ": ");
render_node_grouped(ar, field_node->data.struct_field.type);
}
if (field_node->data.struct_field.value != nullptr) {
fprintf(ar->f, " = ");
render_node_grouped(ar, field_node->data.struct_field.value);
}
fprintf(ar->f, ",\n");
}
for (size_t decl_i = 0; decl_i < node->data.container_decl.decls.length; decl_i += 1) {
AstNode *decls_node = node->data.container_decl.decls.at(decl_i);
render_node_grouped(ar, decls_node);
if (decls_node->type == NodeTypeUse ||
decls_node->type == NodeTypeVariableDeclaration ||
decls_node->type == NodeTypeFnProto)
{
fprintf(ar->f, ";");
}
fprintf(ar->f, "\n");
}
if (!node->data.container_decl.is_root) {
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 NodeTypePromiseType:
{
fprintf(ar->f, "promise");
if (node->data.promise_type.payload_type != nullptr) {
fprintf(ar->f, "->");
render_node_grouped(ar, node->data.promise_type.payload_type);
}
break;
}
case NodeTypeErrorType:
fprintf(ar->f, "anyerror");
break;
case NodeTypeAsmExpr:
{
AstNodeAsmExpr *asm_expr = &node->data.asm_expr;
const char *volatile_str = (asm_expr->volatile_token != nullptr) ? " volatile" : "";
fprintf(ar->f, "asm%s (\"%s\"\n", volatile_str, buf_ptr(&asm_expr->asm_template->data.str_lit.str));
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:
{
if (node->data.while_expr.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
}
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 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 NodeTypeIfErrorExpr:
{
fprintf(ar->f, "if (");
render_node_grouped(ar, node->data.if_err_expr.target_node);
fprintf(ar->f, ") ");
if (node->data.if_err_expr.var_symbol) {
const char *ptr_str = node->data.if_err_expr.var_is_ptr ? "*" : "";
const char *var_name = buf_ptr(node->data.if_err_expr.var_symbol);
fprintf(ar->f, "|%s%s| ", ptr_str, var_name);
}
render_node_grouped(ar, node->data.if_err_expr.then_node);
if (node->data.if_err_expr.else_node) {
fprintf(ar->f, " else ");
if (node->data.if_err_expr.err_symbol) {
fprintf(ar->f, "|%s| ", buf_ptr(node->data.if_err_expr.err_symbol));
}
render_node_grouped(ar, node->data.if_err_expr.else_node);
}
break;
}
case NodeTypeIfOptional:
{
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 NodeTypeCompTime:
{
fprintf(ar->f, "comptime ");
render_node_grouped(ar, node->data.comptime_expr.expr);
break;
}
case NodeTypeForExpr:
{
if (node->data.for_expr.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
}
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");
if (node->data.continue_expr.name != nullptr) {
fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
}
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, " catch ");
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 NodeTypeErrorSetDecl:
{
fprintf(ar->f, "error {\n");
ar->indent += ar->indent_size;
for (size_t i = 0; i < node->data.err_set_decl.decls.length; i += 1) {
AstNode *field_node = node->data.err_set_decl.decls.at(i);
assert(field_node->type == NodeTypeSymbol);
print_indent(ar);
print_symbol(ar, field_node->data.symbol_expr.symbol);
fprintf(ar->f, ",\n");
}
ar->indent -= ar->indent_size;
print_indent(ar);
fprintf(ar->f, "}");
break;
}
case NodeTypeCancel:
{
fprintf(ar->f, "cancel ");
render_node_grouped(ar, node->data.cancel_expr.expr);
break;
}
case NodeTypeResume:
{
fprintf(ar->f, "resume ");
render_node_grouped(ar, node->data.resume_expr.expr);
break;
}
case NodeTypeAwaitExpr:
{
fprintf(ar->f, "await ");
render_node_grouped(ar, node->data.await_expr.expr);
break;
}
case NodeTypeSuspend:
{
fprintf(ar->f, "suspend");
if (node->data.suspend.block != nullptr) {
render_node_grouped(ar, node->data.suspend.block);
}
break;
}
case NodeTypeParamDecl:
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);
}