///////////////////////////////////// //~ nbr: Node data structure // // [ ] Add a way to infer or get file path from a node AST_Kind :: enum { Program; Function; Return; // @Incomplete(nb): Should these three really be their own block types? // Maybe they at least shouldn't need to have their own tokens... Properties; Meta; Instance; //== // Hint; // Type; // Operator; Call; Struct; If; CBuffer; FieldList; ArgList; Variable; Binary; Unary; Integer; Float; Expression_Statement; Field; Unnamed_Field; Block; Error; } AST_Node :: struct { kind : AST_Kind; // @Note(niels): Children nodes can be interpreted as anything useful. // for an if-statement we would have at most 2 children // a property block has a field list child node which has // a child node for each field declaration etc. children : [..]*AST_Node; parent : *AST_Node; // @Note(niels): Every node can have a name, but most nodes don't. A function or field declaration has one, // but an if-statement does not name : string; integer_value : int; float_value : float; token : Token; array_field : bool; source_location : Source_Range; type_variable : Type_Variable_Handle; foreign_declaration : bool; // @Incomplete(nb): Change this to just be children and a single token_data field, // then we can add new node types (hint, typespec, operator) and have them // as children instead. hint_tokens : [..]Token; vertex_entry_point : bool; pixel_entry_point : bool; } // =========================================================== // Pretty printing pretty_print_call :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "("); append(builder, node.name); if node.children.count > 0 { append(builder, " "); pretty_print_children(node.children[0], indentation, builder, flags = 0, skip_indent = true); } append(builder, ")"); } pretty_print_arglist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "["); pretty_print_children(node, indentation + 1, builder, flags = .NewLine); append(builder, "]"); } pretty_print_fieldlist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "["); pretty_print_children(node, indentation + 1, builder, flags = .NewLine); append(builder, "]"); } pretty_print_field :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } print_to_builder(builder, tprint("(:= %", node.name)); if node.kind != .Unnamed_Field && node.token.ident_value.count > 0 { if node.array_field { append(builder, " ["); pretty_print_node(node.children[0], indentation, builder, true); append(builder, "]."); print_to_builder(builder, "%", node.token.ident_value); } else { print_to_builder(builder, " %", node.token.ident_value); } } for hint : node.hint_tokens { if hint.string_value.count > 0 { print_to_builder(builder, " (@%)", hint.string_value); } } if !node.array_field && node.children.count > 0 { append(builder, " "); pretty_print_node(node.children[0], indentation, builder, true); } append(builder, ")"); } Children_Print_Flags :: enum_flags { NewLine :: 1 << 0; Separator :: 1 << 1; Space :: 1 << 2; Dont_Skip_Indent_On_First :: 1 << 3; } pretty_print_block :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if node.children.count == 0 { if !skip_indent { indent(builder, indentation); } append(builder, "()"); } else { flags := Children_Print_Flags.NewLine; if !skip_indent { flags |= .Dont_Skip_Indent_On_First; } pretty_print_children(node, indentation, builder, flags); } } pretty_print_children :: (parent : *AST_Node, indentation : int, builder : *String_Builder, flags : Children_Print_Flags = .Separator, skip_indent := false) { if !parent { return; } children := parent.children; for child : children { // if it_index > 0 { // indent(builder, indentation); // } if !child continue; ind := indentation; if flags & .Dont_Skip_Indent_On_First { ind = indentation; } else { if it_index == 0 { ind = 0; } } if skip_indent{ ind = 0; } // skip := ifx it_index > 0 then false else true; pretty_print_node(child, ind, builder); if it_index != children.count - 1 { if flags & .Separator { append(builder, ","); } append(builder, " "); if flags & .NewLine { append(builder, "\n"); } } } } op_to_string :: (oper : Token) -> string { if oper.kind == { case .TOKEN_PLUS; return "+"; case .TOKEN_MINUS; return "-"; case .TOKEN_STAR; return "*"; case .TOKEN_SLASH; return "/"; case .TOKEN_MINUSEQUALS; return "-="; case .TOKEN_PLUSEQUALS; return "+="; case .TOKEN_DIVEQUALS; return "/="; case .TOKEN_TIMESEQUALS; return "*="; case .TOKEN_MODEQUALS; return "%="; case .TOKEN_ISEQUAL; return "=="; case .TOKEN_ASSIGN; return "="; case .TOKEN_ISNOTEQUAL; return "!="; case .TOKEN_LOGICALOR; return "||"; case .TOKEN_LOGICALAND; return "&&"; case .TOKEN_LESS; return "<"; case .TOKEN_LESSEQUALS; return "<="; case .TOKEN_GREATER; return ">"; case .TOKEN_GREATEREQUALS; return ">="; } return ""; } pretty_print_binary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "("); op := node.token; print_to_builder(builder, op_to_string(op)); append(builder, " "); pretty_print_node(node.children[0], 0, builder); append(builder, " "); pretty_print_node(node.children[1], 0, builder); append(builder, ")"); } pretty_print_unary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } op := node.token; print_to_builder(builder, op_to_string(op)); pretty_print_node(node.children[0], 0, builder); } print_return_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "(return "); pretty_print_children(node, indentation, builder); append(builder, ")"); } pretty_print_if :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "(if "); condition := node.children[0]; pretty_print_node(condition, 0, builder); append(builder, "\n"); body := node.children[1]; // indent(builder,indentation + 4); // append(builder, "("); pretty_print_node(body, indentation + 4, builder); // append(builder, ")"); if node.children.count == 3 { append(builder, "\n"); pretty_print_node(node.children[2], indentation + 4, builder); } append(builder, ")"); } print_expression_statement :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } if node.children[0] { pretty_print_node(node.children[0], 0, builder); } } pretty_print_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if node.kind == { case .Return; { print_return_node(node, indentation, builder, skip_indent); } case .If; { pretty_print_if(node, indentation, builder, skip_indent); } case .Struct; case .ArgList; { pretty_print_arglist(node, indentation + 2, builder, skip_indent); } case .FieldList; { pretty_print_fieldlist(node, indentation + 2, builder, skip_indent); } case .Field; { pretty_print_field(node, indentation, builder, skip_indent); } case .Unnamed_Field; { pretty_print_field(node, indentation, builder, skip_indent); } case .Block; { pretty_print_block(node, indentation, builder, skip_indent); } case .Binary; { pretty_print_binary(node, indentation, builder, skip_indent); } case .Unary; { pretty_print_unary(node, indentation, builder, skip_indent); } case .Variable; { pretty_print_variable(node, indentation, builder, skip_indent); } case .Expression_Statement; { print_expression_statement(node, indentation, builder, skip_indent); } case .Integer; { print_to_builder(builder, "%", node.integer_value, skip_indent); } case .Float; { print_to_builder(builder, "%", node.float_value, skip_indent); } case .Call; { pretty_print_call(node, indentation, builder, skip_indent); } case .Error; { print_to_builder(builder, "(error \"%\")", node.name, skip_indent); } } } pretty_print_variable :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } print_to_builder(builder, "%", node.name); for child : node.children { if child.kind == .Variable { append(builder, "."); pretty_print_variable(child, indentation, builder, skip_indent = true); } else if child.kind == .Unary { append(builder, "["); pretty_print_node(child.children[0], 0, builder); append(builder, "]"); } } } pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) { if !skip_indent { indent(builder, indentation); } append(builder, "("); if declaration.foreign_declaration { append(builder, "foreign "); } if declaration.kind == .Function { append(builder, "fun "); } if declaration.vertex_entry_point { append(builder, "vertex "); } if declaration.pixel_entry_point { append(builder, "pixel "); } if declaration.kind == .Properties { append(builder, "properties"); if declaration.name.count > 0 { print_to_builder(builder, " %", declaration.name); } } else if declaration.kind == .Instance { append(builder, "instance"); } else if declaration.kind == .Meta { append(builder, "meta"); } else { if declaration.kind == .Struct { append(builder, "struct "); } else if declaration.kind == .CBuffer { append(builder, "constant_buffer "); } print_to_builder(builder, "%", declaration.name); } if declaration.kind == .Function && declaration.token.kind == .TOKEN_IDENTIFIER{ print_to_builder(builder, " -> %", declaration.token.ident_value); for hint : declaration.hint_tokens { if hint.string_value.count > 0 { print_to_builder(builder, " (@%)", hint.string_value); } } } if declaration.children.count > 0 { // print_to_builder(builder, "\n"); // if declaration.kind == .Function { // field_list := declaration.children[0]; // pretty_print_fieldlist(field_list, indentation + 1, builder); // append(builder, "\n"); // if declaration.children.count > 1 { // body := declaration.children[1]; // pretty_print_node(body, indentation + 1, builder, true); // } // } else if declaration.kind == .Struct { // pretty_print_node(declaration.children[0], indentation + 1, builder); // } else { // pretty_print_node(declaration.children[0], indentation + 1, builder); // } print_to_builder(builder, "\n"); pretty_print_children(declaration, indentation + 1, builder, flags = .NewLine); } append(builder, ")"); } pretty_print_ast :: (root : *AST_Node, allocator : Allocator) -> string { builder : String_Builder; init_string_builder(*builder,, allocator); indentation := 0; append(*builder, "("); append(*builder, "program\t\n"); indentation += 1; declarations := root.children; for declaration : declarations { pretty_print_declaration(declaration, indentation, *builder); if it_index < declarations.count - 1 { append(*builder, "\n\n"); } } append(*builder, ")"); return builder_to_string(*builder,, allocator); } #scope_file indent :: (builder : *String_Builder, indentation : int) { for 0..indentation - 1 { append(builder, " "); } }