///////////////////////////////////// //~ nbr: // ///////////////////////////////////// //~ nbr: Codegen TODOs // 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; ctx : *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, ctx : *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 :: (variables : []Type_Variable, type_handle : Type_Variable_Handle) -> string { return hlsl_type_to_string(variables, from_handle(variables, type_handle)); } hlsl_type_to_string :: (variables : []Type_Variable, 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 hlsl_type_to_string(variables, type_variable.element_type); } return ""; } emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) { find_result := find_symbol(state.ctx.scope_stack, node.name, state.current_scope); field := from_handle(state.ctx.type_variables, find_result.type_variable); indent(state, indentation); print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, field)); 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]; if field.type == .Array { append(*state.builder, "["); emit_node(state, child, 0); append(*state.builder, "]"); } else { 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.ctx.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) { previous_scope := state.current_scope; for statement : node.children { if statement.type_variable { state.current_scope = from_handle(state.ctx.type_variables, statement.type_variable).scope; } emit_node(state, statement, indentation); if it_index < node.children.count { append(*state.builder, "\n"); } } state.current_scope = previous_scope; } 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_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, emit_body := true) { name := get_actual_function_name(node); find_result := find_symbol(state.ctx.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.ctx.messages, message); } for func : find_result.functions { function_variable := from_handle(state.ctx.type_variables, func.type_variable); indent(state, indentation); if function_variable.return_type_variable { return_variable := from_handle(state.ctx.type_variables, function_variable.return_type_variable); print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, 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 .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.ctx.type_variables, node.type_variable); print_to_builder(*state.builder, "%", node.name); if node.children.count > 0 { append(*state.builder, "."); emit_node(state, node.children[0], 0); } } case .Access; { indent(*state.builder, indentation); lhs := node.children[0]; rhs := node.children[1]; emit_node(state, lhs, 0); print_to_builder(*state.builder, "%.", node.name); emit_node(state, rhs, 0); } case .Binary; { indent(*state.builder, indentation); if node.token.kind != .TOKEN_ASSIGN && node.token.kind != .TOKEN_LEFTBRACKET { if node.parent.kind == .Binary && node.parent.token.kind != .TOKEN_ASSIGN { append(*state.builder, "("); } } lhs := node.children[0]; rhs := node.children[1]; if node.token.kind == .TOKEN_LEFTBRACKET { emit_node(state, lhs, 0); append(*state.builder, "["); emit_node(state, rhs, 0); append(*state.builder, "]"); } else { 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 && node.token.kind != .TOKEN_LEFTBRACKET { if node.parent.kind == .Binary && node.parent.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]; append(*state.builder, "("); emit_node(state, cond, 0); append(*state.builder, ")"); 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.ctx.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.ctx.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.ctx.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_buffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) { variable := from_handle(state.ctx.type_variables, node.type_variable); element := from_handle(state.ctx.type_variables, variable.element_type); emit_struct(state, node, indentation); // print_to_builder(*state.builder, "struct %\n", element.name); print_to_builder(*state.builder, "StructuredBuffer<%> %;\n\n", element.typename, variable.name); } emit_declaration :: (state : *Codegen_State, node : *AST_Node) { if node.kind == { case .Function; { emit_function(state, node, 0); } case .CBuffer; { emit_cbuffer(state, node, 0); } case .Buffer; { emit_buffer(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.ctx = 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.ctx.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.ctx.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.ctx.root.children { if declaration.foreign_declaration { continue; } emit_declaration(state, declaration); } state.ctx.codegen_result_text = builder_to_string(*state.builder); } #scope_module #import "ncore";