525 lines
12 KiB
Plaintext
525 lines
12 KiB
Plaintext
/////////////////////////////////////
|
|
//~ 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;
|
|
//==
|
|
// Directives
|
|
If_Directive;
|
|
|
|
// Hint;
|
|
// Type;
|
|
// Operator;
|
|
Call;
|
|
Struct;
|
|
If;
|
|
For;
|
|
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;
|
|
|
|
if child.kind == .Function {
|
|
pretty_print_declaration(child, ind, builder);
|
|
} else {
|
|
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, ")");
|
|
}
|
|
|
|
pretty_print_for :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
|
if !skip_indent {
|
|
indent(builder, indentation);
|
|
}
|
|
|
|
append(builder, "(for ");
|
|
|
|
loop_iterator := node.token;
|
|
print_to_builder(builder, "% : ", loop_iterator.ident_value);
|
|
|
|
pretty_print_node(node.children[0], 0, builder);
|
|
append(builder, "..");
|
|
pretty_print_node(node.children[1], 0, builder);
|
|
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 .For; {
|
|
pretty_print_for(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 == .If_Directive {
|
|
append(builder, "#if ");
|
|
}
|
|
|
|
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 {
|
|
if declaration.kind == .If_Directive {
|
|
pretty_print_node(declaration.children[0], 0, builder);
|
|
append(builder, "\n");
|
|
pretty_print_node(declaration.children[1], indentation + 5, builder);
|
|
} else {
|
|
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, " ");
|
|
}
|
|
}
|