Added Ink and ncore to modules
This commit is contained in:
519
module.jai
Normal file
519
module.jai
Normal file
@@ -0,0 +1,519 @@
|
||||
#load "lexing.jai";
|
||||
#load "error.jai";
|
||||
#load "parsing.jai";
|
||||
#load "check.jai";
|
||||
#load "codegen.jai";
|
||||
|
||||
#import "File_Utilities";
|
||||
|
||||
/* TODO
|
||||
- [x] Remove builtin stringbuilding and replace it with ad-hoc string building when error reporting. In that case we are already building a string anyway, so we can just pass in the string builder
|
||||
- [ ] Support structured buffers (ro, rw, w)
|
||||
- [ ] Support mesh and amplification shaders
|
||||
- [ ] Support compute shaders
|
||||
- [x] Support #if at top level
|
||||
- [x] Support #if at block level
|
||||
- [x] Remove properties block and just use hinted constant buffers instead
|
||||
```
|
||||
props :: constant_buffer @properties {
|
||||
[...]
|
||||
}
|
||||
```
|
||||
- [ ] while loops
|
||||
- [ ] for-each loops
|
||||
- [ ] add parameters to hints (meta properties, resource binding indices if needed)
|
||||
- [ ] consider @entry(stage) syntax instead of the forced keyword
|
||||
- [ ] Add flags to compiler
|
||||
- [ ] Generate output flag(s)
|
||||
- [ ] Possibly final stage flag, so you can just call compile_file and it only does what you need.
|
||||
- Probably this flag is about which stage you need as the _last_ and not which stages to do, as that doesn't make sense.
|
||||
- [ ] Multiple output languages?
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
Output_Position;
|
||||
|
||||
Custom;
|
||||
}
|
||||
|
||||
Hint_Names :: #run -> [(cast(int)Hint_Kind.Target) + 1]string {
|
||||
names : [(cast(int)Hint_Kind.Target) + 1]string;
|
||||
names[Hint_Kind.Position] = "position";
|
||||
names[Hint_Kind.UV] = "uv";
|
||||
names[Hint_Kind.Target] = "target";
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
lookup_hint :: (name : string) -> Hint_Kind {
|
||||
if name == "position" {
|
||||
return Hint_Kind.Position;
|
||||
} else if name == "uv" {
|
||||
return Hint_Kind.UV;
|
||||
} else if starts_with(name, "target") {
|
||||
return Hint_Kind.Target;
|
||||
} else if name == "outposition" {
|
||||
return Hint_Kind.Output_Position;
|
||||
}
|
||||
return .None;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Buffer_Kind :: enum {
|
||||
Constant;
|
||||
Structured;
|
||||
}
|
||||
|
||||
Buffer :: struct {
|
||||
kind : Buffer_Kind;
|
||||
name : string;
|
||||
|
||||
fields : Static_Array(Field, 16);
|
||||
|
||||
hints : [..]Field_Hint;
|
||||
|
||||
buffer_index : u32;
|
||||
}
|
||||
|
||||
Input_File :: struct {
|
||||
source : string;
|
||||
path : string;
|
||||
}
|
||||
|
||||
Compiler_Context :: struct {
|
||||
file : Input_File;
|
||||
|
||||
environment : Environment;
|
||||
|
||||
tokens : [..]Token;;
|
||||
root : *AST_Node;
|
||||
nodes : [..]AST_Node;
|
||||
|
||||
codegen_result_text : string;
|
||||
|
||||
typed_buffers : Static_Array(Type_Variable_Handle, 32);
|
||||
// structured_buffers : Static_Array(Type_Variable_Handle, 16);
|
||||
|
||||
scope_stack : Scope_Stack;
|
||||
type_variables : [..]Type_Variable;
|
||||
|
||||
vertex_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
input : [..]Field;
|
||||
}
|
||||
|
||||
pixel_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
return_value : Field;
|
||||
}
|
||||
|
||||
max_buffers :: 32;
|
||||
|
||||
buffers : Static_Array(Buffer, max_buffers);
|
||||
|
||||
had_error : bool;
|
||||
messages : [..]Compiler_Message;
|
||||
}
|
||||
|
||||
#add_context scratch_allocators : [2]Allocator;
|
||||
#add_context scratch_id : int = 0;
|
||||
|
||||
init_context_allocators :: () {
|
||||
if get_arena(context.scratch_allocators[0]) == null {
|
||||
context.scratch_allocators[0] = make_arena(Megabytes(128));
|
||||
context.scratch_allocators[1] = make_arena(Megabytes(128));
|
||||
}
|
||||
}
|
||||
|
||||
clear_context_allocators :: () {
|
||||
if get_arena(context.scratch_allocators[0]) != null {
|
||||
clear(context.scratch_allocators[0]);
|
||||
clear(context.scratch_allocators[1]);
|
||||
}
|
||||
}
|
||||
|
||||
get_scratch :: (conflict : Allocator = .{}) -> Scratch {
|
||||
arena := cast(*Arena)conflict.data;
|
||||
if arena == get_arena(context.scratch_allocators[0]) || context.scratch_id == 0 {
|
||||
context.scratch_id = 1;
|
||||
return scratch_begin(*context.scratch_allocators[1]);
|
||||
}
|
||||
context.scratch_id = 0;
|
||||
return scratch_begin(*context.scratch_allocators[0]);
|
||||
}
|
||||
|
||||
record_error :: (result : *Compiler_Context, format : string, args : .. Any) {
|
||||
error : Compiler_Message;
|
||||
error.message_kind = .Error;
|
||||
error.message = sprint(format, args);
|
||||
|
||||
array_add(*result.messages, error);
|
||||
}
|
||||
|
||||
make_file :: (result : *Compiler_Context, path : string) -> Input_File {
|
||||
if !file_exists(path) {
|
||||
record_error(result, "Unable to load file: %", path);
|
||||
return .{};
|
||||
}
|
||||
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);
|
||||
|
||||
newline_after := type.children.count / 4;
|
||||
|
||||
for *child : type.children {
|
||||
pretty_print_field(builder, child);
|
||||
if it_index < type.children.count - 1 {
|
||||
append(builder, ", ");
|
||||
}
|
||||
|
||||
if it_index % newline_after == 0 {
|
||||
append(builder, "\n");
|
||||
indent(builder, 4);
|
||||
}
|
||||
}
|
||||
|
||||
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 :: (ctx : *Compiler_Context, 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(ctx.scope_stack, variable.typename, xx 1);
|
||||
assert(find_result != null, "Internal compiler error\n");
|
||||
|
||||
type_var := from_handle(ctx.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(ctx, child);
|
||||
array_add(*type.children, child_field);
|
||||
}
|
||||
|
||||
type.name = variable.typename;
|
||||
}
|
||||
}
|
||||
|
||||
for hint : variable.source_node.hint_tokens {
|
||||
field_hint : Field_Hint;
|
||||
|
||||
if lookup_hint(hint.ident_value) == .Position {
|
||||
field_hint.kind = .Position;
|
||||
} else if lookup_hint(hint.ident_value) == .UV {
|
||||
field_hint.kind = .UV;
|
||||
} else if lookup_hint(hint.ident_value) == .Target {
|
||||
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 :: (ctx : *Compiler_Context, variable : Type_Variable_Handle) -> Field {
|
||||
return type_variable_to_field(ctx, from_handle(ctx.type_variables, variable));
|
||||
}
|
||||
|
||||
generate_buffer :: (ctx : *Compiler_Context, type_handle : Type_Variable_Handle, buffers : *Static_Array) {
|
||||
variable := from_handle(ctx.type_variables, type_handle);
|
||||
|
||||
buffer := array_add(buffers);
|
||||
|
||||
if variable.type == {
|
||||
case .CBuffer; {
|
||||
buffer.kind = .Constant;
|
||||
}
|
||||
case .Buffer; {
|
||||
buffer.kind = .Structured;
|
||||
}
|
||||
}
|
||||
buffer.name = variable.name;
|
||||
|
||||
for i : 0..variable.children.count - 1 {
|
||||
child := variable.children[i];
|
||||
field : Field = type_variable_to_field(ctx, from_handle(ctx.type_variables, child));
|
||||
array_add(*buffer.fields, field);
|
||||
}
|
||||
|
||||
buffer.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(*buffer.hints, field_hint);
|
||||
}
|
||||
}
|
||||
|
||||
generate_output_data :: (ctx : *Compiler_Context) {
|
||||
if ctx.had_error {
|
||||
return;
|
||||
}
|
||||
|
||||
if ctx.vertex_entry_point.node {
|
||||
ctx.vertex_entry_point.name = ctx.vertex_entry_point.node.name;
|
||||
|
||||
type_variable := from_handle(ctx.type_variables, ctx.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(ctx.type_variables, child.type_variable);
|
||||
field := type_variable_to_field(ctx, tv);
|
||||
array_add(*ctx.vertex_entry_point.input, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for buffer_variable : ctx.typed_buffers {
|
||||
generate_buffer(ctx, buffer_variable, *ctx.buffers);
|
||||
}
|
||||
|
||||
if ctx.pixel_entry_point.node {
|
||||
ctx.pixel_entry_point.name = ctx.pixel_entry_point.node.name;
|
||||
|
||||
type_variable := from_handle(ctx.type_variables, ctx.pixel_entry_point.node.type_variable);
|
||||
assert(type_variable.type == .Function);
|
||||
|
||||
if type_variable.return_type_variable > 0 {
|
||||
field := type_variable_to_field(ctx, type_variable.return_type_variable);
|
||||
for hint : type_variable.source_node.hint_tokens {
|
||||
field_hint : Field_Hint;
|
||||
|
||||
if lookup_hint(hint.ident_value) == .Position {
|
||||
field_hint.kind = .Position;
|
||||
} else if lookup_hint(hint.ident_value) == .Target {
|
||||
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);
|
||||
}
|
||||
ctx.pixel_entry_point.return_value = field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compile_file :: (ctx : *Compiler_Context, path : string, allocator : Allocator = temp) {
|
||||
new_context := context;
|
||||
new_context.allocator = allocator;
|
||||
push_context new_context {
|
||||
init_context_allocators();
|
||||
defer clear_context_allocators();
|
||||
|
||||
ctx.file = make_file(ctx, path);
|
||||
|
||||
lex(ctx, allocator);
|
||||
parse(ctx, allocator);
|
||||
check(ctx, allocator);
|
||||
codegen(ctx, allocator);
|
||||
generate_output_data(ctx);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user