///////////////////////////////////// //~ nbr: // ///////////////////////////////////// //~ nbr: Codegen TODOs // // [ ] Prefix output of property values with __PROPERTIES so we don't get name clashes Output_Language :: enum { HLSL; GLSL; // @Incomplete MLSL; // @Incomplete // SPIRV; // @Incomplete: Should we do this? } Codegen_State :: struct { path : string; current_scope : Scope_Handle; output_language : Output_Language; builder : String_Builder; result : *Compiler_Context; } Reserved_HLSL_Words :: string.[ "texture", "sampler", "matrix", "line", "precise", "shared", "triangle", "triangleadj", ]; Reserved_MLSL_Words :: string.[ "" ]; Reserved_GLSL_Words :: string.[ "" ]; init_codegen_state :: (state : *Codegen_State, result : *Compiler_Context, output_language : Output_Language) { state.current_scope = cast(Scope_Handle)1; state.output_language = output_language; init_string_builder(*state.builder); } indent :: (state : *Codegen_State, indentation : int) { for 1..indentation append(*state.builder, " "); } hlsl_type_to_string :: (type_variable : Type_Variable) -> string { if type_variable.type == { case .Invalid; return "{{invalid}}"; case .Unit; return "()"; case .Int; { return "int"; } case .Half; { return "half"; } case .Float; { return "float"; } case .Double; { return "double"; } case .Sampler; { return "SamplerState"; } case .Texture2D; { return "Texture2D"; } case .Function; #through; case .Struct; { return type_variable.typename; } case .Array; return "array"; } return ""; } emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) { find_result := find_symbol(state.result.scope_stack, node.name, state.current_scope); field := from_handle(state.result.type_variables, find_result.type_variable); indent(state, indentation); print_to_builder(*state.builder, "% ", hlsl_type_to_string(field)); if field.struct_field_parent { parent_tv := from_handle(state.result.type_variables, field.struct_field_parent.type_variable); if parent_tv.typename == "properties" { append(*state.builder, "__PROPERTIES__"); } } print_to_builder(*state.builder, "%", node.name); if field.type == .Sampler { print_to_builder(*state.builder, " : register(s%)", field.resource_index); } if field.type == .Texture2D { print_to_builder(*state.builder, " : register(t%)", field.resource_index); } if node.children.count == 1 { child := node.children[0]; print_to_builder(*state.builder, " = "); emit_node(state, child, 0); } if node.parent.kind == .Block { append(*state.builder, ";"); } for i :0..field.children.count - 1 { child := from_handle(state.result.type_variables, field.children[i]); emit_node(state, child.source_node, 0); } for hint : node.hint_tokens { if lookup_hint(hint.ident_value) == .Position { append(*state.builder, " : POSITION"); } else if lookup_hint(hint.ident_value) == .UV { append(*state.builder, " : TEXCOORD0"); } else if lookup_hint(hint.ident_value) == .Output_Position { append(*state.builder, " : SV_POSITION"); } } } emit_block :: (state : *Codegen_State, node : *AST_Node, indentation : int) { for statement : node.children { emit_node(state, statement, indentation); if it_index < node.children.count { append(*state.builder, "\n"); } } } emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) { indent(state, indentation); if node.name == "sample" { assert(node.children.count > 0); args := node.children[0]; emit_node(state, args.children[0], 0); append(*state.builder, "."); print_to_builder(*state.builder, "Sample("); for i : 1..args.children.count - 1 { child := args.children[i]; emit_node(state, child, 0); if i != args.children.count - 1 { append(*state.builder, ", "); } } } else if starts_with(node.name, "float") && node.children[0].children.count == 1 { args := node.children[0]; print_to_builder(*state.builder, "%(", node.name); number : string; number.data = *node.name.data[5]; number.count = node.name.count - 5; count := parse_int(*number, s32); for i : 0..count - 1 { child := args.children[0]; emit_node(state, child, 0); if i != count - 1 { append(*state.builder, ", "); } } } else { print_to_builder(*state.builder, "%(", node.name); if node.children.count > 0 { args := node.children[0]; for child : args.children { emit_node(state, child, 0); if it_index != args.children.count - 1 { append(*state.builder, ", "); } } } } append(*state.builder, ")"); } emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int) { find_result := find_symbol(state.result.scope_stack, ifx node.name.count > 0 then node.name else "properties", state.current_scope); if !find_result { message : Compiler_Message; message.message_kind = .Internal_Error; message.path = state.path; message.message = "Attempting to generate undeclared properties buffer. This should never happen at this stage."; array_add(*state.result.messages, message); } assert(find_result != null, "Attempting to generate undeclared properties buffer. This should never happen at this stage."); variable := from_handle(state.result.type_variables, find_result.type_variable); print_to_builder(*state.builder, "cbuffer __PROPERTIES : register(b%) \n{\n", variable.resource_index); previous_scope := state.current_scope; state.current_scope = variable.scope; resources : Static_Array(*AST_Node, 8); for child : node.children { if child.kind == .FieldList { for field : child.children { tv := from_handle(state.result.type_variables, field.type_variable); if tv.type == .Sampler || tv.type == .Texture2D { array_add(*resources, field); continue; } emit_node(state, field, 1); append(*state.builder, ";\n"); } } } append(*state.builder, "}\n\n"); for i : 0..resources.count - 1 { resource := resources[i]; emit_node(state, resource, 0); append(*state.builder, ";\n"); } append(*state.builder, "\n"); state.current_scope = previous_scope; } emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, emit_body := true) { name := get_actual_function_name(node); find_result := find_symbol(state.result.scope_stack, name, state.current_scope); assert(find_result != null, "Attempting to generate undeclared function. This should never happen at this stage."); if !find_result { message : Compiler_Message; message.message_kind = .Internal_Error; message.path = state.path; message.message = "Attempting to generate undeclared function. This should never happen at this stage."; array_add(*state.result.messages, message); } for func : find_result.functions { function_variable := from_handle(state.result.type_variables, func.type_variable); indent(state, indentation); if function_variable.return_type_variable { return_variable := from_handle(state.result.type_variables, function_variable.return_type_variable); print_to_builder(*state.builder, "% ", hlsl_type_to_string(return_variable)); } else { append(*state.builder, "void "); } print_to_builder(*state.builder, "%", node.name); previous_scope := state.current_scope; state.current_scope = function_variable.scope; append(*state.builder, "("); if node.children.count > 0 && node.children[0].kind == .FieldList { params := node.children[0]; for child : params.children { emit_node(state, child, 0); if it_index != params.children.count - 1 { append(*state.builder, ", "); } } } append(*state.builder, ")"); for hint : node.hint_tokens { if hint.ident_value == "position" { // @Incomplete(nb): Should be a lookup table somewhere append(*state.builder, " : SV_POSITION"); } if starts_with(hint.ident_value, "target") { // @Incomplete(nb): Should be a lookup table somewhere append(*state.builder, " : SV_TARGET"); } } if emit_body { append(*state.builder, "\n{\n"); if node.children.count > 1 { emit_block(state, node.children[1], indentation + 1); } append(*state.builder, "}\n"); } else { append(*state.builder, ";"); } append(*state.builder, "\n"); state.current_scope = previous_scope; } } emit_operator :: (state : *Codegen_State, op_kind : Token_Kind) { if op_kind == { case .TOKEN_PLUS; { append(*state.builder, "+"); } case .TOKEN_MINUS; { append(*state.builder, "-"); } case .TOKEN_STAR; { append(*state.builder, "*"); } case .TOKEN_SLASH; { append(*state.builder, "/"); } case .TOKEN_MINUSEQUALS; { append(*state.builder, "-="); } case .TOKEN_PLUSEQUALS; { append(*state.builder, "+="); } case .TOKEN_DIVEQUALS; { append(*state.builder, "/="); } case .TOKEN_TIMESEQUALS; { append(*state.builder, "*="); } case .TOKEN_MODEQUALS; { append(*state.builder, "%="); } case .TOKEN_ISEQUAL; { append(*state.builder, "=="); } case .TOKEN_ASSIGN; { append(*state.builder, "="); } case .TOKEN_ISNOTEQUAL; { append(*state.builder, "!="); } case .TOKEN_LOGICALOR; { append(*state.builder, "||"); } case .TOKEN_LOGICALAND; { append(*state.builder, "&&"); } case .TOKEN_LESS; { append(*state.builder, "<"); } case .TOKEN_LESSEQUALS; { append(*state.builder, "<="); } case .TOKEN_GREATER; { append(*state.builder, ">"); } case .TOKEN_GREATEREQUALS; { append(*state.builder, ">="); } } } emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) { if node.kind == { case .Integer; { print_to_builder(*state.builder, "%", node.integer_value); } case .Float; { print_to_builder(*state.builder, "%f", formatFloat(node.float_value, zero_removal=.ONE_ZERO_AFTER_DECIMAL)); } case .Properties; { } case .Field; { emit_field(state, node, indentation); } case .Block; { assert(false, "Not implemented yet: block"); } case .Variable; { indent(*state.builder, indentation); type_var := from_handle(state.result.type_variables, node.type_variable); is_properties := type_var.typename == "properties"; if !is_properties { if type_var.struct_field_parent { parent_tv := from_handle(state.result.type_variables, type_var.struct_field_parent.type_variable); if parent_tv.typename == "properties" { append(*state.builder, "__PROPERTIES__"); } } print_to_builder(*state.builder, "%", node.name); } if node.children.count > 0 { if !is_properties { append(*state.builder, "."); } emit_node(state, node.children[0], 0); } } case .Binary; { indent(*state.builder, indentation); if node.token.kind != .TOKEN_ASSIGN { append(*state.builder, "("); } lhs := node.children[0]; rhs := node.children[1]; emit_node(state, lhs, 0); append(*state.builder, " "); emit_operator(state, node.token.kind); append(*state.builder, " "); emit_node(state, rhs, 0); if node.token.kind != .TOKEN_ASSIGN { append(*state.builder, ")"); } } case .Unary; { indent(*state.builder, indentation); emit_operator(state, node.token.kind); emit_node(state, node.children[0], 0); } case .Expression_Statement; { emit_node(state, node.children[0], indentation); append(*state.builder, ";"); } case .Call; { emit_call(state, node, indentation); } case .Return; { indent(*state.builder, indentation); append(*state.builder, "return "); emit_node(state, node.children[0], 0); append(*state.builder, ";"); } case .For; { if node.parent.kind != .For { indent(*state.builder, indentation); } append(*state.builder, "for "); loop_ident := node.token.ident_value; begin_val := node.children[0].integer_value; end_val := node.children[1].integer_value; print_to_builder(*state.builder, "(int % = %; % < %; %++)\n", loop_ident, begin_val, loop_ident, end_val, loop_ident); indent(*state.builder, indentation); append(*state.builder, "{\n"); emit_block(state, node.children[2], indentation + 1); indent(*state.builder, indentation); append(*state.builder, "}\n"); } case .If; { if node.parent.kind != .If { indent(*state.builder, indentation); } append(*state.builder, "if "); cond := node.children[0]; emit_node(state, cond, 0); body := node.children[1]; append(*state.builder, "\n"); indent(*state.builder, indentation); append(*state.builder, "{\n"); emit_block(state, body, indentation + 1); indent(*state.builder, indentation); append(*state.builder, "}\n"); if node.children.count == 3 { emit_else(state, node.children[2], indentation); } } } } emit_else :: (state : *Codegen_State, node : *AST_Node, indentation : int) { indent(*state.builder, indentation); append(*state.builder, "else "); if node.kind == .If { emit_node(state, node, indentation); } else if node.kind == .Block { append(*state.builder, "\n"); indent(*state.builder, indentation); append(*state.builder, "{\n"); emit_block(state, node, indentation + 1); indent(*state.builder, indentation); append(*state.builder, "}"); } } emit_field_list :: (state : *Codegen_State, field_list : *AST_Node, indentation : int) { for child : field_list.children { emit_node(state, child, 1); if it_index < field_list.children.count { append(*state.builder, ";\n"); } } } emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int) { print_to_builder(*state.builder, "struct %", node.name); current_scope := state.current_scope; state.current_scope = from_handle(state.result.type_variables, node.type_variable).scope; field_list := node.children[0]; if field_list.children.count > 0 { append(*state.builder, "\n{\n"); } else { append(*state.builder, " {"); } emit_field_list(state, field_list, indentation); append(*state.builder, "};\n\n"); state.current_scope = current_scope; } emit_cbuffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) { variable := from_handle(state.result.type_variables, node.type_variable); print_to_builder(*state.builder, "cbuffer % : register(b%)", variable.name, variable.resource_index); current_scope := state.current_scope; state.current_scope = from_handle(state.result.type_variables, node.type_variable).scope; field_list := node.children[0]; if field_list.children.count > 0 { append(*state.builder, "\n{\n"); } else { append(*state.builder, " {"); } emit_field_list(state, field_list, indentation); append(*state.builder, "}\n\n"); state.current_scope = current_scope; } emit_declaration :: (state : *Codegen_State, node : *AST_Node) { if node.kind == { case .Function; { emit_function(state, node, 0); } case .Properties; { emit_properties(state, node, 0); } case .CBuffer; { emit_cbuffer(state, node, 0); } case .Struct; { emit_struct(state, node, 0); } } } codegen :: (result : *Compiler_Context, allocator := temp) { codegen(result, .HLSL); } codegen :: (result : *Compiler_Context, output_language : Output_Language, allocator := temp) { if result.had_error { return; } new_context := context; new_context.allocator = allocator; push_context new_context { init_context_allocators(); defer clear_context_allocators(); state : Codegen_State; state.result = result; state.current_scope = cast(Scope_Handle)1; state.output_language = output_language; init_string_builder(*state.builder); codegen(*state); } } #scope_file codegen :: (state : *Codegen_State) { found_function : bool = false; // found_struct : bool = false; // for variable : state.result.type_variables { // if variable.type == .Struct && variable.kind == .Declaration && !variable.builtin { // if variable.source_node.kind == .Properties continue; // if variable.source_node.kind == .Meta continue; // print_to_builder(*state.builder, "struct %;\n", variable.source_node.name); // found_struct = true; // } // } // if found_struct { // append(*state.builder, "\n"); // } for variable : state.result.type_variables { if variable.type == .Function && !variable.builtin && !variable.source_node.vertex_entry_point && !variable.source_node.pixel_entry_point { emit_function(state, variable.source_node, 0, false); found_function = true; } } if found_function { append(*state.builder, "\n"); } for declaration : state.result.root.children { if declaration.foreign_declaration { continue; } emit_declaration(state, declaration); } state.result.codegen_result_text = builder_to_string(*state.builder); } #scope_module #import "ncore";