Files
Ink-Shader-Language/module.jai
Niels Bross 603b625e21 Rename of files, improved for handling, add cb hints
- Rename Test to Ink as main file
- Support more errors for for loops.
- Add hints to cbs
2025-09-02 11:55:27 +02:00

491 lines
11 KiB
Plaintext

#load "Lexing.jai";
#load "Error.jai";
#load "Parsing.jai";
#load "Semantic_Analysis.jai";
#load "Codegen.jai";
add_define :: (env : *Environment, key : string) {
for define : env.defines {
if define == key {
return;
}
}
array_add(*env.defines, key);
}
remove_define :: (env : *Environment, key : string) {
for define : env.defines {
if define == key {
env.defines[it_index] = env.defines[env.defines.count - 1];
}
}
}
Environment :: struct {
defines : [..]string;
}
Shader_Compiler :: struct {
environment : Environment;
}
Field_Kind :: enum {
Int :: 0;
Half :: 1;
Float :: 2;
Double :: 3;
Texture2D :: 8;
Sampler :: 9;
Function;
Struct;
Array;
}
Field_Type :: struct {
kind : Field_Kind;
name : string; //@Note(niels): for structs
children : [..]Field;
}
Hint_Kind :: enum {
None;
Position;
UV;
Target;
Custom;
}
Field_Hint :: struct {
kind : Hint_Kind;
target_index : int;
custom_hint_name : string;
}
Field :: struct {
name : string;
type : Field_Type;
resource_index : u32;
hints : [..]Field_Hint;
}
Entry_Point :: struct {
name : string;
function_input : [..]Field;
return_value : Field;
}
Shader_Variant :: struct {
text : string;
vertex_entry_point : struct {
name : string;
input : [..]Field;
}
pixel_entry_point : struct {
name : string;
return_value : Field;
}
}
Property_Field :: struct {
base_field : Field;
// @Incomplete(nb): Editor information, min max, etc.
// This should also be compiled out for ship
}
Properties :: struct {
fields : [..]Property_Field;
buffer_index : u32;
}
Constant_Buffer :: struct {
register : int;
name : string;
fields : Static_Array(Property_Field, 16);
// hints : Field_Hint; // optional hint...
hints : [..]Field_Hint;
buffer_index : u32;
}
Shader_Variant_Collection :: struct {
properties : Properties;
max_constant_buffers :: 16;
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
variants : [..]Shader_Variant;
}
Input_File :: struct {
source : string;
path : string;
}
Token_Stream :: struct {
tokens : [..]Token;
}
Compile_Result :: struct {
file : Input_File;
tokens : Token_Stream;
root : *AST_Node;
nodes : [..]AST_Node;
codegen_result_text : string;
constant_buffers : Static_Array(Type_Variable_Handle, 16);
scope_stack : Scope_Stack;
type_variables : [..]Type_Variable;
property_name : string;
vertex_entry_point : struct {
node : *AST_Node;
name : string;
input : [..]Field;
}
pixel_entry_point : struct {
node : *AST_Node;
name : string;
return_value : Field;
}
properties : Properties;
max_constant_buffers :: 16;
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
had_error : bool;
messages : [..]Compiler_Message;
allocator : Allocator;
arena : Arena;
}
record_error :: (result : *Compile_Result, format : string, args : .. Any) {
error : Compiler_Message;
error.message_kind = .Error;
error.message = sprint(format, args);
array_add(*result.messages, error);
}
make_file :: (result : *Compile_Result, path : string) -> Input_File {
file_string, ok := read_entire_file(path);
if !ok {
record_error(result, "Unable to load file: %", path);
return .{};
}
return make_file_from_string(file_string, path);
}
make_file_from_string :: (source : string, path : string = "") -> Input_File {
input_file : Input_File;
input_file.source = source;
input_file.path = path;
return input_file;
}
pretty_print_field :: (field : *Field) -> string {
builder : String_Builder;
init_string_builder(*builder,, temp);
pretty_print_field(*builder, field);
return builder_to_string(*builder);
}
Min_Field_Name :: 10;
pretty_print_field :: (builder : *String_Builder, field : *Field) {
if field.name.count > 0 {
print_to_builder(builder, "% ", field.name);
append(builder, "- ");
} else {
append(builder, "return - ");
}
type := field.type;
if type.kind == {
case .Int; {
append(builder, "int");
}
case .Half; {
append(builder, "half");
}
case .Float; {
append(builder, "float");
}
case .Double; {
append(builder, "double");
}
case .Texture2D; {
append(builder, "texture2D");
}
case .Sampler; {
append(builder, "sampler");
}
case .Struct; {
print_to_builder(builder, "struct : % {", type.name);
for *child : type.children {
pretty_print_field(builder, child);
if it_index < type.children.count - 1 {
append(builder, " ");
}
}
append(builder, "} ");
}
case .Array; {
}
}
for hint : field.hints {
if hint.kind == {
case .Position; {
append(builder, "(@position)");
}
case .Target; {
print_to_builder(builder, "(@target%)", hint.target_index);
}
case .Custom; {
print_to_builder(builder, "(@%)", hint.custom_hint_name);
}
}
if it_index != field.hints.count - 1 {
append(builder, ", ");
}
}
}
type_variable_to_field :: (checker : *Semantic_Checker, variable : Type_Variable_Handle) -> Field {
return type_variable_to_field(checker, from_handle(checker, variable));
}
type_variable_to_field :: (type_variables : []Type_Variable, scope_stack : Scope_Stack, variable : *Type_Variable) -> Field {
field : Field;
field.name = variable.name;
type : Field_Type;
if variable.type == {
case .Int; {
type.kind = Field_Kind.Int;
}
case .Half; {
type.kind = Field_Kind.Half;
}
case .Float; {
type.kind = Field_Kind.Float;
}
case .Double; {
type.kind = Field_Kind.Double;
}
case .Texture2D; {
type.kind = Field_Kind.Texture2D;
field.resource_index = variable.resource_index;
}
case .Sampler; {
type.kind = Field_Kind.Sampler;
field.resource_index = variable.resource_index;
}
case .Struct; {
type.kind = Field_Kind.Struct;
find_result := find_symbol(scope_stack, variable.typename, xx 1);
assert(find_result != null, "Internal compiler error\n");
type_var := from_handle(type_variables, find_result.type_variable);
for i : 0..type_var.children.count - 1 {
child := type_var.children[i];
child_field := type_variable_to_field(type_variables, scope_stack, child);
array_add(*type.children, child_field);
}
type.name = variable.typename;
}
}
for hint : variable.source_node.hint_tokens {
field_hint : Field_Hint;
if hint.ident_value == "position" {
// @Incomplete(nb): Should be a lookup table somewhere
field_hint.kind = .Position;
} else if hint.ident_value == "uv" {
field_hint.kind = .UV;
} else if starts_with(hint.ident_value, "target") {
// @Incomplete(nb): Should be a lookup table somewhere
index_str : string;
index_str.data = *hint.ident_value.data[7];
index_str.count = 1;
result, ok, remainder := string_to_int(index_str);
if ok {
field_hint.target_index = result;
}
field_hint.kind = .Target;
} else {
field_hint.custom_hint_name = hint.ident_value;
field_hint.kind = .Custom;
}
array_add(*field.hints, field_hint);
}
field.type = type;
return field;
}
type_variable_to_field :: (type_variables : []Type_Variable, scope_stack : Scope_Stack, variable : Type_Variable_Handle) -> Field {
return type_variable_to_field(type_variables, scope_stack, from_handle(type_variables, variable));
}
type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variable) -> Field {
return type_variable_to_field(checker.result.type_variables, checker.result.scope_stack, variable);
}
generate_output_data :: (result : *Compile_Result) {
if result.had_error {
return;
}
if result.vertex_entry_point.node {
result.vertex_entry_point.name = result.vertex_entry_point.node.name;
type_variable := from_handle(result.type_variables, result.vertex_entry_point.node.type_variable);
assert(type_variable.type == .Function);
node := type_variable.source_node;
if node.children.count > 0 {
if node.children[0].kind == .FieldList {
field_list := node.children[0];
for child : field_list.children {
tv := from_handle(result.type_variables, child.type_variable);
field := type_variable_to_field(result.type_variables, result.scope_stack, tv);
array_add(*result.vertex_entry_point.input, field);
}
}
}
}
for buffer_variable : result.constant_buffers {
variable := from_handle(result.type_variables, buffer_variable);
cb := array_add(*result.cbuffers);
for i : 0..variable.children.count - 1 {
child := variable.children[i];
field : Property_Field;
field.base_field = type_variable_to_field(result.type_variables, result.scope_stack, from_handle(result.type_variables, child));
array_add(*cb.fields, field);
}
cb.buffer_index = variable.resource_index;
for hint : variable.source_node.hint_tokens {
field_hint : Field_Hint;
field_hint.custom_hint_name = hint.ident_value;
field_hint.kind = .Custom;
array_add(*cb.hints, field_hint);
}
}
find_result := find_symbol(*result.scope_stack, result.property_name, xx 1);
if find_result {
property_variable := from_handle(result.type_variables, find_result.type_variable);
for i : 0..property_variable.children.count - 1 {
child := property_variable.children[i];
field := type_variable_to_field(result.type_variables, result.scope_stack, from_handle(result.type_variables, child));
prop_field : Property_Field;
prop_field.base_field = field;
array_add(*result.properties.fields, prop_field);
}
result.properties.buffer_index = property_variable.resource_index;
}
if result.pixel_entry_point.node {
result.pixel_entry_point.name = result.pixel_entry_point.node.name;
type_variable := from_handle(result.type_variables, result.pixel_entry_point.node.type_variable);
assert(type_variable.type == .Function);
field := type_variable_to_field(result.type_variables, result.scope_stack, type_variable.return_type_variable);
for hint : type_variable.source_node.hint_tokens {
field_hint : Field_Hint;
if hint.ident_value == "position" {
// @Incomplete(nb): Should be a lookup table somewhere
field_hint.kind = .Position;
} else if starts_with(hint.ident_value, "target") {
// @Incomplete(nb): Should be a lookup table somewhere
index_str : string;
index_str.data = *hint.ident_value.data[7];
index_str.count = 1;
result, ok, remainder := string_to_int(index_str);
if ok {
field_hint.target_index = result;
}
field_hint.kind = .Target;
} else {
// @Incomplete(nb): custom hints
}
array_add(*field.hints, field_hint);
}
result.pixel_entry_point.return_value = field;
}
}
compile_file :: (compiler : *Shader_Compiler, path : string) -> Compile_Result {
result : Compile_Result;
result.allocator = make_arena(*result.arena);
result.file = make_file(*result, path);
lex(*result);
parse(*result);
check(*result);
codegen(*result);
generate_output_data(*result);
return result;
}