Files
Ink-Shader-Language/Semantic_Analysis.jai
2025-08-25 21:56:08 +02:00

2067 lines
55 KiB
Plaintext

/////////////////////////////////////
//~ nbr:
//
/////////////////////////////////////
//~ nbr: Error reporting TODOs
//
// [ ] Add and error for using keywords as names, or rename the dx11 keywords in the resulting hlsl shader.
// [x] Improve error reporting on mismatched overloads when types don't match, but arity does.
// [x] Improve error reporting for type mismatches in general. It seems like the expect node is not always correct.
VERTEX_MAIN_FUNCTION_PREFIX :: "vertex";
PIXEL_MAIN_FUNCTION_PREFIX :: "pixel";
PROPERTIES_PREFIX :: "properties";
Type_Kind :: enum {
Invalid :: 128;
Int :: 0;
Half :: 1;
Float :: 2;
Double :: 3;
Texture2D :: 4;
Sampler :: 5;
Bool :: 6;
Max_Builtin :: Sampler + 1;
Unit;
Function;
Call;
Unresolved_Variable;
Unresolved_Expression;
Struct;
Properties;
CBuffer;
Array;
}
Type_Variable_Kind :: enum {
Expression;
Declaration; // struct, properties, function, etc.
}
Typenames :: string.[
"int" ,
"half" ,
"float" ,
"double" ,
"Texture2D",
"Sampler" ,
"bool" ,
];
Type_Variable :: struct {
type : Type_Kind;
kind : Type_Variable_Kind;
builtin : bool;
name : string;
//@Note(niels) For functions
return_type_variable : Type_Variable_Handle;
//@Note(niels) The scope this variable creates (function, struct, global)
scope : Scope_Handle;
//@Note(niels): For struct members
struct_field_parent : *AST_Node;
typename : string;
is_array : bool;
MAX_TYPE_VARIABLE_CHILDREN :: 32;
children : Static_Array(Type_Variable_Handle, MAX_TYPE_VARIABLE_CHILDREN);
//@Note(niels): For constant buffers
resource_index : u32;
source_node : *AST_Node;
}
Type_Variable_Handle :: #type, distinct u32;
Scope_Stack :: struct {
allocator : Allocator;
arena : Arena;
stack : [..]Scope;
}
Defined_Symbol :: struct {
name : string;
type_variable : Type_Variable_Handle;
source_node : *AST_Node;
functions : [..]Defined_Symbol;
builtin : bool;
}
Scope_Kind :: enum {
Global;
Function;
Struct;
Properties;
}
Scope :: struct {
table : Table(string, Defined_Symbol);
//@Note(nb): Only for pretty printing
longest_key_length : int;
name : string;
children : [..]Scope_Handle;
parent : Scope_Handle;
builtin : bool;
kind : Scope_Kind;
}
Scope_Handle :: #type, distinct u32;
Checker_State :: enum {
Type_Checking;
Adding_Builtins;
}
Semantic_Checker :: struct {
program_root : *AST_Node;
path : string;
state : Checker_State;
current_scope : Scope_Handle;
result_file : *Compiled_File;
current_buffer_index : u32 = 0;
current_sampler_index : u32 = 0;
current_texture_index : u32 = 0;
messages : [..]Compiler_Message;
message_arena : Arena;
message_allocator : Allocator;
had_error : bool;
}
record_error :: (checker : *Semantic_Checker, message : string, source_location : Source_Range, report_source_location : bool = true) {
locations : [1]Source_Range;
locations[0] = source_location;
record_error(checker, message, locations, report_source_location);
}
invalid_symbol_name :: (checker : *Semantic_Checker, node : *AST_Node, type : string) {
record_error(checker, tprint("Invalid % name '%'", type, node.name), node.source_location);
}
symbol_redeclaration :: (checker : *Semantic_Checker, redeclared_node : *AST_Node, symbol : *Defined_Symbol) {
/*
Redeclaration of '%':
foo : int;
^^^
Here is the first redeclaration of 'foo':
foo : int;
^^^
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Redeclaration of '%'\n", symbol.name);
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(redeclared_node.source_location));
indent(*builder, 1);
print_token_pointer(*builder, redeclared_node.source_location.main_token);
append(*builder, "\n\n");
white(*builder);
path := checker.path;
if path.count > 0 {
print_to_builder(*builder, "%:", checker.path);
} else {
append(*builder, "internal:");
}
if symbol.source_node {
location := symbol.source_node.source_location;
print_to_builder(*builder, "%,%: info: ", location.begin.line, location.begin.column);
print_to_builder(*builder, "Here is the first declaration of '%'\n", symbol.name);
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(location));
indent(*builder, 1);
print_token_pointer(*builder, symbol.source_node.source_location.main_token);
}
message := builder_to_string(*builder);
record_error(checker, message, redeclared_node.source_location, false);
}
symbol_undeclared :: (checker : *Semantic_Checker, node : *AST_Node) {
/*
Use of undeclard symbol '%'.
b = f;
^
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Use of undeclared symbol '%'\n", node.name);
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(node.source_location));
indent(*builder, 1);
print_token_pointer(*builder, node.source_location.main_token);
message := builder_to_string(*builder);
record_error(checker, message, node.source_location, false);
}
no_matching_overload_found :: (checker : *Semantic_Checker, call : *AST_Node, overloads : *Defined_Symbol, arg_node : *AST_Node = null) {
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Procedure call did not match any of the possible overloads for '%'\n", overloads.name);
cyan(*builder);
indent(*builder, 1);
append(*builder, "found:\n");
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(call.source_location));
indent(*builder, 2);
print_token_pointer(*builder, call.source_location.main_token);
newline(*builder);
if arg_node {
newline(*builder);
white(*builder);
location := arg_node.source_location;
indent(*builder, 1);
field_list := arg_node.parent;
index : s64 = -1;
for child : field_list.children {
if child == arg_node {
index = it_index + 1;
break;
}
}
print_to_builder(*builder, "While matching argument % in function call.\n", index);
cyan(*builder);
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(location));
indent(*builder, 2);
print_token_pointer(*builder, arg_node.source_location.main_token);
newline(*builder);
}
white(*builder);
indent(*builder, 1);
append(*builder, "Possible overloads:\n");
for func : overloads.functions {
func_var := from_handle(checker, func.type_variable);
cyan(*builder);
// foo :: (f : float) {} (file_path:line_num)
func_location := func_var.source_node.source_location;
indent(*builder, 2);
// @Incomplete(niels): We need a way to properly save the path of the declaration
print_to_builder(*builder, "% (%:%)\n", print_from_source_location(func_location), checker.path,
func_location.main_token.line);
if !arg_node {
white(*builder);
arg_list := call.children[0];
indent(*builder, 2);
func_var := from_handle(checker, func.type_variable);
if arg_list.children.count != func_var.children.count {
print_to_builder(*builder, "Not enough arguments: Wanted %, got %.\n\n", func_var.children.count, arg_list.children.count);
}
}
}
locations : [1]Source_Range;
locations[0] = call.source_location;
message := builder_to_string(*builder);
record_error(checker, message, locations, false);
}
not_all_control_paths_return_value :: (checker : *Semantic_Checker, node : *AST_Node) {
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Not all control paths return a value.\n\n");
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(node.source_location));
indent(*builder, 1);
print_token_pointer(*builder, node.token);
white(*builder);
message := builder_to_string(*builder);
record_error(checker, message, node.source_location, false);
}
mismatched_arguments :: (checker : *Semantic_Checker, call : *AST_Node, symbol : *Defined_Symbol, args_call : int, args_def : int) {
builder : String_Builder;
init_string_builder(*builder,, temp);
if args_call > args_def {
print_to_builder(*builder, "Too many arguments: Wanted %, got %\n", args_def, args_call);
} else {
print_to_builder(*builder, "Too few arguments: Wanted %, got %\n", args_def, args_call);
}
/*
Too many arguments: Expected %, got %.
found
foo(2, 3)
^^^
expected
foo :: (x : int, y : int, z : int)
*/
cyan(*builder);
indent(*builder, 1);
append(*builder, "found:\n");
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(call.source_location));
indent(*builder, 2);
print_token_pointer(*builder, call.source_location.main_token);
append(*builder, "\n\n");
indent(*builder, 1);
append(*builder, "expected:\n");
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(symbol.source_node.source_location));
locations : [1]Source_Range;
locations[0] = call.source_location;
message := builder_to_string(*builder);
record_error(checker, message, locations, false);
}
function_undeclared :: (checker : *Semantic_Checker, node : *AST_Node) {
/*
Error: Undeclared identifier 'name'.
name();
^^^^
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Attempt to call undeclared function '%'.\n\n", node.name);
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(node.source_location));
indent(*builder, 1);
print_token_pointer(*builder, node.source_location.main_token);
newline(*builder);
newline(*builder);
message := builder_to_string(*builder);
record_error(checker, message, node.source_location, false);
}
field_not_defined_on_struct :: (checker : *Semantic_Checker, node : *AST_Node, struct_symbol : *Defined_Symbol) {
/*
Field '%' is not defined in struct '%'.
b.t = f;
^
declaration:
Bar :: struct {
f : Foo;
}
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
print_to_builder(*builder, "Field '%' is not defined in struct '%'.\n", node.name, struct_symbol.name);
cyan(*builder);
indent(*builder, 1);
print_to_builder(*builder, "%\n", print_from_source_location(node.source_location));
indent(*builder, 1);
print_token_pointer(*builder, node.source_location.main_token);
newline(*builder);
newline(*builder);
indent(*builder, 1);
append(*builder, "declaration:\n");
print_from_source_location(*builder, struct_symbol.source_node.source_location, 2);
message := builder_to_string(*builder);
record_error(checker, message, node.source_location, false);
}
field_access_on_primitive_type :: (checker : *Semantic_Checker, node : *AST_Node, handle : Type_Variable_Handle) {
/*
Attempting to access a field on a primitive type '%'.
x.d = 5;
^^
declaration:
x : int = 5;
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
variable := from_handle(checker, handle);
print_to_builder(*builder, "Attempting to access a field on a primitive type '%'.\n", proper_type_to_string(checker.result_file.type_variables, variable));
indent(*builder, 1);
cyan(*builder);
print_to_builder(*builder, "%\n", print_from_source_location(node.source_location));
indent(*builder, 1);
node_variable := from_handle(checker, node.type_variable);
for 0..node.name.count - 1 {
append(*builder, " ");
}
print_token_pointer(*builder, node.source_location.begin);
append(*builder, "\n");
indent(*builder, 1);
append(*builder, "declaration:\n");
indent(*builder, 2);
print_to_builder(*builder, "%", print_from_source_location(variable.source_node.source_location));
message := builder_to_string(*builder,, temp);
record_error(checker, message, node.source_location, false);
white(*builder);
}
if_condition_has_to_be_boolean_type :: (checker : *Semantic_Checker, usage_site : *AST_Node, handle : Type_Variable_Handle) {
/*
Type of expression in if condition has to be bool.
if 100.0
^^^^^^^
100.0 has type float
*/
builder : String_Builder;
init_string_builder(*builder,, temp);
variable := from_handle(checker, handle);
append(*builder, "Type of expression in if condition has to be bool.\n");
indent(*builder, 1);
cyan(*builder);
location := usage_site.source_location;
print_to_builder(*builder, "%\n", print_from_source_location(location));
indent(*builder, 1);
print_token_pointer(*builder, usage_site.children[0].source_location.begin);
append(*builder, "\n");
indent(*builder, 1);
var := from_handle(checker, handle);
usage_child := usage_site.children[0];
usage_loc := usage_child.source_location;
print_to_builder(*builder, "% has type %\n", print_from_source_location(*usage_loc), proper_type_to_string(checker.result_file.type_variables, var));
message := builder_to_string(*builder,, temp);
record_error(checker, message, usage_site.source_location, false);
white(*builder);
}
type_mismatch :: (checker : *Semantic_Checker, usage_site : *AST_Node, expect_node : *AST_Node, expect : Type_Variable_Handle, got : Type_Variable_Handle) {
expect_var := from_handle(checker, expect);
got_var := from_handle(checker, got);
builder : String_Builder;
init_string_builder(*builder,, temp);
if got_var.builtin {
print_to_builder(*builder, "% :: (", got_var.name);
for i: 0..got_var.children.count - 1{
child_handle := got_var.children[i];
child := from_handle(checker, child_handle);
print_to_builder(*builder, "% : %", child.name, type_to_string(child));
}
}
print_to_builder(*builder, "Type mismatch. Expected % got %\n", type_to_string(expect_var), type_to_string(got_var));
cyan(*builder);
location := usage_site.source_location;
indent(*builder, 1);
append(*builder, "found:\n");
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(location));
indent(*builder, 2);
print_token_pointer(*builder, location.main_token);
append(*builder, "\n");
indent(*builder, 1);
print_to_builder(*builder, "expected:\n");
indent(*builder, 2);
proper_type_to_string(*builder, checker.result_file.type_variables, expect_var);
append(*builder, "\n");
// indent(*builder, 2);
{
// expect_location := expect_var.source_node.source_location;
// // token.length = expect_location.end.index + token.length - token.index + expect_location.end.length - 1;
// print_token_pointer(*builder, expect_location.main_token);
append(*builder, "\n");
}
indent(*builder, 1);
print_to_builder(*builder, "got:\n");
indent(*builder, 2);
print_to_builder(*builder, "%\n", print_from_source_location(got_var.source_node.source_location));
message := builder_to_string(*builder);
record_error(checker, message, Source_Range.[usage_site.source_location], false);
}
record_error :: (checker : *Semantic_Checker, error_string : string, locations : []Source_Range, report_source_location : bool = true) {
error : Compiler_Message;
error.message_kind = .Error;
error.report_source_location = report_source_location;
for location : locations {
array_add(*error.source_locations, location);
}
error.path = checker.path;
error.message = error_string;
checker.had_error = true;
array_add(*checker.messages, error);
}
is_proper :: (var : Type_Variable) -> bool {
if var.type == {
case .Function; #through;
case .Int; #through;
case .Struct; #through;
case .Float; {
return true;
}
}
return false;
}
use_scope :: (checker : *Semantic_Checker, handle : Scope_Handle) -> Scope_Handle {
assert(handle > 0, "Invalid scope handle: %", handle);
previous_scope := checker.current_scope;
checker.current_scope = handle;
return previous_scope;
}
push_scope :: (checker : *Semantic_Checker, name := "", kind : Scope_Kind = .Global) -> *Scope, Scope_Handle {
new_scope : Scope;
array_add(*checker.result_file.scope_stack.stack, new_scope);
count := checker.result_file.scope_stack.stack.count;
scope := *checker.result_file.scope_stack.stack[count - 1];
scope.parent = checker.current_scope;
scope.name = name;
scope.kind = kind;
if checker.state == .Adding_Builtins {
scope.builtin = true;
}
scope.children.allocator = checker.result_file.scope_stack.allocator;
if checker.current_scope {
scope := get_current_scope(checker);
array_add(*scope.children, xx count);
}
checker.current_scope = xx count;
return scope, xx count;
}
pop_scope :: (checker : *Semantic_Checker) -> Scope_Handle {
scope := get_scope(checker, checker.current_scope);
if !scope.parent {
return 0;
}
checker.current_scope = scope.parent;
return checker.current_scope;
}
peek_scope :: (checker : *Semantic_Checker) -> *Scope, Scope_Handle {
if checker.result_file.scope_stack.stack.count == 0 {
return null, 0;
}
count := checker.result_file.scope_stack.stack.count;
scope := *checker.result_file.scope_stack.stack[count - 1];
return scope, xx count;
}
get_current_scope :: (checker : *Semantic_Checker) -> *Scope {
return get_scope(checker, checker.current_scope);
}
get_scope :: (scope_stack : Scope_Stack, handle : Scope_Handle) -> *Scope {
if handle == 0 {
return null;
}
return *scope_stack.stack[handle - 1];
}
get_scope :: (checker : *Semantic_Checker, handle : Scope_Handle) -> *Scope {
return get_scope(*checker.result_file.scope_stack, handle);
}
add_symbol_to_scope :: (state : Checker_State, scope_stack : *Scope_Stack, scope_handle : Scope_Handle, name : string, symbol : Defined_Symbol) -> *Defined_Symbol {
scope := get_scope(scope_stack.*, scope_handle);
scope.longest_key_length = max(scope.longest_key_length, name.count);
symbol_to_add : Defined_Symbol;
symbol_to_add.name = symbol.name;
symbol_to_add.type_variable = symbol.type_variable;
symbol_to_add.source_node = symbol.source_node;
symbol_to_add.functions = symbol.functions;
if state == .Adding_Builtins {
symbol_to_add.builtin = true;
}
return table_set(*scope.table, name, symbol_to_add);
}
new_type_variable :: (checker : *Semantic_Checker) -> *Type_Variable, Type_Variable_Handle {
variable : Type_Variable;
handle := cast(Type_Variable_Handle)checker.result_file.type_variables.count + 1;
array_add(*checker.result_file.type_variables, variable);
return from_handle(checker, handle), handle;
}
add_child :: (variable : *Type_Variable, child : Type_Variable_Handle) {
assert(variable.children.count < Type_Variable.MAX_TYPE_VARIABLE_CHILDREN);
array_add(*variable.children, child);
// variable.children[variable.children.count] = child;
// variable.children.count += 1;
}
add_child :: (checker : *Semantic_Checker, handle : Type_Variable_Handle, child : Type_Variable_Handle) {
variable := from_handle(checker, handle);
assert(variable.children.count < Type_Variable.MAX_TYPE_VARIABLE_CHILDREN);
array_add(*variable.children, child);
}
init_semantic_checker :: (checker : *Semantic_Checker, root : *AST_Node, path : string) {
checker.current_buffer_index = 0;
checker.current_sampler_index = 0;
checker.current_texture_index = 0;
array_reserve(*checker.messages, 16);
checker.program_root = root;
checker.path = path;
// @Incomplete(niels): Use other allocator and/or add static array with convenience functions
array_reserve(*checker.result_file.type_variables, 2048);
checker.result_file.scope_stack.allocator = make_arena(*checker.result_file.scope_stack.arena);
array_reserve(*checker.result_file.scope_stack.stack, 256);
global_scope, global_handle := push_scope(checker, kind = .Global);
array_reserve(*global_scope.children, 2048);
}
find_symbol :: (scope_stack : Scope_Stack, name : string, current_scope : Scope_Handle, containing_scope : *Scope_Handle = null) -> *Defined_Symbol {
handle := current_scope;
current := get_scope(scope_stack, current_scope);
while current {
table := current.table;
info := table_find_pointer(*table, name);
if info {
if containing_scope {
containing_scope.* = handle;
}
return info;
}
handle = current.parent;
current = get_scope(scope_stack, current.parent);
}
return null;
}
find_symbol :: (checker : *Semantic_Checker, name : string, current_scope : Scope_Handle, containing_scope : *Scope_Handle = null) -> *Defined_Symbol {
return find_symbol(checker.result_file.scope_stack, name, current_scope, containing_scope);
}
find_symbol :: (name : string, checker : *Semantic_Checker, containing_scope : *Scope_Handle = null) -> *Defined_Symbol {
return find_symbol(checker, name, checker.current_scope, containing_scope);
}
from_handle :: (variables : []Type_Variable, handle : Type_Variable_Handle) -> *Type_Variable {
assert(handle > 0 && xx handle <= variables.count, tprint("Invalid handle: %. Range is: 1-%", handle, variables.count - 1));
return *variables[handle - 1];
}
from_handle :: (checker : *Semantic_Checker, handle : Type_Variable_Handle) -> *Type_Variable {
return from_handle(checker.result_file.type_variables, handle);
}
proper_type_to_string :: (builder : *String_Builder, variables : []Type_Variable, var : Type_Variable) {
if var.type == {
case .Int; #through;
case .Half; #through;
case .Float; #through;
case .Double; {
print_to_builder(builder, "%", Typenames[var.type]);
}
case .Struct; {
print_to_builder(builder, "%", var.typename);
}
case .Function; {
append(builder, "(");
for child : var.source_node.children {
if child.kind == .FieldList {
for field : child.children {
var := field.type_variable;
print_type_variable(builder, variables, var);
if it_index != child.children.count - 1 {
append(builder, ", ");
}
}
}
}
append(builder, ")");
if var.return_type_variable > 0 {
append(builder, " -> ", );
return_var := from_handle(variables, var.return_type_variable);
if is_proper(return_var) {
proper_type_to_string(builder, variables, return_var);
} else {
append(builder, "[[");
print_type_variable(builder, variables, var.return_type_variable);
append(builder, "]]", );
}
} else {
append(builder, " -> unit");
}
}
case; {
append(builder, "______not proper type______");
}
}
}
proper_type_to_string :: (variables : []Type_Variable, var : Type_Variable, allocator := context.allocator) -> string {
if var.type == {
case .Int; #through;
case .Half; #through;
case .Float; #through;
case .Double; {
return Typenames[var.type];
}
case .Function; {
builder : String_Builder;
init_string_builder(*builder,, allocator);
proper_type_to_string(*builder, variables, var);
return builder_to_string(*builder,, allocator);
}
case .Struct; {
return var.typename;
}
}
return "______not proper type______";
}
get_type_from_identifier :: (checker : *Semantic_Checker, scope : Scope_Handle, node : *AST_Node, typename : *string = null) -> Type_Kind {
type_string := node.token.ident_value;
if type_string == {
case Typenames[Type_Kind.Int]; return .Int;
case Typenames[Type_Kind.Half]; return .Half;
case Typenames[Type_Kind.Float]; return .Float;
case Typenames[Type_Kind.Double]; return .Double;
case Typenames[Type_Kind.Sampler]; return .Sampler;
case Typenames[Type_Kind.Texture2D]; return .Texture2D;
}
symbol := find_symbol(checker, type_string, scope);
if symbol {
symbol_var := from_handle(checker, symbol.type_variable);
if symbol_var.type == .Struct {
if typename {
typename.* = symbol_var.typename;
}
return symbol_var.type;
}
} else {
return .Unresolved_Variable;
}
return .Invalid;
}
check_expression :: (node : *AST_Node, checker : *Semantic_Checker) -> Type_Variable_Handle {
return 0;
}
check_block :: (checker : *Semantic_Checker, node : *AST_Node) {
for child : node.children {
check_node(checker, child);
}
}
declare_struct :: (checker : *Semantic_Checker, node : *AST_Node, name : string) -> Type_Variable_Handle {
variable, handle := new_type_variable(checker);
variable.type = .Struct;
variable.kind = .Declaration;
variable.name = name;
variable.source_node = node;
variable.typename = name;
node.type_variable = handle;
find_result := find_symbol(checker, name, checker.current_scope);
if !find_result {
symbol : Defined_Symbol;
symbol.name = name;
symbol.source_node = node;
symbol.type_variable = handle;
add_symbol_to_scope(checker.state, *checker.result_file.scope_stack, checker.current_scope, name, symbol);
} else {
symbol_redeclaration(checker, node, find_result);
return 0;
}
scope, scope_handle := push_scope(checker, name, .Struct);
variable.scope = scope_handle;
for child : node.children {
if child.kind == .FieldList {
for field : child.children {
type_var := check_field(checker, field);
if type_var > 0 {
from_handle(checker, type_var).scope = scope_handle;
add_child(checker, handle, type_var);
}
}
}
}
pop_scope(checker);
return handle;
}
declare_struct :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
return declare_struct(checker, node, node.name);
}
declare_properties :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
name := ifx node.name.count == 0 then "properties" else node.name;
if node.name.count > 0 {
checker.result_file.property_name = name;
}
type_var := declare_struct(checker, node, name);
var := from_handle(checker, type_var);
var.type = .Properties;
var.typename = "properties";
var.resource_index = checker.current_buffer_index;
checker.current_buffer_index += 1;
return type_var;
}
declare_cbuffer :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
type_var := declare_struct(checker, node);
var := from_handle(checker, type_var);
var.type = .CBuffer;
var.resource_index = checker.current_buffer_index;
checker.current_buffer_index += 1;
array_add(*checker.result_file.constant_buffers, type_var);
return type_var;
}
get_actual_function_name :: (node : *AST_Node) -> string {
name_to_check := node.name;
if node.vertex_entry_point {
name_to_check = sprint("%__%", VERTEX_MAIN_FUNCTION_PREFIX, node.name);
} else if node.pixel_entry_point {
name_to_check = sprint("%__%", PIXEL_MAIN_FUNCTION_PREFIX, node.name);
}
return name_to_check;
}
declare_foreign_function :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
return declare_function(checker, node, true);
}
declare_function :: (checker : *Semantic_Checker, node : *AST_Node, builtin : bool = false) -> Type_Variable_Handle {
if !node.foreign_declaration && !can_declare(checker, node.name) {
invalid_symbol_name(checker, node, "function");
return 0;
}
variable, handle := new_type_variable(checker);
variable.type = .Function;
variable.kind = .Declaration;
variable.name = node.name;
variable.source_node = node;
variable.builtin = builtin;
node.type_variable = handle;
name_to_check := get_actual_function_name(node);
if node.vertex_entry_point {
checker.result_file.vertex_entry_point.node = node;
}
if node.pixel_entry_point {
checker.result_file.pixel_entry_point.node = node;
}
find_result := find_symbol(checker, name_to_check, checker.current_scope);
if !find_result {
function : Defined_Symbol;
function.name = name_to_check;
function.source_node = node;
function.type_variable = handle;
symbol : Defined_Symbol;
symbol.name = name_to_check;
symbol.source_node = node;
symbol.type_variable = 0;
array_reserve(*symbol.functions, 32);
array_add(*symbol.functions, function);
add_symbol_to_scope(checker.state, *checker.result_file.scope_stack, checker.current_scope, name_to_check, symbol);
} else {
//@Note(niels): This is some ugly code, but it's probably fine for now.
field_list := node.children[0];
for function : find_result.functions {
func_var := from_handle(checker, function.type_variable);
if func_var.source_node.children[0].children.count != field_list.children.count {
continue;
}
all_same : bool = true;
for i : 0..func_var.children.count - 1 {
arg := func_var.children[i];
node_child := field_list.children[i];
typename : string;
arg_type := get_type_from_identifier(checker, checker.current_scope, node_child, *typename);
other_arg := from_handle(checker, arg);
if arg_type != other_arg.type {
all_same = false;
break;
} else {
if arg_type == .Struct && other_arg.type == .Struct {
if typename != other_arg.typename {
all_same = false;
break;
}
}
}
}
if all_same {
symbol_redeclaration(checker, node, find_result);
return 0;
} else {
function : Defined_Symbol;
function.name = name_to_check;
function.source_node = node;
function.type_variable = handle;
array_add(*find_result.functions, function);
break;
}
}
function : Defined_Symbol;
function.name = name_to_check;
function.source_node = node;
function.type_variable = handle;
array_add(*find_result.functions, function);
}
if !builtin {
scope, scope_handle := push_scope(checker, name_to_check, .Function);
variable.scope = scope_handle;
}
for child : node.children {
if child.kind == .FieldList {
for field : child.children {
type_var := check_node(checker, field);
if type_var > 0 {
if builtin {
var := from_handle(checker, type_var);
var.builtin = true;
}
add_child(checker, handle, type_var);
}
}
}
}
if builtin && node.token.ident_value.count > 0 {
return_var, return_handle := new_type_variable(checker);
return_var.type = get_type_from_identifier(checker, checker.current_scope, node, *return_var.typename);
from_handle(checker, handle).return_type_variable= return_handle;
}
if !builtin {
pop_scope(checker);
}
return handle;
}
check_function :: (checker : *Semantic_Checker, node : *AST_Node) {
name_to_check := get_actual_function_name(node);
find_result := find_symbol(checker, name_to_check, checker.current_scope);
if !find_result {
assert(false, "Compiler error. Functions should all be declared at this point in time.");
}
handle := find_result.type_variable;
if node.foreign_declaration {
return;
}
for function : find_result.functions {
variable := from_handle(checker, function.type_variable);
assert(variable.scope > 0, "Declared function is missing scope.");
previous_scope := use_scope(checker, variable.scope);
for child : node.children {
if child.kind == .Block {
for statement : child.children {
if statement.kind == .Return {
result_var := check_node(checker, statement);
if result_var > 0 {
variable.return_type_variable = result_var;
}
} else {
result_var := check_node(checker, statement);
if result_var > 0 {
stm := from_handle(checker, result_var);
add_child(variable, result_var);
}
}
}
}
}
if node.token.ident_value.count > 0 && !variable.return_type_variable{
not_all_control_paths_return_value(checker, node);
}
use_scope(checker, previous_scope);
}
}
check_variable :: (checker : *Semantic_Checker, node : *AST_Node, struct_field_parent : *AST_Node = null) -> Type_Variable_Handle {
find_result := find_symbol(checker, node.name, checker.current_scope);
// x : int;
// x.d = 5;
if find_result {
node.type_variable = find_result.type_variable;
variable := from_handle(checker, find_result.type_variable);
variable.struct_field_parent = struct_field_parent;
if get_scope(checker, checker.current_scope).kind == .Struct {
variable.scope = checker.current_scope;
}
if node.children.count > 0 {
if variable.type != .Struct && variable.type != .Properties && variable.type != .CBuffer {
field_access_on_primitive_type(checker, node, find_result.type_variable);
return 0;
} else {
lookup_name : string = variable.typename;
if variable.typename == "properties" {
lookup_name = variable.name;
}
struct_symbol := find_symbol(checker, lookup_name, checker.current_scope);
type_variable := from_handle(checker, struct_symbol.type_variable);
previous_scope := use_scope(checker, type_variable.scope);
child := node.children[0];
var := find_symbol(checker, child.name, type_variable.scope);
if var == null {
field_not_defined_on_struct(checker, child, struct_symbol);
return 0;
}
access := check_variable(checker, child, node);
use_scope(checker, previous_scope);
return access;
}
}
return find_result.type_variable;
}
symbol_undeclared(checker, node);
return 0;
}
can_declare :: (checker : *Semantic_Checker, name : string) -> bool {
max_value := Type_Kind.Max_Builtin;
for i : 0..max_value - 1 {
kind := cast(Type_Kind)i;
if name == Typenames[kind] {
return false;
}
}
return true;
}
//@Incomplete(niels): Handle meta stuff here
check_field :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
variable, handle := new_type_variable(checker);
variable.name = node.name;
typename : string;
variable.type = get_type_from_identifier(checker, checker.current_scope, node, *typename);
variable.is_array = node.array_field;
if variable.is_array {
size_node := node.children[0];
size_var := check_node(checker, size_node);
if from_handle(checker, size_var).type != .Int {
//@Incomplete(niels): Type mismatch here. With integral type required message.
}
}
if variable.kind == .Declaration && variable.type == .Sampler {
variable.resource_index = checker.current_sampler_index;
checker.current_sampler_index += 1;
}
if variable.kind == .Declaration && variable.type == .Texture2D {
variable.resource_index = checker.current_texture_index;
checker.current_texture_index += 1;
}
variable.typename = typename;
variable.source_node = node;
variable.scope = checker.current_scope;
node.type_variable = handle;
if node.kind != .Unnamed_Field {
if !can_declare(checker, node.name) {
invalid_symbol_name(checker, node, "variable");
return 0;
}
find_result := find_symbol(checker, node.name, checker.current_scope);
if !find_result {
symbol : Defined_Symbol;
symbol.name = node.name;
symbol.source_node = node;
symbol.type_variable = handle;
add_symbol_to_scope(checker.state, *checker.result_file.scope_stack, checker.current_scope, node.name, symbol);
} else {
symbol_redeclaration(checker, node, find_result);
return 0;
}
}
if node.token.ident_value.count > 0 {
variable.type = get_type_from_identifier(checker, checker.current_scope, node);
}
if node.children.count > 0 {
rhs : Type_Variable_Handle;
assert(node.children.count == 1);
for child : node.children {
rhs = check_node(checker, child);
}
if handle == 0 || rhs == 0 {
return handle;
}
l := from_handle(checker, handle);
r := from_handle(checker, rhs);
assert(l.type != .Unresolved_Expression && r.type != .Unresolved_Expression);
if l.type == .Unresolved_Variable {
l.type = r.type;
l.typename = r.typename;
} else {
if !types_compatible(checker, handle, rhs) {
type_mismatch(checker, l.source_node, r.source_node, rhs, handle);
return 0;
}
}
}
return handle;
}
check_call :: (checker : *Semantic_Checker, node : *AST_Node, type_var : Type_Variable_Handle) -> error : bool {
find_result := find_symbol(checker, node.name, checker.current_scope);
if !find_result {
function_undeclared(checker, node);
return true;
}
overload_found := false;
arg_node : *AST_Node = null;
Result :: struct {
var : Type_Variable_Handle;
node : *AST_Node;
}
arg_vars : [..]Result;
// @incomplete(niels): Should be some kind of scratch allocator instead probably?
arg_vars.allocator = temp;
arg_count := 0;
for child : node.children {
if child.kind == {
case .ArgList; {
arg_count = child.children.count;
for arg_child : child.children {
arg_var := check_node(checker, arg_child);
if arg_var != 0 {
array_add(*arg_vars, .{ arg_var, arg_child });
}
}
}
}
}
if arg_vars.count != arg_count {
return true;
}
Type_Mismatch_Data :: struct {
lhs : Result;
rhs : Result;
}
mismatches : [..]Type_Mismatch_Data;
mismatches.allocator = temp;
for *func : find_result.functions {
if overload_found {
break;
}
function := from_handle(checker, func.type_variable);
field_list := function.source_node.children[0];
if arg_count != field_list.children.count {
continue;
}
if node.children.count == 0 && function.children.count == 0 {
overload_found = true;
}
if overload_found && function.return_type_variable == 0 {
break;
}
all_args_match : bool = true;
for arg : arg_vars {
function_param := function.children[it_index];
if !types_compatible(checker, arg.var, function_param, true) {
if all_args_match {
arg_node = arg.node;
}
fun_param := from_handle(checker, function_param);
mismatch : Type_Mismatch_Data;
mismatch.lhs = arg;
mismatch.rhs = .{ function_param, fun_param.source_node };
array_add(*mismatches, mismatch);
all_args_match = false;
overload_found = false;
} else {
overload_found = all_args_match;
}
}
if overload_found {
if function.return_type_variable > 0 {
return_var := from_handle(checker, function.return_type_variable);
constrained_var := from_handle(checker, type_var);
constrained_var.type = return_var.type;
constrained_var.typename = return_var.typename;
}
}
}
if !overload_found {
no_matching_overload_found(checker, node, find_result, arg_node);
for mismatch : mismatches {
type_mismatch(checker, mismatch.lhs.node, mismatch.rhs.node, mismatch.rhs.var, mismatch.lhs.var);
}
return true;
}
return false;
}
check_node :: (checker : *Semantic_Checker, node : *AST_Node) -> Type_Variable_Handle {
if node.kind == {
case .Function; {
check_function(checker, node);
return 0;
}
case. Field; {
field_var := check_field(checker, node);
return field_var;
}
case .Unnamed_Field; {
field_var := check_field(checker, node);
return field_var;
}
case .Unary; {
var := check_node(checker, node.children[0]);
variable, handle := new_type_variable(checker);
type := from_handle(checker, var);
variable.type = type.type;
variable.typename = type.typename;
variable.scope = type.scope;
variable.source_node = node;
node.type_variable = handle;
add_child(variable, var);
return handle;
}
case .Binary; {
lhs_var := check_node(checker, node.children[0]);
if lhs_var == 0 {
return 0;
}
rhs_var := check_node(checker, node.children[1]);
if rhs_var == 0 {
return 0;
}
variable, handle := new_type_variable(checker);
lhs_type := from_handle(checker, lhs_var);
rhs_type := from_handle(checker, rhs_var);
variable.type = lhs_type.type;
variable.typename = lhs_type.typename;
variable.scope = lhs_type.scope;
variable.source_node = node;
node.type_variable = handle;
add_child(variable, lhs_var);
add_child(variable, rhs_var);
if node.token.kind == {
case .TOKEN_PLUS; #through;
case .TOKEN_MINUS; #through;
case .TOKEN_STAR; #through;
case .TOKEN_SLASH; {
if !types_compatible(checker, lhs_var, rhs_var) {
type_mismatch(checker, node, node.children[1], lhs_var, rhs_var);
return 0;
}
if lhs_type.type == .Struct {
variable.type = lhs_type.type;
variable.typename = lhs_type.typename;
variable.scope = lhs_type.scope;
} else if rhs_type.type == .Struct {
variable.type = rhs_type.type;
variable.typename = rhs_type.typename;
variable.scope = rhs_type.scope;
}
}
case .TOKEN_ASSIGN; {
if !types_compatible(checker, lhs_var, rhs_var) {
type_mismatch(checker, node.parent, node.children[1], lhs_var, rhs_var);
return 0;
}
}
case .TOKEN_GREATER; #through;
case .TOKEN_GREATEREQUALS; #through;
case .TOKEN_LESS; #through;
case .TOKEN_LESSEQUALS; #through;
case .TOKEN_LOGICALOR; #through;
case .TOKEN_ISEQUAL; #through;
case .TOKEN_ISNOTEQUAL; #through;
case .TOKEN_LOGICALAND; {
variable.type = .Bool;
variable.typename = Typenames[variable.type];
}
}
return handle;
}
case .Return; {
return check_node(checker, node.children[0]);
}
case .If; {
cond_var := check_node(checker, node.children[0]);
if cond_var > 0 {
cond := from_handle(checker, cond_var);
if cond.type != .Bool {
if_condition_has_to_be_boolean_type(checker, node, cond_var);
}
}
body_var := check_block(checker, node.children[1]);
if node.children.count == 3 {
if node.children[2].kind == .If {
check_node(checker, node.children[2]);
} else {
check_block(checker, node.children[2]);
}
}
}
case .Variable; {
return check_variable(checker, node);
}
case .Integer; {
type_variable, handle := new_type_variable(checker);
type_variable.type = .Int;
type_variable.source_node = node;
node.type_variable = handle;
return handle;
}
case .Float; {
type_variable, handle := new_type_variable(checker);
type_variable.type = .Float;
type_variable.source_node = node;
node.type_variable = handle;
return handle;
}
case .Expression_Statement; {
return check_node(checker, node.children[0]);
}
case .Call; {
type_variable, handle := new_type_variable(checker);
type_variable.type = .Unresolved_Expression;
type_variable.source_node = node;
node.type_variable = handle;
if check_call(checker, node, handle) {
return 0;
}
return handle;
}
}
return 0;
}
traverse :: (checker : *Semantic_Checker, root : *AST_Node) {
declarations := root.children;
for declaration : declarations {
if declaration.kind == .Function {
if declaration.foreign_declaration {
fun_handle := declare_foreign_function(checker, declaration);
} else {
fun_handle := declare_function(checker, declaration);
}
} else if declaration.kind == .Properties {
declare_properties(checker, declaration);
} else if declaration.kind == .Struct {
declare_struct(checker, declaration);
} else if declaration.kind == .CBuffer {
declare_cbuffer(checker, declaration);
}
}
if checker.had_error {
return;
}
for declaration : declarations {
check_node(checker, declaration);
}
}
traverse :: (checker : *Semantic_Checker) {
traverse(checker, checker.program_root);
}
types_compatible :: (checker : *Semantic_Checker, lhs : Type_Variable_Handle, rhs : Type_Variable_Handle, param_matching : bool = false) -> bool {
lhs_var := from_handle(checker, lhs);
rhs_var := from_handle(checker, rhs);
if lhs_var.type == {
case .Int; #through;
case .Half; #through;
case .Float; #through;
case .Double; {
if !param_matching {
if rhs_var.type == .Struct {
if rhs_var.typename == {
case "float2"; #through;
case "float3"; #through;
case "float4"; {
return true;
}
}
}
}
return rhs_var.type == .Int || rhs_var.type == .Half ||
rhs_var.type == .Float || rhs_var.type == .Double;
}
case .Sampler; #through;
case .Texture2D; {
return rhs_var.type == lhs_var.type;
}
case .Struct; {
lhs_node := lhs_var.source_node;
rhs_node := rhs_var.source_node;
if rhs_var.type != .Struct && !param_matching {
if lhs_var.typename == {
case "float2"; #through;
case "float3"; #through;
case "float4"; {
return rhs_var.type == .Int || rhs_var.type == .Half || rhs_var.type == .Double || rhs_var.type == .Float;
}
}
}
if rhs_var.type != .Struct {
return false;
}
lhs_struct := find_symbol(checker, lhs_var.typename, xx 1);
rhs_struct := find_symbol(checker, rhs_var.typename, xx 1);
if !lhs_struct || !rhs_struct {
return false;
}
if !lhs_struct.type_variable || !rhs_struct.type_variable {
return false;
}
lhs_struct_var := from_handle(checker, lhs_struct.type_variable);
rhs_struct_var := from_handle(checker, rhs_struct.type_variable);
if lhs_struct_var.children.count != rhs_struct_var.children.count {
return false;
}
for i : 0..lhs_struct_var.children.count - 1 {
lhs_child := lhs_struct_var.children[i];
rhs_child := rhs_struct_var.children[i];
if !types_compatible(checker, lhs_child, rhs_child) {
return false;
}
}
return true;
}
case .Unresolved_Expression; #through;
case .Unresolved_Variable; {
return true;
}
}
return false;
}
// add_type :: (checker : *Semantic_Checker, kind : Type_Kind, members : ..Type_Kind) {
// }
// add_builtins :: (checler : *Semantic_Checker) {
// //~ Types
// add_type(checker, "float2", .Struct, .Float, .Float);
// add_type(checker, "float3", .Struct, .Float, .Float, .Float);
// //~ Functions
// }
add_hlsl_builtins :: (checker : *Semantic_Checker) {
source_location := #location().fully_pathed_filename;
path_array := split(source_location, "/");
sb : String_Builder;
for i : 0..path_array.count - 2 {
print_to_builder(*sb, path_array[i]);
append(*sb, "/");
}
append(*sb, "hlsl_builtin.ink");
path := builder_to_string(*sb);
HLSL_BUILTIN, ok := read_entire_file(path);
if !ok {
messages : [..]Compiler_Message;
internal_error_message(*messages, "Error loading builtin functions.", checker.path);
print("%\n", report_messages(messages));
assert(false);
return;
}
checker.state = .Adding_Builtins;
lexer : Lexer;
init_lexer_from_string(*lexer, HLSL_BUILTIN);
if lexer.result.had_error {
print("%\n", report_messages(lexer.result.messages));
return;
}
lex_result := lex(*lexer,, *temp);
if lex_result.had_error {
print("%\n", report_messages(lex_result.messages));
return;
}
parse_state : Parse_State;
init_parse_state(*parse_state, lex_result.tokens, lexer.path);
parse_result := parse(*parse_state);
if parse_result.had_error {
print("%\n", report_messages(parse_result.messages));
return;
}
type_check(checker, parse_result.root);
if checker.had_error {
print("%\n", report_messages(checker.messages));
return;
}
for *type_var : checker.result_file.type_variables {
type_var.builtin = true;
}
checker.state = .Type_Checking;
}
type_check :: (checker : *Semantic_Checker, root : *AST_Node) {
traverse(checker, root);
}
check :: (result : *Compile_Result) {
if result.had_error {
return;
}
for *file : result.files {
checker : Semantic_Checker;
checker.current_buffer_index = 0;
checker.current_sampler_index = 0;
checker.current_texture_index = 0;
checker.result_file = file;
array_reserve(*checker.messages, 32);
init_semantic_checker(*checker, file.ast_root, file.file.path);
// @Performance: Should have this built in stuff done earlier and only once
add_hlsl_builtins(*checker);
type_check(*checker, file.ast_root);
result.had_error |= checker.had_error;
copy_messages(checker.messages, *result.messages);
}
}
// ===========================================================
// Pretty printing
#scope_file
type_to_string :: (type_variable : Type_Variable) -> string {
if type_variable.type == {
case .Invalid;
return "{{invalid}}";
case .Unit;
return "()";
case .Int; #through;
case .Half; #through;
case .Float; #through;
case .Sampler; #through;
case .Texture2D; #through;
case .Double; {
return Typenames[type_variable.type];
}
case .Function; #through;
case .Struct; {
return type_variable.typename;
}
case .Array;
return "array";
}
return "";
}
#scope_export
print_key :: (scope_stack : *Scope_Stack, current_scope : Scope_Handle, builder : *String_Builder, name : string) {
scope := get_scope(scope_stack, current_scope);
target_length := scope.longest_key_length + 1;
missing_padding := target_length - name.count;
str := tprint("[%]", name);
append(builder, str);
for 0..missing_padding - 1 {
append(builder, " ");
}
append(builder, ": ");
}
pretty_print_function :: (scope_stack : *Scope_Stack, current_scope : Scope_Handle, variables : []Type_Variable, builder : *String_Builder, name : string, function : Type_Variable, indentation : int) {
indent(builder, indentation);
print_key(scope_stack, current_scope, builder, name);
append(builder, "(");
for child : function.source_node.children {
if child.kind == .FieldList {
for field : child.children {
tv := from_handle(variables, field.type_variable);
if tv.type != .Function {
if tv.builtin {
print_to_builder(builder, "%", tv.name);
} else {
print_to_builder(builder, "% : %", tv.name, type_to_string(tv));
}
} else {
pretty_print_function(scope_stack, current_scope, variables, builder, "", tv, 0);
}
if it_index < child.children.count - 1 {
append(builder, ", ");
}
}
}
}
if function.return_type_variable> 0 {
print_to_builder(builder, ") -> %\n", type_to_string(from_handle(variables, function.return_type_variable)));
} else {
append(builder, ")\n");
}
}
pretty_print_struct :: (scope_stack : *Scope_Stack, current_scope : Scope_Handle, variables : []Type_Variable, builder : *String_Builder, name : string, struct_type : Type_Variable, indentation : int) {
indent(builder, indentation);
print_key(scope_stack, current_scope, builder, name);
append(builder, "{");
for 0..struct_type.children.count - 1 {
child_handle := struct_type.children[it];
child := from_handle(variables, child_handle);
print_to_builder(builder, child.name);
append(builder, " : ");
print_to_builder(builder, type_to_string(child));
if it < struct_type.children.count - 1 {
append(builder, ", ");
}
}
append(builder, "}\n");
}
pretty_print_scope :: (current_scope : Scope_Handle, scope_stack : Scope_Stack, variables : []Type_Variable, scope : *Scope, builder : *String_Builder, indentation : int = 0) {
if scope.builtin {
return;
}
table := scope.table;
indent(builder, indentation);
append(builder, "scope (");
if scope.name.count > 0 {
print_to_builder(builder, "%", scope.name);
} else {
append(builder, "global");
}
append(builder, ") [");
if scope.table.count > 0 {
append(builder, "\n");
}
for table {
key, value := it_index, it;
if value.builtin continue;
if value.functions.count > 0 {
for func : value.functions {
type_variable := from_handle(variables, func.type_variable);
if type_variable.type == {
case .Function; {
pretty_print_function(*scope_stack, current_scope, variables, builder, key, type_variable, 1);
}
case .CBuffer; #through;
case .Properties; #through;
case .Struct; {
if type_variable.typename.count > 0 && type_variable.kind != .Declaration {
indent(builder, indentation + 1);
print_key(*scope_stack, current_scope, builder, key);
print_type_variable(builder, variables, type_variable);
append(builder, "\n");
// print_to_builder(builder, "%\n", type_variable.typename);
} else {
pretty_print_struct(*scope_stack, current_scope, variables, builder, key, type_variable, 1);
}
}
case; {
indent(builder, indentation + 1);
print_key(*scope_stack, current_scope, builder, key);
print_to_builder(builder, "%\n", type_to_string(type_variable));
}
}
}
} else {
type_variable := from_handle(variables, value.type_variable);
if type_variable.type == {
case .Function; {
pretty_print_function(*scope_stack, current_scope, variables, builder, key, type_variable, 1);
}
case .CBuffer; #through;
case .Properties; #through;
case .Struct; {
if type_variable.typename.count > 0 && type_variable.kind != .Declaration {
indent(builder, indentation + 1);
print_key(*scope_stack, current_scope, builder, key);
print_to_builder(builder, "%\n", type_variable.typename);
} else {
pretty_print_struct(*scope_stack, current_scope, variables, builder, key, type_variable, 1);
}
}
case; {
indent(builder, indentation + 1);
print_key(*scope_stack, current_scope, builder, key);
print_to_builder(builder, "%\n", type_to_string(type_variable));
}
}
}
}
for child : scope.children {
child_scope := *scope_stack.stack[child - 1];
pretty_print_scope(current_scope, *scope_stack, variables, child_scope, builder, indentation + 1);
}
if scope.table.count > 0 {
indent(builder, indentation);
}
append(builder, "]\n");
}
print_type_variable :: (builder : *String_Builder, variables : []Type_Variable, variable : Type_Variable) {
if variable.builtin {
if variable.type != .Function || variable.type != .Struct {
print_to_builder(builder, "%", type_to_string(variable));
} else {
print_to_builder(builder, "%", variable.name);
}
} else {
node := variable.source_node;
if node {
if node.kind == {
case .Properties; {
if node.name.count > 0 {
print_to_builder(builder, "% : ", node.name);
}
append(builder, "properties");
}
case .Meta; {
append(builder, "meta");
}
case .Function; #through;
case .Struct; #through;
case .CBuffer; #through;
case .Field; {
if variable.struct_field_parent {
print_to_builder(builder, "%.", variable.struct_field_parent.name);
}
print_to_builder(builder, "%", node.name);
}
case .Binary; {
left_most := node.children[0];
while left_most.kind == .Binary {
left_most = left_most.children[0];
}
right_most := node.children[1];
while right_most.kind == .Binary {
right_most = right_most.children[1];
}
source_location : Source_Range;
source_location.begin = left_most.source_location.main_token;
source_location.end = right_most.source_location.main_token;
source_location.main_token = node.source_location.main_token;
print_from_source_location(builder, source_location);
}
case .Call; {
if variable.return_type_variable{
assert(false);
print_to_builder(builder, "%", variable.typename);
print_type_variable(builder, variables, variable.return_type_variable);
}
print_to_builder(builder, "%(", node.name);
for child : node.children {
if child.kind == .ArgList {
for arg : child.children {
print_type_variable(builder, variables, arg.type_variable);
if it_index < child.children.count - 1 {
append(builder, ", ");
}
}
}
}
append(builder, ")");
}
case; {
print_from_source_location(builder, node.source_location);
}
}
}
}
}
print_type_variable :: (builder : *String_Builder, variables : []Type_Variable, handle : Type_Variable_Handle) {
variable := from_handle(variables, handle);
print_type_variable(builder, variables, variable);
}
pretty_print_symbol_table :: (checker : *Semantic_Checker, allocator : Allocator) -> string {
builder : String_Builder;
init_string_builder(*builder,, allocator);
pretty_print_scope(xx checker.current_scope, checker.result_file.scope_stack, checker.result_file.type_variables, *checker.result_file.scope_stack.stack[0], *builder);
return builder_to_string(*builder,, allocator);
}
pretty_print_symbol_table :: (result : *Compile_Result, allocator : Allocator) -> string {
builder : String_Builder;
init_string_builder(*builder,, allocator);
for *file : result.files {
current_scope := cast(Scope_Handle)1;
pretty_print_scope(current_scope, file.scope_stack, file.type_variables, *file.scope_stack.stack[0], *builder);
}
return builder_to_string(*builder,, allocator);
}
#scope_module
#import "ncore";
#import "Hash_Table";
#import "String";