Files
Ink-Shader-Language/AST.jai

582 lines
14 KiB
Plaintext

/////////////////////////////////////
//~ nbr: Node data structure
//
// [ ] Add a way to infer or get file path from a node
AST_Kind :: enum {
Program;
Function;
Return;
//==
// Directives
If_Directive;
Access;
Call;
Struct;
If;
For;
CBuffer;
Buffer;
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);
}
if node.token.kind == .TOKEN_LEFTBRACKET {
pretty_print_node(node.children[0], 0, builder);
append(builder, "[");
pretty_print_node(node.children[1], 0, builder);
append(builder, "]");
} else {
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_access :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
if !skip_indent {
indent(builder, indentation);
}
pretty_print_node(node.children[0], 0, builder);
append(builder, ".");
pretty_print_node(node.children[1], 0, 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 .If_Directive; {
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 { //@Note: Else branch
append(builder, "\n");
pretty_print_node(node.children[2], indentation + 4, builder);
}
append(builder, ")");
}
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 .Access; {
pretty_print_access(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 == .Struct {
append(builder, "struct ");
} else if declaration.kind == .CBuffer {
append(builder, "constant_buffer ");
} else if declaration.kind == .Buffer {
append(builder, "buffer ");
}
print_to_builder(builder, "%", declaration.name);
if declaration.kind == .CBuffer || declaration.kind == .Buffer{
for hint : declaration.hint_tokens {
if hint.string_value.count > 0 {
print_to_builder(builder, " (@%)", hint.string_value);
}
}
// if declaration.kind != .If_Directive {
// 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);
if declaration.children.count > 2 {
append(builder, "\n");
if declaration.children[2].kind == .If_Directive {
pretty_print_declaration(declaration.children[2], indentation + 5, builder);
} else {
pretty_print_node(declaration.children[2], indentation + 5, builder);
}
}
} else {
print_to_builder(builder, "\n");
flags := Children_Print_Flags.NewLine;
if declaration.parent && declaration.parent.parent {
if declaration.parent.parent.kind == .If_Directive {
indent(builder, indentation - 1); //@Note: Hack the indent for now... Wow this is stupid, but it works!
}
}
pretty_print_children(declaration, indentation + 1, builder, flags = flags);
}
}
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, " ");
}
}