Compare commits
59 Commits
dev
...
8b2141df16
| Author | SHA1 | Date | |
|---|---|---|---|
| 8b2141df16 | |||
| 7fefe0ecf6 | |||
| f99f86bc37 | |||
| d5476b54d7 | |||
| 622ce388fa | |||
| b0653b6563 | |||
| d6ea2e4c2f | |||
| 361a310ed1 | |||
| 78e6d6146e | |||
| 79ec6cc42f | |||
| 9461fe626f | |||
| ceafd197f5 | |||
| f4a9592f26 | |||
| 9cf51a1534 | |||
| 11c936ba7f | |||
| 4924b01eac | |||
| 603b625e21 | |||
| 9e0728f952 | |||
| 94fc3a4dad | |||
| 14f8b20d5f | |||
| 4825623c73 | |||
| da87209690 | |||
| e0908a67c0 | |||
| ab711b5610 | |||
| f6801e3eeb | |||
| 4f37ed03c0 | |||
| 5b2e2e936b | |||
| b491a56409 | |||
| 45f67e16a8 | |||
| 01ffe9c73d | |||
| 382d790c5b | |||
| 27933e599a | |||
| b7e34a22b2 | |||
| c36712b3ed | |||
| 6b6c7bce62 | |||
| e356c5a3a9 | |||
| 5ec2186a42 | |||
| af3e298b29 | |||
| af42b61ed6 | |||
| cd167d1560 | |||
| 8ce8651d6b | |||
| 42c5baa846 | |||
| 45ea54cf93 | |||
| b4d119230b | |||
| a72a9ff50d | |||
| 41d1dd406d | |||
| bc69a39570 | |||
| aaeda22fa3 | |||
| 4b927b6be9 | |||
| 85b23f90e5 | |||
| ec31046d30 | |||
| 8bd766281e | |||
| 4053400152 | |||
| 1adb289c10 | |||
| d65c6359db | |||
| d08529a3eb | |||
| 7787d1307b | |||
| 4deb07027f | |||
| f13508262b |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +1,6 @@
|
|||||||
[submodule "modules/nbrutil"]
|
[submodule "modules/nbrutil"]
|
||||||
path = modules/ncore
|
path = modules/ncore
|
||||||
url = git@git.nbross.com:nielsbross/NCore.git
|
url = git@git.nbross.com:nielsbross/NCore.git
|
||||||
|
[submodule "modules/tracy"]
|
||||||
|
path = modules/tracy
|
||||||
|
url = https://github.com/rluba/jai-tracy.git
|
||||||
|
|||||||
284
AST.jai
284
AST.jai
@@ -14,12 +14,16 @@ AST_Kind :: enum {
|
|||||||
Meta;
|
Meta;
|
||||||
Instance;
|
Instance;
|
||||||
//==
|
//==
|
||||||
|
// Directives
|
||||||
|
If_Directive;
|
||||||
|
|
||||||
// Hint;
|
// Hint;
|
||||||
// Type;
|
// Type;
|
||||||
// Operator;
|
// Operator;
|
||||||
Call;
|
Call;
|
||||||
Struct;
|
Struct;
|
||||||
|
If;
|
||||||
|
For;
|
||||||
CBuffer;
|
CBuffer;
|
||||||
FieldList;
|
FieldList;
|
||||||
ArgList;
|
ArgList;
|
||||||
@@ -71,22 +75,25 @@ AST_Node :: struct {
|
|||||||
pixel_entry_point : bool;
|
pixel_entry_point : bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
// Pretty printing
|
// Pretty printing
|
||||||
pretty_print_call :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_call :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
append(builder, "(");
|
append(builder, "(");
|
||||||
append(builder, node.name);
|
append(builder, node.name);
|
||||||
if node.children.count > 0 {
|
if node.children.count > 0 {
|
||||||
append(builder, " ");
|
append(builder, " ");
|
||||||
pretty_print_children(node.children[0], indentation, builder, flags = 0);
|
pretty_print_children(node.children[0], indentation, builder, flags = 0, skip_indent = true);
|
||||||
}
|
}
|
||||||
append(builder, ")");
|
append(builder, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_arglist :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_arglist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
append(builder, "[");
|
append(builder, "[");
|
||||||
|
|
||||||
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
||||||
@@ -94,8 +101,10 @@ pretty_print_arglist :: (node : *AST_Node, indentation : int, builder : *String_
|
|||||||
append(builder, "]");
|
append(builder, "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_fieldlist :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_fieldlist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
append(builder, "[");
|
append(builder, "[");
|
||||||
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
||||||
@@ -103,13 +112,16 @@ pretty_print_fieldlist :: (node : *AST_Node, indentation : int, builder : *Strin
|
|||||||
append(builder, "]");
|
append(builder, "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_field :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_field :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
print_to_builder(builder, tprint("(:= %", node.name));
|
print_to_builder(builder, tprint("(:= %", node.name));
|
||||||
|
|
||||||
if node.kind != .Unnamed_Field && node.token.ident_value.count > 0 {
|
if node.kind != .Unnamed_Field && node.token.ident_value.count > 0 {
|
||||||
if node.array_field {
|
if node.array_field {
|
||||||
append(builder, " [");
|
append(builder, " [");
|
||||||
pretty_print_node(node.children[0], 0, builder);
|
pretty_print_node(node.children[0], indentation, builder, true);
|
||||||
append(builder, "].");
|
append(builder, "].");
|
||||||
print_to_builder(builder, "%", node.token.ident_value);
|
print_to_builder(builder, "%", node.token.ident_value);
|
||||||
} else {
|
} else {
|
||||||
@@ -126,31 +138,68 @@ pretty_print_field :: (node : *AST_Node, indentation : int, builder : *String_Bu
|
|||||||
|
|
||||||
if !node.array_field && node.children.count > 0 {
|
if !node.array_field && node.children.count > 0 {
|
||||||
append(builder, " ");
|
append(builder, " ");
|
||||||
pretty_print_children(node, indentation, builder);
|
pretty_print_node(node.children[0], indentation, builder, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
append(builder, ")");
|
append(builder, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
Children_Print_Flags :: enum_flags {
|
Children_Print_Flags :: enum_flags {
|
||||||
NewLine :: 1 << 0;
|
NewLine :: 1 << 0;
|
||||||
Separator :: 1 << 1;
|
Separator :: 1 << 1;
|
||||||
Space :: 1 << 2;
|
Space :: 1 << 2;
|
||||||
|
Dont_Skip_Indent_On_First :: 1 << 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_children :: (parent : *AST_Node, indentation : int, builder : *String_Builder, flags : Children_Print_Flags = .Separator) {
|
pretty_print_block :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if node.children.count == 0 {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
append(builder, "()");
|
||||||
|
} else {
|
||||||
|
flags := Children_Print_Flags.NewLine;
|
||||||
|
if !skip_indent {
|
||||||
|
flags |= .Dont_Skip_Indent_On_First;
|
||||||
|
}
|
||||||
|
pretty_print_children(node, indentation, builder, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_print_children :: (parent : *AST_Node, indentation : int, builder : *String_Builder, flags : Children_Print_Flags = .Separator, skip_indent := false) {
|
||||||
if !parent {
|
if !parent {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
children := parent.children;
|
children := parent.children;
|
||||||
for child : children {
|
for child : children {
|
||||||
if it_index > 0 {
|
// if it_index > 0 {
|
||||||
indent(builder, indentation);
|
// indent(builder, indentation);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if !child continue;
|
if !child continue;
|
||||||
pretty_print_node(child, 0, builder);
|
|
||||||
|
|
||||||
|
ind := indentation;
|
||||||
|
if flags & .Dont_Skip_Indent_On_First {
|
||||||
|
ind = indentation;
|
||||||
|
} else {
|
||||||
|
if it_index == 0 {
|
||||||
|
ind = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if skip_indent{
|
||||||
|
ind = 0;
|
||||||
|
}
|
||||||
|
// skip := ifx it_index > 0 then false else true;
|
||||||
|
|
||||||
|
if child.kind == .Function {
|
||||||
|
pretty_print_declaration(child, ind, builder);
|
||||||
|
} else {
|
||||||
|
pretty_print_node(child, ind, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if it_index != children.count - 1 {
|
if it_index != children.count - 1 {
|
||||||
if flags & .Separator {
|
if flags & .Separator {
|
||||||
@@ -176,6 +225,16 @@ op_to_string :: (oper : Token) -> string {
|
|||||||
return "*";
|
return "*";
|
||||||
case .TOKEN_SLASH;
|
case .TOKEN_SLASH;
|
||||||
return "/";
|
return "/";
|
||||||
|
case .TOKEN_MINUSEQUALS;
|
||||||
|
return "-=";
|
||||||
|
case .TOKEN_PLUSEQUALS;
|
||||||
|
return "+=";
|
||||||
|
case .TOKEN_DIVEQUALS;
|
||||||
|
return "/=";
|
||||||
|
case .TOKEN_TIMESEQUALS;
|
||||||
|
return "*=";
|
||||||
|
case .TOKEN_MODEQUALS;
|
||||||
|
return "%=";
|
||||||
case .TOKEN_ISEQUAL;
|
case .TOKEN_ISEQUAL;
|
||||||
return "==";
|
return "==";
|
||||||
case .TOKEN_ASSIGN;
|
case .TOKEN_ASSIGN;
|
||||||
@@ -198,94 +257,184 @@ op_to_string :: (oper : Token) -> string {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_binary :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_binary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
append(builder, "(");
|
append(builder, "(");
|
||||||
op := node.token;
|
op := node.token;
|
||||||
|
|
||||||
print_to_builder(builder, op_to_string(op));
|
print_to_builder(builder, op_to_string(op));
|
||||||
append(builder, " ");
|
append(builder, " ");
|
||||||
|
|
||||||
pretty_print_children(node, 0, builder, flags = 0);
|
pretty_print_node(node.children[0], 0, builder);
|
||||||
|
append(builder, " ");
|
||||||
|
pretty_print_node(node.children[1], 0, builder);
|
||||||
append(builder, ")");
|
append(builder, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_unary :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_unary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
op := node.token;
|
||||||
|
|
||||||
|
print_to_builder(builder, op_to_string(op));
|
||||||
|
pretty_print_node(node.children[0], 0, builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
print_return_node :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
print_return_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
append(builder, "(return ");
|
append(builder, "(return ");
|
||||||
|
|
||||||
pretty_print_children(node, 0, builder);
|
pretty_print_children(node, indentation, builder);
|
||||||
|
|
||||||
append(builder, ")");
|
append(builder, ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
print_expression_statement :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_if :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
append(builder, "(if ");
|
||||||
|
|
||||||
|
condition := node.children[0];
|
||||||
|
pretty_print_node(condition, 0, builder);
|
||||||
|
append(builder, "\n");
|
||||||
|
|
||||||
|
body := node.children[1];
|
||||||
|
// indent(builder,indentation + 4);
|
||||||
|
// append(builder, "(");
|
||||||
|
pretty_print_node(body, indentation + 4, builder);
|
||||||
|
// append(builder, ")");
|
||||||
|
|
||||||
|
if node.children.count == 3 {
|
||||||
|
append(builder, "\n");
|
||||||
|
pretty_print_node(node.children[2], indentation + 4, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(builder, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
pretty_print_for :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(builder, "(for ");
|
||||||
|
|
||||||
|
loop_iterator := node.token;
|
||||||
|
print_to_builder(builder, "% : ", loop_iterator.ident_value);
|
||||||
|
|
||||||
|
pretty_print_node(node.children[0], 0, builder);
|
||||||
|
append(builder, "..");
|
||||||
|
pretty_print_node(node.children[1], 0, builder);
|
||||||
|
append(builder, "\n");
|
||||||
|
|
||||||
|
pretty_print_node(node.children[2], indentation + 4, builder);
|
||||||
|
|
||||||
|
append(builder, ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
print_expression_statement :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
if node.children[0] {
|
if node.children[0] {
|
||||||
pretty_print_node(node.children[0], indentation, builder);
|
pretty_print_node(node.children[0], 0, builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_node :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
if node.kind == {
|
if node.kind == {
|
||||||
case .Return; {
|
case .Return; {
|
||||||
print_return_node(node, indentation, builder);
|
print_return_node(node, indentation, builder, skip_indent);
|
||||||
|
}
|
||||||
|
case .If; {
|
||||||
|
pretty_print_if(node, indentation, builder, skip_indent);
|
||||||
|
}
|
||||||
|
case .If_Directive; {
|
||||||
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
|
append(builder, "(#if ");
|
||||||
|
|
||||||
|
condition := node.children[0];
|
||||||
|
pretty_print_node(condition, 0, builder);
|
||||||
|
append(builder, "\n");
|
||||||
|
|
||||||
|
body := node.children[1];
|
||||||
|
// indent(builder,indentation + 4);
|
||||||
|
// append(builder, "(");
|
||||||
|
pretty_print_node(body, indentation + 4, builder);
|
||||||
|
// append(builder, ")");
|
||||||
|
|
||||||
|
if node.children.count == 3 { //@Note: Else branch
|
||||||
|
append(builder, "\n");
|
||||||
|
pretty_print_node(node.children[2], indentation + 4, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(builder, ")");
|
||||||
|
}
|
||||||
|
case .For; {
|
||||||
|
pretty_print_for(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Struct;
|
case .Struct;
|
||||||
case .ArgList; {
|
case .ArgList; {
|
||||||
pretty_print_arglist(node, indentation + 2, builder);
|
pretty_print_arglist(node, indentation + 2, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .FieldList; {
|
case .FieldList; {
|
||||||
pretty_print_fieldlist(node, indentation + 2, builder);
|
pretty_print_fieldlist(node, indentation + 2, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Field; {
|
case .Field; {
|
||||||
pretty_print_field(node, indentation, builder);
|
pretty_print_field(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Unnamed_Field; {
|
case .Unnamed_Field; {
|
||||||
pretty_print_field(node, indentation, builder);
|
pretty_print_field(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Block; {
|
case .Block; {
|
||||||
pretty_print_children(node, indentation + 2, builder, flags = .NewLine);
|
pretty_print_block(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Binary; {
|
case .Binary; {
|
||||||
pretty_print_binary(node, indentation, builder);
|
pretty_print_binary(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Unary; {
|
case .Unary; {
|
||||||
pretty_print_unary(node, indentation, builder);
|
pretty_print_unary(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Variable; {
|
case .Variable; {
|
||||||
pretty_print_variable(node, indentation, builder);
|
pretty_print_variable(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Expression_Statement; {
|
case .Expression_Statement; {
|
||||||
print_expression_statement(node, indentation, builder);
|
print_expression_statement(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Integer; {
|
case .Integer; {
|
||||||
print_to_builder(builder, "%", node.integer_value);
|
print_to_builder(builder, "%", node.integer_value, skip_indent);
|
||||||
}
|
}
|
||||||
case .Float; {
|
case .Float; {
|
||||||
print_to_builder(builder, "%", node.float_value);
|
print_to_builder(builder, "%", node.float_value, skip_indent);
|
||||||
}
|
}
|
||||||
case .Call; {
|
case .Call; {
|
||||||
pretty_print_call(node, indentation, builder);
|
pretty_print_call(node, indentation, builder, skip_indent);
|
||||||
}
|
}
|
||||||
case .Error; {
|
case .Error; {
|
||||||
print_to_builder(builder, "(error \"%\")", node.name);
|
print_to_builder(builder, "(error \"%\")", node.name, skip_indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_variable :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_variable :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
print_to_builder(builder, "%", node.name);
|
print_to_builder(builder, "%", node.name);
|
||||||
|
|
||||||
for child : node.children {
|
for child : node.children {
|
||||||
if child.kind == .Variable {
|
if child.kind == .Variable {
|
||||||
append(builder, ".");
|
append(builder, ".");
|
||||||
pretty_print_variable(child, indentation, builder);
|
pretty_print_variable(child, indentation, builder, skip_indent = true);
|
||||||
} else if child.kind == .Unary {
|
} else if child.kind == .Unary {
|
||||||
append(builder, "[");
|
append(builder, "[");
|
||||||
pretty_print_node(child.children[0], 0, builder);
|
pretty_print_node(child.children[0], 0, builder);
|
||||||
@@ -294,8 +443,10 @@ pretty_print_variable :: (node : *AST_Node, indentation : int, builder : *String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder : *String_Builder) {
|
pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||||
indent(builder, indentation);
|
if !skip_indent {
|
||||||
|
indent(builder, indentation);
|
||||||
|
}
|
||||||
append(builder, "(");
|
append(builder, "(");
|
||||||
|
|
||||||
if declaration.foreign_declaration {
|
if declaration.foreign_declaration {
|
||||||
@@ -314,6 +465,10 @@ pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder
|
|||||||
append(builder, "pixel ");
|
append(builder, "pixel ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if declaration.kind == .If_Directive {
|
||||||
|
append(builder, "#if ");
|
||||||
|
}
|
||||||
|
|
||||||
if declaration.kind == .Properties {
|
if declaration.kind == .Properties {
|
||||||
append(builder, "properties");
|
append(builder, "properties");
|
||||||
if declaration.name.count > 0 {
|
if declaration.name.count > 0 {
|
||||||
@@ -330,7 +485,9 @@ pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder
|
|||||||
} else if declaration.kind == .CBuffer {
|
} else if declaration.kind == .CBuffer {
|
||||||
append(builder, "constant_buffer ");
|
append(builder, "constant_buffer ");
|
||||||
}
|
}
|
||||||
print_to_builder(builder, "%", declaration.name);
|
if declaration.kind != .If_Directive {
|
||||||
|
print_to_builder(builder, "%", declaration.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if declaration.kind == .Function && declaration.token.kind == .TOKEN_IDENTIFIER{
|
if declaration.kind == .Function && declaration.token.kind == .TOKEN_IDENTIFIER{
|
||||||
@@ -340,12 +497,35 @@ pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder
|
|||||||
print_to_builder(builder, " (@%)", hint.string_value);
|
print_to_builder(builder, " (@%)", hint.string_value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if declaration.children.count > 0 {
|
if declaration.children.count > 0 {
|
||||||
print_to_builder(builder, "\n");
|
if declaration.kind == .If_Directive {
|
||||||
pretty_print_children(declaration, indentation + 1, builder, flags = .NewLine);
|
pretty_print_node(declaration.children[0], 0, builder);
|
||||||
|
append(builder, "\n");
|
||||||
|
pretty_print_node(declaration.children[1], indentation + 5, builder);
|
||||||
|
|
||||||
|
if declaration.children.count > 2 {
|
||||||
|
append(builder, "\n");
|
||||||
|
if declaration.children[2].kind == .If_Directive {
|
||||||
|
pretty_print_declaration(declaration.children[2], indentation + 5, builder);
|
||||||
|
} else {
|
||||||
|
pretty_print_node(declaration.children[2], indentation + 5, builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print_to_builder(builder, "\n");
|
||||||
|
|
||||||
|
flags := Children_Print_Flags.NewLine;
|
||||||
|
|
||||||
|
if declaration.parent && declaration.parent.parent {
|
||||||
|
if declaration.parent.parent.kind == .If_Directive {
|
||||||
|
indent(builder, indentation - 1); //@Note: Hack the indent for now... Wow this is stupid, but it works!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pretty_print_children(declaration, indentation + 1, builder, flags = flags);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
append(builder, ")");
|
append(builder, ")");
|
||||||
|
|||||||
233
Codegen.jai
233
Codegen.jai
@@ -11,36 +11,41 @@ Output_Language :: enum {
|
|||||||
HLSL;
|
HLSL;
|
||||||
GLSL; // @Incomplete
|
GLSL; // @Incomplete
|
||||||
MLSL; // @Incomplete
|
MLSL; // @Incomplete
|
||||||
|
// SPIRV; // @Incomplete: Should we do this?
|
||||||
}
|
}
|
||||||
|
|
||||||
Codegen_State :: struct {
|
Codegen_State :: struct {
|
||||||
path : string;
|
path : string;
|
||||||
|
|
||||||
scope_stack : Scope_Stack;
|
|
||||||
current_scope : Scope_Handle;
|
current_scope : Scope_Handle;
|
||||||
|
|
||||||
type_variables : []Type_Variable;
|
|
||||||
root : *AST_Node;
|
|
||||||
|
|
||||||
output_language : Output_Language;
|
output_language : Output_Language;
|
||||||
|
|
||||||
builder : String_Builder;
|
builder : String_Builder;
|
||||||
|
|
||||||
result : Codegen_Result;
|
result : *Compiler_Context;
|
||||||
}
|
}
|
||||||
|
|
||||||
Codegen_Result :: struct {
|
Reserved_HLSL_Words :: string.[
|
||||||
messages : [..]Compiler_Message;
|
"texture",
|
||||||
|
"sampler",
|
||||||
|
"matrix",
|
||||||
|
"line",
|
||||||
|
"precise",
|
||||||
|
"shared",
|
||||||
|
"triangle",
|
||||||
|
"triangleadj",
|
||||||
|
];
|
||||||
|
|
||||||
had_error : bool;
|
Reserved_MLSL_Words :: string.[
|
||||||
|
""
|
||||||
|
];
|
||||||
|
|
||||||
result_text : string; // @Incomplete(nb): Result for now, should likely be far more sophisticated.
|
Reserved_GLSL_Words :: string.[
|
||||||
}
|
""
|
||||||
|
];
|
||||||
|
|
||||||
init_codegen_state :: (state : *Codegen_State, root : *AST_Node, checker_result : Semantic_Check_Result, output_language : Output_Language) {
|
init_codegen_state :: (state : *Codegen_State, result : *Compiler_Context, output_language : Output_Language) {
|
||||||
state.root = root;
|
|
||||||
state.scope_stack = checker_result.scope_stack;
|
|
||||||
state.type_variables = checker_result.type_variables;
|
|
||||||
state.current_scope = cast(Scope_Handle)1;
|
state.current_scope = cast(Scope_Handle)1;
|
||||||
state.output_language = output_language;
|
state.output_language = output_language;
|
||||||
init_string_builder(*state.builder);
|
init_string_builder(*state.builder);
|
||||||
@@ -50,7 +55,7 @@ indent :: (state : *Codegen_State, indentation : int) {
|
|||||||
for 1..indentation append(*state.builder, " ");
|
for 1..indentation append(*state.builder, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
dx11_type_to_string :: (type_variable : Type_Variable) -> string {
|
hlsl_type_to_string :: (type_variable : Type_Variable) -> string {
|
||||||
if type_variable.type == {
|
if type_variable.type == {
|
||||||
case .Invalid;
|
case .Invalid;
|
||||||
return "{{invalid}}";
|
return "{{invalid}}";
|
||||||
@@ -86,16 +91,16 @@ dx11_type_to_string :: (type_variable : Type_Variable) -> string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||||
find_result := find_symbol(state.scope_stack, node.name, state.current_scope);
|
find_result := find_symbol(state.result.scope_stack, node.name, state.current_scope);
|
||||||
|
|
||||||
field := h2tv(state.type_variables, find_result.type_variable);
|
field := from_handle(state.result.type_variables, find_result.type_variable);
|
||||||
|
|
||||||
indent(state, indentation);
|
indent(state, indentation);
|
||||||
|
|
||||||
print_to_builder(*state.builder, "% ", dx11_type_to_string(field));
|
print_to_builder(*state.builder, "% ", hlsl_type_to_string(field));
|
||||||
|
|
||||||
if field.struct_field_parent {
|
if field.struct_field_parent {
|
||||||
parent_tv := h2tv(state.type_variables, field.struct_field_parent.type_variable);
|
parent_tv := from_handle(state.result.type_variables, field.struct_field_parent.type_variable);
|
||||||
|
|
||||||
if parent_tv.typename == "properties" {
|
if parent_tv.typename == "properties" {
|
||||||
append(*state.builder, "__PROPERTIES__");
|
append(*state.builder, "__PROPERTIES__");
|
||||||
@@ -111,25 +116,28 @@ emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
print_to_builder(*state.builder, " : register(t%)", field.resource_index);
|
print_to_builder(*state.builder, " : register(t%)", field.resource_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
for i :0..node.children.count - 1 {
|
if node.children.count == 1 {
|
||||||
child := node.children[i];
|
child := node.children[0];
|
||||||
|
|
||||||
print_to_builder(*state.builder, " = ");
|
print_to_builder(*state.builder, " = ");
|
||||||
emit_node(state, child, 0);
|
emit_node(state, child, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if node.parent.kind == .Block {
|
||||||
|
append(*state.builder, ";");
|
||||||
|
}
|
||||||
|
|
||||||
for i :0..field.children.count - 1 {
|
for i :0..field.children.count - 1 {
|
||||||
child := h2tv(state.type_variables, field.children[i]);
|
child := from_handle(state.result.type_variables, field.children[i]);
|
||||||
emit_node(state, child.source_node, 0);
|
emit_node(state, child.source_node, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for hint : node.hint_tokens {
|
for hint : node.hint_tokens {
|
||||||
if hint.ident_value == "position" {
|
if lookup_hint(hint.ident_value) == .Position {
|
||||||
// @Incomplete(nb): Should be a lookup table somewhere
|
|
||||||
append(*state.builder, " : POSITION");
|
append(*state.builder, " : POSITION");
|
||||||
} else if hint.ident_value == "uv" {
|
} else if lookup_hint(hint.ident_value) == .UV {
|
||||||
append(*state.builder, " : TEXCOORD0");
|
append(*state.builder, " : TEXCOORD0");
|
||||||
} else if hint.ident_value == "outposition" {
|
} else if lookup_hint(hint.ident_value) == .Output_Position {
|
||||||
append(*state.builder, " : SV_POSITION");
|
append(*state.builder, " : SV_POSITION");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +148,7 @@ emit_block :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
emit_node(state, statement, indentation);
|
emit_node(state, statement, indentation);
|
||||||
|
|
||||||
if it_index < node.children.count {
|
if it_index < node.children.count {
|
||||||
append(*state.builder, ";\n");
|
append(*state.builder, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,6 +173,24 @@ emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
append(*state.builder, ", ");
|
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 {
|
} else {
|
||||||
print_to_builder(*state.builder, "%(", node.name);
|
print_to_builder(*state.builder, "%(", node.name);
|
||||||
@@ -187,7 +213,7 @@ emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||||
find_result := find_symbol(state.scope_stack, ifx node.name.count > 0 then node.name else "properties", state.current_scope);
|
find_result := find_symbol(state.result.scope_stack, ifx node.name.count > 0 then node.name else "properties", state.current_scope);
|
||||||
|
|
||||||
if !find_result {
|
if !find_result {
|
||||||
message : Compiler_Message;
|
message : Compiler_Message;
|
||||||
@@ -198,7 +224,7 @@ emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int)
|
|||||||
}
|
}
|
||||||
assert(find_result != null, "Attempting to generate undeclared properties buffer. This should never happen at this stage.");
|
assert(find_result != null, "Attempting to generate undeclared properties buffer. This should never happen at this stage.");
|
||||||
|
|
||||||
variable := h2tv(state.type_variables, find_result.type_variable);
|
variable := from_handle(state.result.type_variables, find_result.type_variable);
|
||||||
|
|
||||||
print_to_builder(*state.builder, "cbuffer __PROPERTIES : register(b%) \n{\n", variable.resource_index);
|
print_to_builder(*state.builder, "cbuffer __PROPERTIES : register(b%) \n{\n", variable.resource_index);
|
||||||
|
|
||||||
@@ -210,7 +236,7 @@ emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int)
|
|||||||
for child : node.children {
|
for child : node.children {
|
||||||
if child.kind == .FieldList {
|
if child.kind == .FieldList {
|
||||||
for field : child.children {
|
for field : child.children {
|
||||||
tv := h2tv(state.type_variables, field.type_variable);
|
tv := from_handle(state.result.type_variables, field.type_variable);
|
||||||
if tv.type == .Sampler || tv.type == .Texture2D {
|
if tv.type == .Sampler || tv.type == .Texture2D {
|
||||||
array_add(*resources, field);
|
array_add(*resources, field);
|
||||||
continue;
|
continue;
|
||||||
@@ -238,7 +264,7 @@ emit_properties :: (state : *Codegen_State, node : *AST_Node, indentation : int)
|
|||||||
|
|
||||||
emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, emit_body := true) {
|
emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, emit_body := true) {
|
||||||
name := get_actual_function_name(node);
|
name := get_actual_function_name(node);
|
||||||
find_result := find_symbol(state.scope_stack, name, state.current_scope);
|
find_result := find_symbol(state.result.scope_stack, name, state.current_scope);
|
||||||
|
|
||||||
assert(find_result != null, "Attempting to generate undeclared function. This should never happen at this stage.");
|
assert(find_result != null, "Attempting to generate undeclared function. This should never happen at this stage.");
|
||||||
if !find_result {
|
if !find_result {
|
||||||
@@ -250,13 +276,13 @@ emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
for func : find_result.functions {
|
for func : find_result.functions {
|
||||||
function_variable := h2tv(state.type_variables, func.type_variable);
|
function_variable := from_handle(state.result.type_variables, func.type_variable);
|
||||||
|
|
||||||
indent(state, indentation);
|
indent(state, indentation);
|
||||||
|
|
||||||
if function_variable.return_type_variable {
|
if function_variable.return_type_variable {
|
||||||
return_variable := h2tv(state.type_variables, function_variable.return_type_variable);
|
return_variable := from_handle(state.result.type_variables, function_variable.return_type_variable);
|
||||||
print_to_builder(*state.builder, "% ", dx11_type_to_string(return_variable));
|
print_to_builder(*state.builder, "% ", hlsl_type_to_string(return_variable));
|
||||||
} else {
|
} else {
|
||||||
append(*state.builder, "void ");
|
append(*state.builder, "void ");
|
||||||
}
|
}
|
||||||
@@ -329,6 +355,21 @@ emit_operator :: (state : *Codegen_State, op_kind : Token_Kind) {
|
|||||||
case .TOKEN_SLASH; {
|
case .TOKEN_SLASH; {
|
||||||
append(*state.builder, "/");
|
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; {
|
case .TOKEN_ISEQUAL; {
|
||||||
append(*state.builder, "==");
|
append(*state.builder, "==");
|
||||||
}
|
}
|
||||||
@@ -374,17 +415,18 @@ emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
emit_field(state, node, indentation);
|
emit_field(state, node, indentation);
|
||||||
}
|
}
|
||||||
case .Block; {
|
case .Block; {
|
||||||
|
|
||||||
assert(false, "Not implemented yet: block");
|
assert(false, "Not implemented yet: block");
|
||||||
}
|
}
|
||||||
case .Variable; {
|
case .Variable; {
|
||||||
indent(*state.builder, indentation);
|
indent(*state.builder, indentation);
|
||||||
|
|
||||||
type_var := h2tv(state.type_variables, node.type_variable);
|
type_var := from_handle(state.result.type_variables, node.type_variable);
|
||||||
is_properties := type_var.typename == "properties";
|
is_properties := type_var.typename == "properties";
|
||||||
|
|
||||||
if !is_properties {
|
if !is_properties {
|
||||||
if type_var.struct_field_parent {
|
if type_var.struct_field_parent {
|
||||||
parent_tv := h2tv(state.type_variables, type_var.struct_field_parent.type_variable);
|
parent_tv := from_handle(state.result.type_variables, type_var.struct_field_parent.type_variable);
|
||||||
|
|
||||||
if parent_tv.typename == "properties" {
|
if parent_tv.typename == "properties" {
|
||||||
append(*state.builder, "__PROPERTIES__");
|
append(*state.builder, "__PROPERTIES__");
|
||||||
@@ -420,10 +462,14 @@ emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .Unary; {
|
case .Unary; {
|
||||||
assert(false, "Not implemented yet: unary");
|
indent(*state.builder, indentation);
|
||||||
|
|
||||||
|
emit_operator(state, node.token.kind);
|
||||||
|
emit_node(state, node.children[0], 0);
|
||||||
}
|
}
|
||||||
case .Expression_Statement; {
|
case .Expression_Statement; {
|
||||||
emit_node(state, node.children[0], indentation);
|
emit_node(state, node.children[0], indentation);
|
||||||
|
append(*state.builder, ";");
|
||||||
}
|
}
|
||||||
case .Call; {
|
case .Call; {
|
||||||
emit_call(state, node, indentation);
|
emit_call(state, node, indentation);
|
||||||
@@ -432,7 +478,70 @@ emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
indent(*state.builder, indentation);
|
indent(*state.builder, indentation);
|
||||||
append(*state.builder, "return ");
|
append(*state.builder, "return ");
|
||||||
emit_node(state, node.children[0], 0);
|
emit_node(state, node.children[0], 0);
|
||||||
|
append(*state.builder, ";");
|
||||||
}
|
}
|
||||||
|
case .For; {
|
||||||
|
if node.parent.kind != .For {
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(*state.builder, "for ");
|
||||||
|
|
||||||
|
loop_ident := node.token.ident_value;
|
||||||
|
begin_val := node.children[0].integer_value;
|
||||||
|
end_val := node.children[1].integer_value;
|
||||||
|
print_to_builder(*state.builder, "(int % = %; % < %; %++)\n", loop_ident, begin_val, loop_ident, end_val, loop_ident);
|
||||||
|
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "{\n");
|
||||||
|
|
||||||
|
emit_block(state, node.children[2], indentation + 1);
|
||||||
|
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "}\n");
|
||||||
|
}
|
||||||
|
case .If; {
|
||||||
|
if node.parent.kind != .If {
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
}
|
||||||
|
|
||||||
|
append(*state.builder, "if ");
|
||||||
|
|
||||||
|
cond := node.children[0];
|
||||||
|
emit_node(state, cond, 0);
|
||||||
|
|
||||||
|
body := node.children[1];
|
||||||
|
append(*state.builder, "\n");
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "{\n");
|
||||||
|
|
||||||
|
emit_block(state, body, indentation + 1);
|
||||||
|
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "}\n");
|
||||||
|
|
||||||
|
if node.children.count == 3 {
|
||||||
|
emit_else(state, node.children[2], indentation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_else :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "else ");
|
||||||
|
|
||||||
|
if node.kind == .If {
|
||||||
|
emit_node(state, node, indentation);
|
||||||
|
} else if node.kind == .Block {
|
||||||
|
append(*state.builder, "\n");
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "{\n");
|
||||||
|
|
||||||
|
emit_block(state, node, indentation + 1);
|
||||||
|
|
||||||
|
indent(*state.builder, indentation);
|
||||||
|
append(*state.builder, "}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +559,7 @@ emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
print_to_builder(*state.builder, "struct %", node.name);
|
print_to_builder(*state.builder, "struct %", node.name);
|
||||||
|
|
||||||
current_scope := state.current_scope;
|
current_scope := state.current_scope;
|
||||||
state.current_scope = h2tv(state.type_variables, node.type_variable).scope;
|
state.current_scope = from_handle(state.result.type_variables, node.type_variable).scope;
|
||||||
|
|
||||||
field_list := node.children[0];
|
field_list := node.children[0];
|
||||||
|
|
||||||
@@ -467,11 +576,11 @@ emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
emit_cbuffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
emit_cbuffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||||
variable := h2tv(state.type_variables, node.type_variable);
|
variable := from_handle(state.result.type_variables, node.type_variable);
|
||||||
print_to_builder(*state.builder, "cbuffer % : register(b%)", variable.name, variable.resource_index);
|
print_to_builder(*state.builder, "cbuffer % : register(b%)", variable.name, variable.resource_index);
|
||||||
|
|
||||||
current_scope := state.current_scope;
|
current_scope := state.current_scope;
|
||||||
state.current_scope = h2tv(state.type_variables, node.type_variable).scope;
|
state.current_scope = from_handle(state.result.type_variables, node.type_variable).scope;
|
||||||
|
|
||||||
field_list := node.children[0];
|
field_list := node.children[0];
|
||||||
|
|
||||||
@@ -504,11 +613,37 @@ emit_declaration :: (state : *Codegen_State, node : *AST_Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
codegen :: (state : *Codegen_State) -> Codegen_Result {
|
codegen :: (result : *Compiler_Context, allocator := temp) {
|
||||||
|
codegen(result, .HLSL);
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen :: (result : *Compiler_Context, output_language : Output_Language, allocator := temp) {
|
||||||
|
if result.had_error {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_context := context;
|
||||||
|
new_context.allocator = allocator;
|
||||||
|
push_context new_context {
|
||||||
|
init_context_allocators();
|
||||||
|
defer clear_context_allocators();
|
||||||
|
|
||||||
|
state : Codegen_State;
|
||||||
|
state.result = result;
|
||||||
|
state.current_scope = cast(Scope_Handle)1;
|
||||||
|
state.output_language = output_language;
|
||||||
|
init_string_builder(*state.builder);
|
||||||
|
|
||||||
|
codegen(*state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#scope_file
|
||||||
|
codegen :: (state : *Codegen_State) {
|
||||||
found_function : bool = false;
|
found_function : bool = false;
|
||||||
// found_struct : bool = false;
|
// found_struct : bool = false;
|
||||||
|
|
||||||
// for variable : state.type_variables {
|
// for variable : state.result.type_variables {
|
||||||
// if variable.type == .Struct && variable.kind == .Declaration && !variable.builtin {
|
// if variable.type == .Struct && variable.kind == .Declaration && !variable.builtin {
|
||||||
// if variable.source_node.kind == .Properties continue;
|
// if variable.source_node.kind == .Properties continue;
|
||||||
// if variable.source_node.kind == .Meta continue;
|
// if variable.source_node.kind == .Meta continue;
|
||||||
@@ -521,7 +656,7 @@ codegen :: (state : *Codegen_State) -> Codegen_Result {
|
|||||||
// append(*state.builder, "\n");
|
// append(*state.builder, "\n");
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for variable : state.type_variables {
|
for variable : state.result.type_variables {
|
||||||
if variable.type == .Function && !variable.builtin
|
if variable.type == .Function && !variable.builtin
|
||||||
&& !variable.source_node.vertex_entry_point && !variable.source_node.pixel_entry_point {
|
&& !variable.source_node.vertex_entry_point && !variable.source_node.pixel_entry_point {
|
||||||
emit_function(state, variable.source_node, 0, false);
|
emit_function(state, variable.source_node, 0, false);
|
||||||
@@ -532,22 +667,14 @@ codegen :: (state : *Codegen_State) -> Codegen_Result {
|
|||||||
append(*state.builder, "\n");
|
append(*state.builder, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
for declaration : state.root.children {
|
for declaration : state.result.root.children {
|
||||||
if declaration.foreign_declaration {
|
if declaration.foreign_declaration {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
emit_declaration(state, declaration);
|
emit_declaration(state, declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.result.result_text = builder_to_string(*state.builder);
|
state.result.codegen_result_text = builder_to_string(*state.builder);
|
||||||
|
|
||||||
return state.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
codegen :: (ast_root : *AST_Node, checker_result : Semantic_Check_Result, output_language : Output_Language) -> Codegen_Result {
|
|
||||||
codegen_state : Codegen_State;
|
|
||||||
init_codegen_state(*codegen_state, ast_root, checker_result, output_language);
|
|
||||||
return codegen(*codegen_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#scope_module
|
#scope_module
|
||||||
|
|||||||
12
Error.jai
12
Error.jai
@@ -102,20 +102,20 @@ copy_messages :: (source : []Compiler_Message, dest : *[..]Compiler_Message) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
report_messages :: (messages : []Compiler_Message) -> string {
|
report_messages :: (ctx : *Compiler_Context, messages : []Compiler_Message) -> string {
|
||||||
builder : String_Builder;
|
builder : String_Builder;
|
||||||
init_string_builder(*builder);
|
init_string_builder(*builder);
|
||||||
for message : messages {
|
for message : messages {
|
||||||
report_message(*builder, message);
|
report_message(ctx, *builder, message);
|
||||||
}
|
}
|
||||||
return builder_to_string(*builder);
|
return builder_to_string(*builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
report_message :: (builder : *String_Builder, message : Compiler_Message) {
|
report_message :: (ctx : *Compiler_Context, builder : *String_Builder, message : Compiler_Message) {
|
||||||
report_message(builder, message.path, message.message, message.source_locations, message.message_kind, message.report_source_location);
|
report_message(ctx, builder, message.path, message.message, message.source_locations, message.message_kind, message.report_source_location);
|
||||||
}
|
}
|
||||||
|
|
||||||
report_message :: (builder : *String_Builder, path : string, message : string, source_locations : []Source_Range, kind : Message_Kind, report_source_location : bool = false) {
|
report_message :: (ctx : *Compiler_Context, builder : *String_Builder, path : string, message : string, source_locations : []Source_Range, kind : Message_Kind, report_source_location : bool = false) {
|
||||||
append(builder, "\x1b[1;37m");
|
append(builder, "\x1b[1;37m");
|
||||||
if path.count > 0 {
|
if path.count > 0 {
|
||||||
print_to_builder(builder, "%:", path);
|
print_to_builder(builder, "%:", path);
|
||||||
@@ -140,7 +140,7 @@ report_message :: (builder : *String_Builder, path : string, message : string, s
|
|||||||
if report_source_location {
|
if report_source_location {
|
||||||
for location : source_locations {
|
for location : source_locations {
|
||||||
append(builder, "\t");
|
append(builder, "\t");
|
||||||
print_from_source_location(builder, location);
|
print_from_source_location(ctx, builder, location);
|
||||||
append(builder, "\n\t");
|
append(builder, "\n\t");
|
||||||
begin := location.begin;
|
begin := location.begin;
|
||||||
|
|
||||||
|
|||||||
765
Ink.jai
Normal file
765
Ink.jai
Normal file
@@ -0,0 +1,765 @@
|
|||||||
|
/////////////////////////////////////
|
||||||
|
/*~ nbr: General improvements
|
||||||
|
- [x] Print out all failed tests in a list at the end
|
||||||
|
- [x] Use new compiler API with Compile_Result and Compiled_File instead
|
||||||
|
- [ ] Use unix (posix? bash? ascii?) color codes for errors
|
||||||
|
- [ ] Print golden file as green and new output as red
|
||||||
|
- [ ] Rename to Ink.jai
|
||||||
|
- [ ] Add -test option. -test does the same as test.exe used to do
|
||||||
|
- [ ] Add -fuzz option to run fuzzer (add args later)
|
||||||
|
- [ ] Add -output option to output the compiled file. Issue with this is the generated data can't be output like that. Would require serialization.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import "Basic";
|
||||||
|
#import "File";
|
||||||
|
#import "String";
|
||||||
|
#import "File_Utilities";
|
||||||
|
#import "Print_Color";
|
||||||
|
|
||||||
|
#load "module.jai";
|
||||||
|
|
||||||
|
GOLDEN_EXTENSION :: "golden";
|
||||||
|
LEXER_FOLDER :: "lex";
|
||||||
|
PARSER_FOLDER :: "parse";
|
||||||
|
CODEGEN_FOLDER :: "codegen";
|
||||||
|
COMPILED_FOLDER :: "compiled";
|
||||||
|
CHECK_FOLDER :: "check";
|
||||||
|
TESTS_FOLDER :: "test";
|
||||||
|
|
||||||
|
SHADER_EXTENSION :: "ink";
|
||||||
|
SUITE_EXTENSION :: "suite";
|
||||||
|
|
||||||
|
Stage_Flags :: enum_flags u16 {
|
||||||
|
Lexer :: 0x1;
|
||||||
|
Parser :: 0x2;
|
||||||
|
Check :: 0x4;
|
||||||
|
Codegen :: 0x8;
|
||||||
|
Compile :: 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
Output_Type :: enum_flags u16 {
|
||||||
|
Golden :: 0x1;
|
||||||
|
StdOut :: 0x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result_Type :: enum {
|
||||||
|
File_Read_Failed;
|
||||||
|
Golden_File_Not_Found;
|
||||||
|
StdOut;
|
||||||
|
Golden_Output;
|
||||||
|
Passed;
|
||||||
|
Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result :: struct {
|
||||||
|
type : Result_Type;
|
||||||
|
path : string;
|
||||||
|
stage : Stage_Flags;
|
||||||
|
|
||||||
|
golden_path : string;
|
||||||
|
info_text : string;
|
||||||
|
}
|
||||||
|
|
||||||
|
Test_Case :: struct {
|
||||||
|
path : string;
|
||||||
|
stage_flags : Stage_Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
Test_Suite :: struct {
|
||||||
|
name : string;
|
||||||
|
test_cases : [..]Test_Case;
|
||||||
|
|
||||||
|
results : [..]Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_golden_path :: (file_path : string, stage : Stage_Flags) -> string {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
path := parse_path(file_path,, sc.allocator);
|
||||||
|
file_without_extension := split(path.words[path.words.count - 1], ".",, sc.allocator);
|
||||||
|
|
||||||
|
builder : String_Builder;
|
||||||
|
builder.allocator = temp;
|
||||||
|
|
||||||
|
final_path_length := file_path.count - SHADER_EXTENSION.count + GOLDEN_EXTENSION.count + 1; // +1 for dot
|
||||||
|
|
||||||
|
path.words.count -= 1;
|
||||||
|
path.words.allocator = sc.allocator;
|
||||||
|
|
||||||
|
if stage == {
|
||||||
|
case .Lexer; {
|
||||||
|
dir := tprint("%/%", TESTS_FOLDER, LEXER_FOLDER);
|
||||||
|
make_directory_if_it_does_not_exist(dir);
|
||||||
|
array_add(*path.words, LEXER_FOLDER);
|
||||||
|
}
|
||||||
|
case .Parser; {
|
||||||
|
dir := tprint("%/%", TESTS_FOLDER, PARSER_FOLDER);
|
||||||
|
make_directory_if_it_does_not_exist(dir);
|
||||||
|
array_add(*path.words, PARSER_FOLDER);
|
||||||
|
}
|
||||||
|
case .Check; {
|
||||||
|
dir := tprint("%/%", TESTS_FOLDER, CHECK_FOLDER);
|
||||||
|
make_directory_if_it_does_not_exist(dir);
|
||||||
|
array_add(*path.words, CHECK_FOLDER);
|
||||||
|
}
|
||||||
|
case .Codegen; {
|
||||||
|
dir := tprint("%/%", TESTS_FOLDER, CODEGEN_FOLDER);
|
||||||
|
make_directory_if_it_does_not_exist(dir);
|
||||||
|
array_add(*path.words, CODEGEN_FOLDER);
|
||||||
|
}
|
||||||
|
case .Compile; {
|
||||||
|
dir := tprint("%/%", TESTS_FOLDER, COMPILED_FOLDER);
|
||||||
|
make_directory_if_it_does_not_exist(dir);
|
||||||
|
array_add(*path.words, COMPILED_FOLDER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_string_builder(*builder, file_without_extension.count + GOLDEN_EXTENSION.count + 1);
|
||||||
|
builder.allocator = sc.allocator;
|
||||||
|
append(*builder, file_without_extension[0]);
|
||||||
|
append(*builder, ".");
|
||||||
|
append(*builder, GOLDEN_EXTENSION);
|
||||||
|
golden_path := builder_to_string(*builder,, sc.allocator);
|
||||||
|
array_add(*path.words, golden_path);
|
||||||
|
|
||||||
|
final_path := path_to_string(path);
|
||||||
|
|
||||||
|
return final_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_golden_comparison :: (golden_path : string, comparison_text : string, result : *Result, output_type : Output_Type) {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
if output_type & .Golden {
|
||||||
|
// Output the comparison file
|
||||||
|
write_entire_file(golden_path, comparison_text);
|
||||||
|
result.golden_path = copy_string(golden_path);
|
||||||
|
result.type = .Golden_Output;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Do the comparison
|
||||||
|
if !file_exists(golden_path) {
|
||||||
|
result.info_text = tprint("Golden file % does not exist. Please run with -output-as-golden at least once.\n", golden_path);
|
||||||
|
result.type = .Golden_File_Not_Found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_text, ok := read_entire_file(golden_path,, sc.allocator);
|
||||||
|
if !ok {
|
||||||
|
result.info_text = tprint("Unable to open golden file %\n", golden_path);
|
||||||
|
result.type = .Golden_File_Not_Found;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp := replace(comparison_text, "\r\n", "\n",, sc.allocator);
|
||||||
|
gold := replace(golden_text, "\r\n", "\n",, sc.allocator);
|
||||||
|
ok = compare(comp, gold) == 0;
|
||||||
|
if !ok {
|
||||||
|
result.type = .Failed;
|
||||||
|
result.info_text = tprint("Golden file:\n%\n===============\n%", gold, comp);
|
||||||
|
} else {
|
||||||
|
result.type = .Passed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_codegen_test :: (file_path : string, ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = file_path;
|
||||||
|
|
||||||
|
lex(ctx, context.allocator);
|
||||||
|
parse(ctx, context.allocator);
|
||||||
|
check(ctx, context.allocator);
|
||||||
|
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result = run_codegen_test(ctx, output_type);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_codegen_test :: (ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = ctx.file.path;
|
||||||
|
result_text : string;
|
||||||
|
|
||||||
|
codegen(ctx, context.allocator);
|
||||||
|
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
result_text = report_messages(ctx, ctx.messages);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result_text = ctx.codegen_result_text;
|
||||||
|
|
||||||
|
if output_type & .StdOut {
|
||||||
|
result.info_text = result_text;
|
||||||
|
result.type = .StdOut;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_path := get_golden_path(ctx.file.path, .Codegen);
|
||||||
|
do_golden_comparison(golden_path, result_text, *result, output_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_compile_test :: (path : string, output_type : Output_Type = 0) -> Result, Compiler_Context {
|
||||||
|
ctx : Compiler_Context;
|
||||||
|
result : Result;
|
||||||
|
result.path = path;
|
||||||
|
compile_file(*ctx, path, context.allocator);
|
||||||
|
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
result.info_text = tprint("Failed compiling: %\n", path);
|
||||||
|
} else {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
sb : String_Builder;
|
||||||
|
init_string_builder(*sb,, sc.allocator);
|
||||||
|
if ctx.vertex_entry_point.name.count > 0 {
|
||||||
|
print_to_builder(*sb, "[vertex entry point] - %\n", ctx.vertex_entry_point.name);
|
||||||
|
}
|
||||||
|
if ctx.pixel_entry_point.name.count > 0 {
|
||||||
|
print_to_builder(*sb, "[pixel entry point] - %\n", ctx.pixel_entry_point.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.properties.fields.count > 0{
|
||||||
|
|
||||||
|
props := ctx.properties;
|
||||||
|
append(*sb, "[");
|
||||||
|
if ctx.property_name.count > 0 {
|
||||||
|
print_to_builder(*sb, "% :: ", ctx.property_name);
|
||||||
|
}
|
||||||
|
print_to_builder(*sb, "properties] - %\n", props.buffer_index);
|
||||||
|
|
||||||
|
indent(*sb, 1);
|
||||||
|
for field : props.fields {
|
||||||
|
append(*sb, "[field] - ");
|
||||||
|
pretty_print_field(*sb, *field.base_field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for cb : ctx.cbuffers {
|
||||||
|
print_to_builder(*sb, "[constant_buffer] - % - %\n", cb.name, cb.buffer_index);
|
||||||
|
|
||||||
|
indent(*sb, 1);
|
||||||
|
for field : cb.fields {
|
||||||
|
append(*sb, "[field] - ");
|
||||||
|
pretty_print_field(*sb, *field.base_field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.info_text = builder_to_string(*sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_type & .StdOut {
|
||||||
|
result.type = .StdOut;
|
||||||
|
return result, ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_path := get_golden_path(ctx.file.path, .Compile);
|
||||||
|
do_golden_comparison(golden_path, result.info_text, *result, output_type);
|
||||||
|
|
||||||
|
return result, ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_lexer_test :: (file_path : string, ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = file_path;
|
||||||
|
result.stage = .Lexer;
|
||||||
|
|
||||||
|
result_text : string;
|
||||||
|
|
||||||
|
lex(ctx);
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
result_text = report_messages(ctx, ctx.messages);
|
||||||
|
} else {
|
||||||
|
result_text = pretty_print_tokens(ctx.tokens, context.allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_type & .StdOut {
|
||||||
|
result.info_text = result_text;
|
||||||
|
result.type = .StdOut;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_path := get_golden_path(file_path, .Lexer);
|
||||||
|
do_golden_comparison(golden_path, result_text, *result, output_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_parser_test :: (file_path : string, ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = file_path;
|
||||||
|
|
||||||
|
lex(ctx);
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Passed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = run_parser_test(ctx, output_type);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_parser_test :: (ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
parse(ctx, context.allocator);
|
||||||
|
result : Result;
|
||||||
|
result.path = ctx.file.path;
|
||||||
|
result_text : string;
|
||||||
|
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
result_text = report_messages(ctx, ctx.messages);
|
||||||
|
} else {
|
||||||
|
result_text = pretty_print_ast(ctx.root, context.allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_type & .StdOut {
|
||||||
|
result.info_text = result_text;
|
||||||
|
result.type = .StdOut;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_path := get_golden_path(ctx.file.path, .Parser);
|
||||||
|
do_golden_comparison(golden_path, result_text, *result, output_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_check_test :: (ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = ctx.file.path;
|
||||||
|
result_text : string;
|
||||||
|
|
||||||
|
check(ctx, context.allocator);
|
||||||
|
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
result_text = report_messages(ctx, ctx.messages);
|
||||||
|
} else {
|
||||||
|
result_text = pretty_print_symbol_table(ctx, context.allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
if output_type & .StdOut {
|
||||||
|
result.info_text = result_text;
|
||||||
|
result.type = .StdOut;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
golden_path := get_golden_path(ctx.file.path, .Check);
|
||||||
|
do_golden_comparison(golden_path, result_text, *result, output_type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_check_test :: (file_path : string, ctx : *Compiler_Context, output_type : Output_Type = 0) -> Result {
|
||||||
|
result : Result;
|
||||||
|
result.path = file_path;
|
||||||
|
|
||||||
|
lex(ctx, context.allocator);
|
||||||
|
parse(ctx, context.allocator);
|
||||||
|
if ctx.had_error {
|
||||||
|
result.type = .Failed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = run_check_test(ctx, output_type);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_test_case :: (path : string, stage_flags : Stage_Flags, allocator := context.allocator) -> Test_Case {
|
||||||
|
test_case : Test_Case;
|
||||||
|
test_case.path = copy_string(path,, allocator);
|
||||||
|
replace_chars(test_case.path, "\\", #char "/");
|
||||||
|
test_case.stage_flags = stage_flags;
|
||||||
|
|
||||||
|
return test_case;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test_new :: (file_path : string, stage_flags : Stage_Flags, results : *[..]Result, output_type : Output_Type = 0, allocator := temp) {
|
||||||
|
new_context := context;
|
||||||
|
new_context.allocator = allocator;
|
||||||
|
push_context new_context {
|
||||||
|
ctx : Compiler_Context;
|
||||||
|
|
||||||
|
ctx.file = make_file(*ctx, file_path);
|
||||||
|
|
||||||
|
result : Result;
|
||||||
|
if stage_flags & .Lexer {
|
||||||
|
result = run_lexer_test(file_path, *ctx, output_type);
|
||||||
|
record_result(results, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage_flags & .Parser {
|
||||||
|
if stage_flags & .Lexer && result.type == .Passed || result.type == .Golden_Output {
|
||||||
|
result = run_parser_test(*ctx, output_type);
|
||||||
|
} else {
|
||||||
|
result = run_parser_test(file_path, *ctx, output_type);
|
||||||
|
}
|
||||||
|
record_result(results, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage_flags & .Check {
|
||||||
|
if stage_flags & .Parser && (result.type == .Passed || result.type == .Golden_Output) {
|
||||||
|
result = run_check_test(*ctx, output_type);
|
||||||
|
} else {
|
||||||
|
result = run_check_test(file_path, *ctx, output_type);
|
||||||
|
}
|
||||||
|
record_result(results, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage_flags & .Codegen {
|
||||||
|
if stage_flags & .Check && (result.type == .Passed || result.type == .Golden_Output) {
|
||||||
|
result = run_codegen_test(*ctx, output_type);
|
||||||
|
} else {
|
||||||
|
result = run_codegen_test(file_path, *ctx, output_type);
|
||||||
|
}
|
||||||
|
record_result(results, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if stage_flags & .Compile {
|
||||||
|
result = run_compile_test(file_path, output_type);
|
||||||
|
record_result(results, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test :: (test_case : Test_Case, results : *[..]Result, output_type : Output_Type = 0, allocator := temp) {
|
||||||
|
print("%Running test: %......", cyan(), test_case.path);
|
||||||
|
|
||||||
|
// path 30
|
||||||
|
// len 35
|
||||||
|
// == 5
|
||||||
|
|
||||||
|
|
||||||
|
// path 20
|
||||||
|
// len = 35
|
||||||
|
// == 15
|
||||||
|
|
||||||
|
len := 50;
|
||||||
|
rest := len - test_case.path.count;
|
||||||
|
for i: 0..rest {
|
||||||
|
print(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test_new(test_case.path, test_case.stage_flags, results, output_type, allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
record_result :: (results : *[..]Result, result : Result) {
|
||||||
|
array_add(results, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
run_test_suite :: (using suite : *Test_Suite, output_type : Output_Type = 0) {
|
||||||
|
if suite.name.count > 0 {
|
||||||
|
print("%Running suite: %\n", green(), suite.name);
|
||||||
|
print("%", reset_color());
|
||||||
|
}
|
||||||
|
|
||||||
|
Fail_Data :: struct {
|
||||||
|
path : string;
|
||||||
|
stage : string;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_arena : Allocator = make_arena(Gigabytes(1));
|
||||||
|
|
||||||
|
failed_test_paths : [..]Fail_Data;
|
||||||
|
failed_test_paths.allocator = test_arena;
|
||||||
|
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, test_arena);
|
||||||
|
|
||||||
|
for test_case : test_cases {
|
||||||
|
run_test(test_case, *suite.results, output_type, allocator = test_arena);
|
||||||
|
|
||||||
|
for < suite.results {
|
||||||
|
result := suite.results[it_index];
|
||||||
|
if compare(result.path, test_case.path) == 0 {
|
||||||
|
if result.type == {
|
||||||
|
case .Failed; {
|
||||||
|
array_add(*failed_test_paths, .{ result.path, stage_to_string(result.stage) });
|
||||||
|
}
|
||||||
|
case .File_Read_Failed; {
|
||||||
|
array_add(*failed_test_paths, .{ result.path, "file not found" });
|
||||||
|
}
|
||||||
|
case .Golden_File_Not_Found; {
|
||||||
|
array_add(*failed_test_paths, .{ result.path, tprint("golden file not found for %", stage_to_string(result.stage)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
evaluate_result(result);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print("\n");
|
||||||
|
}
|
||||||
|
append(*builder, "\n");
|
||||||
|
|
||||||
|
if output_type == 0 {
|
||||||
|
if failed_test_paths.count == 0 {
|
||||||
|
green(*builder);
|
||||||
|
print_to_builder(*builder, "All % tests passed!\n", test_cases.count);
|
||||||
|
reset_color(*builder);
|
||||||
|
} else {
|
||||||
|
print_to_builder(*builder, "%/% tests passed\n", test_cases.count - failed_test_paths.count, test_cases.count);
|
||||||
|
red(*builder);
|
||||||
|
|
||||||
|
print_to_builder(*builder, "% failed\n", failed_test_paths.count);
|
||||||
|
for failed_test : failed_test_paths {
|
||||||
|
print_to_builder(*builder, "% failed with error: %\n", failed_test.path, failed_test.stage);
|
||||||
|
}
|
||||||
|
reset_color(*builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("%\n", builder_to_string(*builder,, test_arena));
|
||||||
|
}
|
||||||
|
|
||||||
|
read_suite :: (file_path : string, suite : *Test_Suite, allocator := temp) -> bool {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
bytes, ok := read_entire_file(file_path,, sc.allocator);
|
||||||
|
if !ok {
|
||||||
|
log_error("Unable to read suite file %\n", file_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path := parse_path(file_path,, sc.allocator);
|
||||||
|
file_without_extension := split(path.words[path.words.count - 1], ".",, sc.allocator);
|
||||||
|
suite.name = copy_string(file_without_extension[0],, allocator);
|
||||||
|
split_lines := split(bytes, "\n",, sc.allocator);
|
||||||
|
|
||||||
|
for split_line : split_lines {
|
||||||
|
if split_line.count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if split_line[0] == #char "#" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
line := split(split_line, " ",, sc.allocator);
|
||||||
|
if line[0].count == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line[0].data[0] == #char "#" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if line.count == 1 {
|
||||||
|
line = split(split_line, "\t",, sc.allocator);
|
||||||
|
if line.count == 1 {
|
||||||
|
log_error("Invalid line - % - \n", it_index + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test_case_path := line[0];
|
||||||
|
stage_flags : Stage_Flags;
|
||||||
|
|
||||||
|
for i: 0..line.count - 1 {
|
||||||
|
trimmed := trim(line[i]);
|
||||||
|
if equal(trimmed, "lex") {
|
||||||
|
stage_flags |= .Lexer;
|
||||||
|
} else if equal(trimmed, "parse") {
|
||||||
|
stage_flags |= .Parser;
|
||||||
|
} else if equal(trimmed, "check") {
|
||||||
|
stage_flags |= .Check;
|
||||||
|
} else if equal(trimmed, "codegen") {
|
||||||
|
stage_flags |= .Codegen;
|
||||||
|
} else if equal(trimmed, "compile") {
|
||||||
|
stage_flags |= .Compile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test_case := make_test_case(test_case_path, stage_flags, allocator);
|
||||||
|
array_add(*suite.test_cases, test_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_test :: () {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
stage_to_string :: (stage : Stage_Flags) -> string {
|
||||||
|
if #complete stage == {
|
||||||
|
case .Lexer; return "lexing";
|
||||||
|
case .Parser; return "parsing";
|
||||||
|
case .Check; return "checking";
|
||||||
|
case .Codegen; return "codegen";
|
||||||
|
case .Compile; return "compiled";
|
||||||
|
case; return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_result :: (result : Result) {
|
||||||
|
stage : string = stage_to_string(result.stage);
|
||||||
|
|
||||||
|
if #complete result.type == {
|
||||||
|
case .File_Read_Failed; {
|
||||||
|
print(" %", red());
|
||||||
|
print("failed with File_Read_Failed\n");
|
||||||
|
}
|
||||||
|
case .Golden_File_Not_Found; {
|
||||||
|
print(" %", red());
|
||||||
|
print("failed with Golden File Not Found for stage %\n", stage);
|
||||||
|
}
|
||||||
|
case .StdOut; {
|
||||||
|
}
|
||||||
|
case .Golden_Output; {
|
||||||
|
print(" %", yellow());
|
||||||
|
print("output new golden file at %\n", result.golden_path);
|
||||||
|
}
|
||||||
|
case .Passed; {
|
||||||
|
print(" %", green());
|
||||||
|
print("passed %\n", stage);
|
||||||
|
}
|
||||||
|
case .Failed; {
|
||||||
|
print(" %", red());
|
||||||
|
print("failed %\n", stage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.info_text.count > 0 {
|
||||||
|
print("%", cyan());
|
||||||
|
print("--- Info text ---\n");
|
||||||
|
print("%", yellow());
|
||||||
|
print("%\n", result.info_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("%", reset_color());
|
||||||
|
}
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
args := get_command_line_arguments();
|
||||||
|
|
||||||
|
init_context_allocators();
|
||||||
|
|
||||||
|
local_temp := make_arena(Megabytes(128));
|
||||||
|
|
||||||
|
suites : [..]Test_Suite;
|
||||||
|
suites.allocator = local_temp;
|
||||||
|
output_type : Output_Type = 0;
|
||||||
|
|
||||||
|
Argument_Parse_State :: enum {
|
||||||
|
None;
|
||||||
|
Compile;
|
||||||
|
Run_Suite;
|
||||||
|
Run_Test;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg_parse_state : Argument_Parse_State;
|
||||||
|
current_suite : *Test_Suite;
|
||||||
|
|
||||||
|
path : string;
|
||||||
|
|
||||||
|
for i: 1..args.count - 1 {
|
||||||
|
arg := args[i];
|
||||||
|
if arg == "-output-as-golden" {
|
||||||
|
output_type |= .Golden;
|
||||||
|
continue;
|
||||||
|
} else if arg == "-output" {
|
||||||
|
output_type |= .StdOut;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if arg_parse_state == {
|
||||||
|
case .Run_Suite; {
|
||||||
|
if arg == "-output-as-golden" {
|
||||||
|
output_type |= .Golden;
|
||||||
|
} else if arg == "-output" {
|
||||||
|
output_type |= .StdOut;
|
||||||
|
} else {
|
||||||
|
print("%Unknown argument % %\n", red(), arg, reset_color());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .Run_Test; {
|
||||||
|
cases := current_suite.test_cases.count;
|
||||||
|
if arg == "-lex" {
|
||||||
|
current_suite.test_cases[cases - 1].stage_flags |= .Lexer;
|
||||||
|
} else if arg == "-parse" {
|
||||||
|
current_suite.test_cases[cases - 1].stage_flags |= .Parser;
|
||||||
|
} else if arg == "-check" {
|
||||||
|
current_suite.test_cases[cases - 1].stage_flags |= .Check;
|
||||||
|
} else if arg == "-codegen" {
|
||||||
|
current_suite.test_cases[cases - 1].stage_flags |= .Codegen;
|
||||||
|
} else if arg == "-compile" {
|
||||||
|
current_suite.test_cases[cases - 1].stage_flags |= .Compile;
|
||||||
|
} else if contains(arg, ".") {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
path_split := split(arg, "\\",, sc.allocator);
|
||||||
|
split_path := split(path_split[path_split.count - 1], ".",, sc.allocator);
|
||||||
|
extension := split_path[1];
|
||||||
|
if extension == SHADER_EXTENSION {
|
||||||
|
path := copy_string(arg,, local_temp);
|
||||||
|
test_case := make_test_case(path, 0, local_temp);
|
||||||
|
array_add(*current_suite.test_cases, test_case);
|
||||||
|
} else {
|
||||||
|
print("%Invalid file as argument % %\n", red(), arg, reset_color());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("%Unknown argument % %\n", red(), arg, reset_color());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .None; {
|
||||||
|
if contains(arg, ".") {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
path_split := split(arg, "\\",, sc.allocator);
|
||||||
|
split_path := split(path_split[path_split.count - 1], ".",, sc.allocator);
|
||||||
|
extension := split_path[1];
|
||||||
|
|
||||||
|
if extension == SHADER_EXTENSION {
|
||||||
|
if arg_parse_state == .Run_Suite {
|
||||||
|
log_error("Unable to run a test while already running suite.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !current_suite {
|
||||||
|
suite : Test_Suite;
|
||||||
|
suite.results.allocator = local_temp;
|
||||||
|
suite.test_cases.allocator = local_temp;
|
||||||
|
array_add(*suites, suite);
|
||||||
|
current_suite = *suites[0];
|
||||||
|
}
|
||||||
|
arg_parse_state = .Run_Test;
|
||||||
|
path := copy_string(arg,, local_temp);
|
||||||
|
test_case := make_test_case(path, 0, local_temp);
|
||||||
|
array_add(*current_suite.test_cases, test_case);
|
||||||
|
} else if extension == SUITE_EXTENSION {
|
||||||
|
if arg_parse_state == .Run_Test {
|
||||||
|
log_error("Unable to run a suite while already running test.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
arg_parse_state = .Run_Suite;
|
||||||
|
path := copy_string(arg);
|
||||||
|
|
||||||
|
suite : Test_Suite;
|
||||||
|
suite.results.allocator = local_temp;
|
||||||
|
suite.test_cases.allocator = local_temp;
|
||||||
|
read_suite(path, *suite, local_temp);
|
||||||
|
array_add(*suites, suite);
|
||||||
|
current_suite = *suites[0];
|
||||||
|
} else {
|
||||||
|
print("%Invalid file as argument % %\n", red(), arg, reset_color());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for suite : suites {
|
||||||
|
run_test_suite(*suite, output_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(local_temp);
|
||||||
|
}
|
||||||
170
Lexing.jai
170
Lexing.jai
@@ -5,18 +5,13 @@ Lexer :: struct {
|
|||||||
current_line : int;
|
current_line : int;
|
||||||
current_column : int;
|
current_column : int;
|
||||||
|
|
||||||
result : Lexing_Result;
|
ctx : *Compiler_Context;
|
||||||
|
|
||||||
path : string;
|
path : string;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lexing_Result :: struct {
|
|
||||||
tokens : [..]Token;
|
|
||||||
had_error : bool;
|
|
||||||
messages : [..]Compiler_Message;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token_Kind :: enum {
|
Token_Kind :: enum {
|
||||||
|
TOKEN_INVALID :: 0;
|
||||||
TOKEN_FLOATLITERAL;
|
TOKEN_FLOATLITERAL;
|
||||||
TOKEN_INTLITERAL;
|
TOKEN_INTLITERAL;
|
||||||
|
|
||||||
@@ -54,6 +49,7 @@ Token_Kind :: enum {
|
|||||||
TOKEN_SEMICOLON;
|
TOKEN_SEMICOLON;
|
||||||
TOKEN_COMMA;
|
TOKEN_COMMA;
|
||||||
TOKEN_DOT;
|
TOKEN_DOT;
|
||||||
|
TOKEN_DOTDOT;
|
||||||
|
|
||||||
TOKEN_IDENTIFIER;
|
TOKEN_IDENTIFIER;
|
||||||
|
|
||||||
@@ -100,6 +96,7 @@ Token_Kind :: enum {
|
|||||||
TOKEN_RETURN;
|
TOKEN_RETURN;
|
||||||
TOKEN_REGISTER;
|
TOKEN_REGISTER;
|
||||||
|
|
||||||
|
TOKEN_STRING;
|
||||||
TOKEN_STRUCT;
|
TOKEN_STRUCT;
|
||||||
TOKEN_SWITCH;
|
TOKEN_SWITCH;
|
||||||
|
|
||||||
@@ -133,10 +130,12 @@ Token :: struct {
|
|||||||
// This could all be derived on demand
|
// This could all be derived on demand
|
||||||
line : int;
|
line : int;
|
||||||
length : int;
|
length : int;
|
||||||
column : int;
|
column : int;
|
||||||
index : int;
|
index : int;
|
||||||
|
|
||||||
error : string;
|
error : string;
|
||||||
|
|
||||||
|
builtin : bool; // @Incomplete: This is kind of a bad idea, but let's just do it for now...
|
||||||
}
|
}
|
||||||
|
|
||||||
Source_Range :: struct {
|
Source_Range :: struct {
|
||||||
@@ -264,12 +263,32 @@ identifier_kind :: (using lexer : *Lexer) -> Token_Kind {
|
|||||||
error_token :: (lexer : *Lexer, message : string) -> *Token {
|
error_token :: (lexer : *Lexer, message : string) -> *Token {
|
||||||
token : *Token = new_token(lexer, .TOKEN_ERROR);
|
token : *Token = new_token(lexer, .TOKEN_ERROR);
|
||||||
|
|
||||||
lexer.result.had_error = true;
|
lexer.ctx.had_error = true;
|
||||||
token.error = copy_string(message);
|
token.error = copy_string(message);
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unable_to_open_file :: (state : *Parse_State, path : string, token : Token) {
|
||||||
|
// builder : String_Builder;
|
||||||
|
// init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
// print_to_builder(*builder, "Unable to open file '%' for reading\n\n", path);
|
||||||
|
|
||||||
|
// location := generate_source_location_from_token(state, token);
|
||||||
|
|
||||||
|
// indent(*builder, 1);
|
||||||
|
// cyan(*builder);
|
||||||
|
// print_to_builder(*builder, "%\n", print_from_source_location(location));
|
||||||
|
// indent(*builder, 1);
|
||||||
|
|
||||||
|
// loc := location.begin;
|
||||||
|
// print_token_pointer(*builder, loc);
|
||||||
|
|
||||||
|
// final_message := builder_to_string(*builder);
|
||||||
|
// record_error(state, token, final_message, false);
|
||||||
|
// }
|
||||||
|
|
||||||
record_error :: (lexer : *Lexer, message : string) {
|
record_error :: (lexer : *Lexer, message : string) {
|
||||||
error : Compiler_Message;
|
error : Compiler_Message;
|
||||||
error.message_kind = .Error;
|
error.message_kind = .Error;
|
||||||
@@ -291,8 +310,8 @@ record_error :: (lexer : *Lexer, message : string) {
|
|||||||
|
|
||||||
array_add(*error.source_locations, source_location);
|
array_add(*error.source_locations, source_location);
|
||||||
|
|
||||||
lexer.result.had_error = true;
|
lexer.ctx.had_error = true;
|
||||||
array_add(*lexer.result.messages, error);
|
array_add(*lexer.ctx.messages, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
make_int :: (lexer : *Lexer) -> *Token {
|
make_int :: (lexer : *Lexer) -> *Token {
|
||||||
@@ -322,10 +341,6 @@ make_float :: (lexer : *Lexer) -> *Token {
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_string :: () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
new_token :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
new_token :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
||||||
length := lexer.cursor - lexer.start;
|
length := lexer.cursor - lexer.start;
|
||||||
token : Token;
|
token : Token;
|
||||||
@@ -342,13 +357,54 @@ new_token :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
|||||||
}
|
}
|
||||||
lexer.current_column += length;
|
lexer.current_column += length;
|
||||||
|
|
||||||
array_add(*lexer.result.tokens, token);
|
array_add(*lexer.ctx.tokens, token);
|
||||||
return *lexer.result.tokens[lexer.result.tokens.count - 1];
|
return *lexer.ctx.tokens[lexer.ctx.tokens.count - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
make_directive :: (lexer : *Lexer) -> *Token {
|
make_directive :: (lexer : *Lexer) -> *Token {
|
||||||
lexer.start += 1;
|
lexer.start += 1;
|
||||||
return make_identifier(lexer, .TOKEN_DIRECTIVE);
|
ident := make_identifier(lexer, .TOKEN_DIRECTIVE);
|
||||||
|
if ident.ident_value == "load" {
|
||||||
|
path_tok := scan_next_token(lexer);
|
||||||
|
path := path_tok.string_value;
|
||||||
|
ctx : Compiler_Context;
|
||||||
|
ctx.environment = lexer.ctx.environment;
|
||||||
|
|
||||||
|
ctx.file = make_file(*ctx, path);
|
||||||
|
|
||||||
|
if ctx.file.source.count == 0 {
|
||||||
|
// unable_to_open_file(lexer, path, path_tok);
|
||||||
|
record_error(lexer, tprint("Unable to open file '%' for reading\n", path));
|
||||||
|
return error_token(lexer, tprint("Unable to open file '%' for reading\n", path));
|
||||||
|
}
|
||||||
|
|
||||||
|
lex(*ctx);
|
||||||
|
|
||||||
|
ctx.tokens.count -= 1; // @Note: remote TOKEN_EOF
|
||||||
|
lexer.ctx.tokens.count -= 2;
|
||||||
|
array_resize(*lexer.ctx.tokens, lexer.ctx.tokens.count + ctx.tokens.count);
|
||||||
|
|
||||||
|
for tok : ctx.tokens {
|
||||||
|
lexer.ctx.tokens[it_index] = tok;
|
||||||
|
}
|
||||||
|
return scan_next_token(lexer);
|
||||||
|
} else if ident.ident_value == "add_define" {
|
||||||
|
new_define := scan_next_token(lexer);
|
||||||
|
add_define(*lexer.ctx.environment, new_define.ident_value);
|
||||||
|
lexer.ctx.tokens.count -= 2;
|
||||||
|
return scan_next_token(lexer);
|
||||||
|
}
|
||||||
|
return ident;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_string :: (lexer : *Lexer) -> *Token {
|
||||||
|
token : *Token = new_token(lexer, .TOKEN_STRING);
|
||||||
|
|
||||||
|
name : string = .{ count = token.length - 2,
|
||||||
|
data = *lexer.input.data[lexer.start + 1] };
|
||||||
|
token.string_value = name;
|
||||||
|
|
||||||
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
make_identifier :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
make_identifier :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
||||||
@@ -367,6 +423,7 @@ make_token :: (lexer : *Lexer, token_kind : Token_Kind) -> *Token {
|
|||||||
|
|
||||||
skip_whitespace :: (lexer : *Lexer) {
|
skip_whitespace :: (lexer : *Lexer) {
|
||||||
while true {
|
while true {
|
||||||
|
if is_at_end(lexer) return;
|
||||||
c := peek_char(lexer);
|
c := peek_char(lexer);
|
||||||
|
|
||||||
if c == {
|
if c == {
|
||||||
@@ -421,6 +478,17 @@ scan_next_token :: (lexer : *Lexer) -> *Token {
|
|||||||
if is_digit(c) return number(lexer);
|
if is_digit(c) return number(lexer);
|
||||||
|
|
||||||
if c == {
|
if c == {
|
||||||
|
case #char "\""; {
|
||||||
|
c = advance(lexer);
|
||||||
|
// lexer.start = lexer.cursor;
|
||||||
|
while c != #char "\"" {
|
||||||
|
c = advance(lexer);
|
||||||
|
}
|
||||||
|
// lexer.cursor -= 1;
|
||||||
|
tok := make_string(lexer);
|
||||||
|
// advance(lexer);
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
case #char "+"; {
|
case #char "+"; {
|
||||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_PLUSEQUALS);
|
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_PLUSEQUALS);
|
||||||
return make_token(lexer, .TOKEN_PLUS);
|
return make_token(lexer, .TOKEN_PLUS);
|
||||||
@@ -490,7 +558,10 @@ scan_next_token :: (lexer : *Lexer) -> *Token {
|
|||||||
}
|
}
|
||||||
case #char ";"; return make_token(lexer, .TOKEN_SEMICOLON);
|
case #char ";"; return make_token(lexer, .TOKEN_SEMICOLON);
|
||||||
case #char ","; return make_token(lexer, .TOKEN_COMMA);
|
case #char ","; return make_token(lexer, .TOKEN_COMMA);
|
||||||
case #char "."; return make_token(lexer, .TOKEN_DOT);
|
case #char "."; {
|
||||||
|
if match_character(lexer, #char ".") return make_token(lexer, .TOKEN_DOTDOT);
|
||||||
|
return make_token(lexer, .TOKEN_DOT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s : string = .{ count = 1, data = *c };
|
s : string = .{ count = 1, data = *c };
|
||||||
@@ -499,41 +570,35 @@ scan_next_token :: (lexer : *Lexer) -> *Token {
|
|||||||
// return error_token(lexer, tprint("Invalid token: %", s));
|
// return error_token(lexer, tprint("Invalid token: %", s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lex :: (ctx : *Compiler_Context, allocator := temp) {
|
||||||
|
if ctx.had_error {
|
||||||
lex :: (result : *Compile_Result) {
|
|
||||||
if result.had_error {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for file : result.files {
|
new_context := context;
|
||||||
|
new_context.allocator = allocator;
|
||||||
|
push_context new_context {
|
||||||
|
init_context_allocators();
|
||||||
|
defer clear_context_allocators();
|
||||||
|
|
||||||
lexer : Lexer;
|
lexer : Lexer;
|
||||||
init_lexer_from_string(*lexer, file.file.source);
|
lexer.ctx = ctx;
|
||||||
|
array_reserve(*lexer.ctx.tokens, 1024);
|
||||||
|
|
||||||
|
init_lexer_from_string(*lexer, ctx.file.source);
|
||||||
|
lexer.path = ctx.file.path;
|
||||||
token : *Token = scan_next_token(*lexer);
|
token : *Token = scan_next_token(*lexer);
|
||||||
while token && token.kind != .TOKEN_EOF {
|
while token && token.kind != .TOKEN_EOF {
|
||||||
token = scan_next_token(*lexer);
|
token = scan_next_token(*lexer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Incomplete(nb): Temporary until we figure out a good way of passing this stuff around
|
|
||||||
copy_messages(lexer.result.messages, *result.messages);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lex :: (lexer : *Lexer, allocator : Allocator = context.allocator) -> Lexing_Result {
|
|
||||||
lexer.result.tokens.allocator = allocator;
|
|
||||||
token : *Token = scan_next_token(lexer);
|
|
||||||
while token && token.kind != .TOKEN_EOF {
|
|
||||||
token = scan_next_token(lexer);
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexer.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
init_lexer_from_string :: (lexer : *Lexer, input : string) {
|
init_lexer_from_string :: (lexer : *Lexer, input : string) {
|
||||||
ok := read_input_from_string(lexer, input);
|
ok := read_input_from_string(lexer, input);
|
||||||
if !ok {
|
if !ok {
|
||||||
record_error(lexer, "Unable to initialize from string\n");
|
record_error(lexer, "Unable to initialize from string\n");
|
||||||
lexer.result.had_error = true;
|
lexer.ctx.had_error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,7 +606,7 @@ init_lexer_from_file :: (lexer : *Lexer, file_path : string) {
|
|||||||
ok := read_input_from_file(lexer, file_path);
|
ok := read_input_from_file(lexer, file_path);
|
||||||
if !ok {
|
if !ok {
|
||||||
record_error(lexer, tprint("Unable to read file: %\n", file_path));
|
record_error(lexer, tprint("Unable to read file: %\n", file_path));
|
||||||
lexer.result.had_error = true;
|
lexer.ctx.had_error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,10 +745,25 @@ print_token_pointer :: (builder : *String_Builder, token : Token) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_from_source_location :: (builder : *String_Builder, source_location : Source_Range, indentation : int = 0) {
|
print_from_source_location :: (ctx : *Compiler_Context, builder : *String_Builder, source_location : Source_Range, indentation : int = 0) {
|
||||||
current := source_location.begin;
|
current := source_location.begin;
|
||||||
begin := source_location.begin;
|
begin := source_location.begin;
|
||||||
end := source_location.end;
|
end := source_location.end;
|
||||||
|
|
||||||
|
if begin.builtin {
|
||||||
|
for i : begin.index..end.index - 1 {
|
||||||
|
tok := ctx.tokens[i];
|
||||||
|
text : string;
|
||||||
|
text.data = tok.source;
|
||||||
|
text.count = tok.length;
|
||||||
|
print_to_builder(builder, "%", text);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
begin_pos := 0;
|
begin_pos := 0;
|
||||||
token_string : string;
|
token_string : string;
|
||||||
count := end.index - begin.index + end.length;
|
count := end.index - begin.index + end.length;
|
||||||
@@ -710,10 +790,12 @@ print_from_source_location :: (builder : *String_Builder, source_location : Sour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print_from_source_location :: (source_location : Source_Range, allocator := context.allocator, indentation : int = 0) -> string {
|
print_from_source_location :: (ctx : *Compiler_Context, source_location : Source_Range, allocator := context.allocator, indentation : int = 0) -> string {
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
builder : String_Builder;
|
builder : String_Builder;
|
||||||
init_string_builder(*builder,, allocator);
|
init_string_builder(*builder,, sc.allocator);
|
||||||
print_from_source_location(*builder, source_location);
|
print_from_source_location(ctx, *builder, source_location,, sc.allocator);
|
||||||
return builder_to_string(*builder,, allocator);
|
return builder_to_string(*builder,, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
679
Parsing.jai
679
Parsing.jai
@@ -1,5 +1,7 @@
|
|||||||
#import "Flat_Pool";
|
#import "Flat_Pool";
|
||||||
|
|
||||||
|
// #load "qpwodkqopwkd.jai";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
* if parsing
|
* if parsing
|
||||||
@@ -11,34 +13,14 @@
|
|||||||
Parse_State :: struct {
|
Parse_State :: struct {
|
||||||
current : *Token;
|
current : *Token;
|
||||||
previous : *Token;
|
previous : *Token;
|
||||||
tokens : [..]Token;
|
|
||||||
|
|
||||||
current_token_index : int;
|
current_token_index : int;
|
||||||
|
|
||||||
node_allocator : Allocator;
|
ctx : *Compiler_Context;
|
||||||
node_arena : Arena;
|
|
||||||
|
|
||||||
child_allocator : Allocator;
|
|
||||||
child_arena : Arena;
|
|
||||||
|
|
||||||
had_error : bool;
|
|
||||||
|
|
||||||
path : string;
|
|
||||||
|
|
||||||
result : Parse_Result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
//@nb - Result and error handling
|
//@nb - Result and error handling
|
||||||
Parse_Result :: struct {
|
|
||||||
root : *AST_Node;
|
|
||||||
nodes : [..]AST_Node;
|
|
||||||
|
|
||||||
had_error : bool;
|
|
||||||
|
|
||||||
messages : [..]Compiler_Message;
|
|
||||||
}
|
|
||||||
|
|
||||||
Parse_Error_Kind :: enum {
|
Parse_Error_Kind :: enum {
|
||||||
Parse_Error_Type_Missing;
|
Parse_Error_Type_Missing;
|
||||||
Parse_Error_Expected_Expression;
|
Parse_Error_Expected_Expression;
|
||||||
@@ -101,6 +83,11 @@ parse_rules :: #run -> [(cast(int)Token_Kind.TOKEN_ERROR) + 1]Parse_Rule {
|
|||||||
rules[Token_Kind.TOKEN_STAR] = .{null, binary, .PREC_FACTOR};
|
rules[Token_Kind.TOKEN_STAR] = .{null, binary, .PREC_FACTOR};
|
||||||
rules[Token_Kind.TOKEN_ISNOTEQUAL] = .{null, binary, .PREC_COMPARISON};
|
rules[Token_Kind.TOKEN_ISNOTEQUAL] = .{null, binary, .PREC_COMPARISON};
|
||||||
rules[Token_Kind.TOKEN_ASSIGN] = .{null, binary, .PREC_COMPARISON};
|
rules[Token_Kind.TOKEN_ASSIGN] = .{null, binary, .PREC_COMPARISON};
|
||||||
|
rules[Token_Kind.TOKEN_MINUSEQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
|
rules[Token_Kind.TOKEN_PLUSEQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
|
rules[Token_Kind.TOKEN_DIVEQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
|
rules[Token_Kind.TOKEN_TIMESEQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
|
rules[Token_Kind.TOKEN_MODEQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
rules[Token_Kind.TOKEN_ISEQUAL] = .{null, binary, .PREC_EQUALITY};
|
rules[Token_Kind.TOKEN_ISEQUAL] = .{null, binary, .PREC_EQUALITY};
|
||||||
rules[Token_Kind.TOKEN_GREATER] = .{null, binary, .PREC_COMPARISON};
|
rules[Token_Kind.TOKEN_GREATER] = .{null, binary, .PREC_COMPARISON};
|
||||||
rules[Token_Kind.TOKEN_GREATEREQUALS] = .{null, binary, .PREC_COMPARISON};
|
rules[Token_Kind.TOKEN_GREATEREQUALS] = .{null, binary, .PREC_COMPARISON};
|
||||||
@@ -124,16 +111,6 @@ parse_rules :: #run -> [(cast(int)Token_Kind.TOKEN_ERROR) + 1]Parse_Rule {
|
|||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
init_parse_state :: (parse_state : *Parse_State, tokens : [..]Token, path : string) {
|
|
||||||
parse_state.tokens = tokens;
|
|
||||||
parse_state.path = path;
|
|
||||||
parse_state.node_allocator = make_arena(*parse_state.node_arena);
|
|
||||||
parse_state.child_allocator = make_arena(*parse_state.child_arena);
|
|
||||||
parse_state.result.nodes.allocator = parse_state.node_allocator;
|
|
||||||
array_reserve(*parse_state.result.nodes, 4096);
|
|
||||||
parse_state.current_token_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
//@nb - Error handling functions
|
//@nb - Error handling functions
|
||||||
|
|
||||||
@@ -142,7 +119,7 @@ record_error :: (parse_state : *Parse_State, token : Token, message : string, re
|
|||||||
error : Compiler_Message;
|
error : Compiler_Message;
|
||||||
error.message_kind = .Error;
|
error.message_kind = .Error;
|
||||||
error.message = message;
|
error.message = message;
|
||||||
error.path = parse_state.path;
|
error.path = parse_state.ctx.file.path;
|
||||||
|
|
||||||
source_location : Source_Range;
|
source_location : Source_Range;
|
||||||
source_location.begin = token;
|
source_location.begin = token;
|
||||||
@@ -150,14 +127,17 @@ record_error :: (parse_state : *Parse_State, token : Token, message : string, re
|
|||||||
source_location.begin.source = source_location.begin.source - source_location.begin.column;
|
source_location.begin.source = source_location.begin.source - source_location.begin.column;
|
||||||
source_location.main_token = token;
|
source_location.main_token = token;
|
||||||
|
|
||||||
|
snap := snapshot_state(parse_state);
|
||||||
advance_to_sync_point(parse_state);
|
advance_to_sync_point(parse_state);
|
||||||
error.report_source_location = report_source_location;
|
error.report_source_location = report_source_location;
|
||||||
|
|
||||||
source_location.end = parse_state.current;
|
source_location.end = parse_state.current;
|
||||||
array_add(*error.source_locations, source_location);
|
array_add(*error.source_locations, source_location);
|
||||||
|
|
||||||
parse_state.result.had_error = true;
|
parse_state.ctx.had_error = true;
|
||||||
array_add(*parse_state.result.messages, error);
|
array_add(*parse_state.ctx.messages, error);
|
||||||
|
|
||||||
|
rewind_to_snapshot(parse_state, snap);
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_source_location_from_token :: (state : *Parse_State, token : Token) -> Source_Range {
|
generate_source_location_from_token :: (state : *Parse_State, token : Token) -> Source_Range {
|
||||||
@@ -182,7 +162,101 @@ generate_source_location_from_token :: (state : *Parse_State, token : Token) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
unexpected_token :: (state : *Parse_State, token : Token, message : string) {
|
unexpected_token :: (state : *Parse_State, token : Token, message : string) {
|
||||||
record_error(state, token, message);
|
/*
|
||||||
|
|
||||||
|
*/
|
||||||
|
sc := get_scratch();
|
||||||
|
defer scratch_end(sc);
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, sc.allocator);
|
||||||
|
|
||||||
|
print_to_builder(*builder, "%\n\n", message);
|
||||||
|
|
||||||
|
location : Source_Range;
|
||||||
|
location.begin = token;
|
||||||
|
location.begin.index -= location.begin.column;
|
||||||
|
location.begin.source -= location.begin.column;
|
||||||
|
location.begin.length += location.begin.column;
|
||||||
|
location.begin.column = 0;
|
||||||
|
|
||||||
|
location.main_token = token;
|
||||||
|
location.end = token;
|
||||||
|
|
||||||
|
// advance(state);
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
print_token_pointer(*builder, token);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder,, context.allocator);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
else_if_without_if :: (state : *Parse_State) {
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
append(*builder, "'else if' without 'if'\n");
|
||||||
|
|
||||||
|
token := state.previous;
|
||||||
|
|
||||||
|
location : Source_Range = generate_source_location_from_token(state, token);
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
print_token_pointer(*builder, token);
|
||||||
|
white(*builder);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
else_without_if :: (state : *Parse_State) {
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
append(*builder, "'else' without 'if'\n");
|
||||||
|
|
||||||
|
token := state.previous;
|
||||||
|
|
||||||
|
location : Source_Range = generate_source_location_from_token(state, token);
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
print_token_pointer(*builder, token);
|
||||||
|
white(*builder);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
unable_to_parse_statement :: (state : *Parse_State, token : Token, message : string = "") {
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
print_to_builder(*builder, "Unable to parse statement here. %\n", message);
|
||||||
|
|
||||||
|
location : Source_Range = generate_source_location_from_token(state, token);
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
print_token_pointer(*builder, token);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
expected_expression :: (state : *Parse_State, token : Token, message : string) {
|
expected_expression :: (state : *Parse_State, token : Token, message : string) {
|
||||||
@@ -195,7 +269,7 @@ expected_expression :: (state : *Parse_State, token : Token, message : string) {
|
|||||||
|
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
cyan(*builder);
|
cyan(*builder);
|
||||||
print_to_builder(*builder, "%\n", print_from_source_location(location));
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
print_token_pointer(*builder, token);
|
print_token_pointer(*builder, token);
|
||||||
@@ -214,7 +288,7 @@ missing_type_specifier :: (state : *Parse_State, token : Token, message : string
|
|||||||
|
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
cyan(*builder);
|
cyan(*builder);
|
||||||
print_to_builder(*builder, "%\n", print_from_source_location(location));
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
|
|
||||||
loc := location.begin;
|
loc := location.begin;
|
||||||
@@ -238,7 +312,7 @@ empty_block :: (state : *Parse_State, token : Token, message : string) {
|
|||||||
|
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
cyan(*builder);
|
cyan(*builder);
|
||||||
print_to_builder(*builder, "%\n", print_from_source_location(location));
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
indent(*builder, 1);
|
indent(*builder, 1);
|
||||||
|
|
||||||
loc := location.begin;
|
loc := location.begin;
|
||||||
@@ -252,6 +326,45 @@ empty_block :: (state : *Parse_State, token : Token, message : string) {
|
|||||||
record_error(state, token, final_message, false);
|
record_error(state, token, final_message, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unable_to_open_file :: (state : *Parse_State, path : string, token : Token) {
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
print_to_builder(*builder, "Unable to open file '%' for reading\n\n", path);
|
||||||
|
|
||||||
|
location := generate_source_location_from_token(state, token);
|
||||||
|
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
indent(*builder, 1);
|
||||||
|
|
||||||
|
loc := location.begin;
|
||||||
|
print_token_pointer(*builder, loc);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_point_requires_return_value :: (state : *Parse_State, token : Token) {
|
||||||
|
builder : String_Builder;
|
||||||
|
init_string_builder(*builder,, temp);
|
||||||
|
|
||||||
|
print_to_builder(*builder, "Entry point '%' requires return value\n\n", token.ident_value);
|
||||||
|
|
||||||
|
location := generate_source_location_from_token(state, token);
|
||||||
|
indent(*builder, 1);
|
||||||
|
cyan(*builder);
|
||||||
|
print_to_builder(*builder, "%\n", print_from_source_location(state.ctx, location));
|
||||||
|
indent(*builder, 1);
|
||||||
|
|
||||||
|
loc := location.begin;
|
||||||
|
print_token_pointer(*builder, loc);
|
||||||
|
|
||||||
|
final_message := builder_to_string(*builder);
|
||||||
|
record_error(state, token, final_message, false);
|
||||||
|
}
|
||||||
|
|
||||||
error_node :: (parse_state : *Parse_State, message : string) -> *AST_Node {
|
error_node :: (parse_state : *Parse_State, message : string) -> *AST_Node {
|
||||||
node := make_node(parse_state, .Error);
|
node := make_node(parse_state, .Error);
|
||||||
node.name = copy_string(message);
|
node.name = copy_string(message);
|
||||||
@@ -264,8 +377,8 @@ error_node :: (parse_state : *Parse_State, message : string) -> *AST_Node {
|
|||||||
advance_to_sync_point :: (parse_state : *Parse_State) {
|
advance_to_sync_point :: (parse_state : *Parse_State) {
|
||||||
while true {
|
while true {
|
||||||
if parse_state.current.kind == .TOKEN_SEMICOLON || parse_state.current.kind == .TOKEN_RIGHTBRACE ||
|
if parse_state.current.kind == .TOKEN_SEMICOLON || parse_state.current.kind == .TOKEN_RIGHTBRACE ||
|
||||||
parse_state.current.kind == .TOKEN_LEFTBRACE{
|
parse_state.current.kind == .TOKEN_LEFTBRACE || parse_state.current.kind == .TOKEN_EOF {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
}
|
}
|
||||||
@@ -276,14 +389,154 @@ advance_to_sync_point :: (parse_state : *Parse_State) {
|
|||||||
////////////////////////////
|
////////////////////////////
|
||||||
//@nb - Base parsing functions
|
//@nb - Base parsing functions
|
||||||
|
|
||||||
make_node :: (parse_state : *Parse_State, kind : AST_Kind) -> *AST_Node {
|
make_node :: (nodes : *[..]AST_Node, kind : AST_Kind) -> *AST_Node {
|
||||||
node : AST_Node;
|
node : AST_Node;
|
||||||
|
|
||||||
node.kind = kind;
|
node.kind = kind;
|
||||||
node.children.allocator = parse_state.child_allocator;
|
array_add(nodes, node);
|
||||||
array_add(*parse_state.result.nodes, node);
|
|
||||||
|
|
||||||
return *parse_state.result.nodes[parse_state.result.nodes.count - 1];
|
return *(nodes.*[nodes.count - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
make_node :: (parse_state : *Parse_State, kind : AST_Kind) -> *AST_Node {
|
||||||
|
return make_node(*parse_state.ctx.nodes, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
make_builtin_token :: (tokens : *[..]Token, kind : Token_Kind, text : string, col : *int, line : *int) -> *Token {
|
||||||
|
tok : Token;
|
||||||
|
tok.kind = kind;
|
||||||
|
|
||||||
|
start := 0;
|
||||||
|
|
||||||
|
tok.column = col.*;
|
||||||
|
|
||||||
|
for c : text {
|
||||||
|
if c == #char "\n" {
|
||||||
|
line.* ++ 1;
|
||||||
|
col.* = 0;
|
||||||
|
} else {
|
||||||
|
col.* += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok.index = tokens.count;
|
||||||
|
tok.length = text.count;
|
||||||
|
tok.builtin = true;
|
||||||
|
tok.source = text.data;
|
||||||
|
tok.ident_value = text;
|
||||||
|
|
||||||
|
array_add(tokens, tok);
|
||||||
|
|
||||||
|
return *(tokens.*)[tokens.count - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
new_builtin_struct_node :: (ctx : *Compiler_Context, name : string, members : []Arg) -> *AST_Node {
|
||||||
|
sc := get_scratch(context.allocator);
|
||||||
|
defer scratch_end(sc);
|
||||||
|
node := make_node(*ctx.nodes, .Struct);
|
||||||
|
|
||||||
|
source_location : Source_Range;
|
||||||
|
|
||||||
|
col := 0;
|
||||||
|
line := 0;
|
||||||
|
|
||||||
|
tok_index := ctx.tokens.count;
|
||||||
|
|
||||||
|
ident_token := make_builtin_token(*ctx.tokens, .TOKEN_IDENTIFIER, name, *col, *line);
|
||||||
|
ident_token.ident_value = name;
|
||||||
|
source_location.begin = ident_token;
|
||||||
|
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_DOUBLECOLON, " :: ", *col, *line);
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_STRUCT, "struct ", *col, *line);
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_LEFTBRACE, "{\n\t", *col, *line);
|
||||||
|
line += 1;
|
||||||
|
col = 0;
|
||||||
|
|
||||||
|
field_list := make_node(*ctx.nodes, .FieldList);
|
||||||
|
add_child(node, field_list);
|
||||||
|
|
||||||
|
for member : members {
|
||||||
|
field := make_node(*ctx.nodes, .Field);
|
||||||
|
field_source_loc : Source_Range;
|
||||||
|
|
||||||
|
field_ident := make_builtin_token(*ctx.tokens, .TOKEN_IDENTIFIER, member.name, *col, *line);
|
||||||
|
field_source_loc.begin = field_ident;
|
||||||
|
field.token = field_ident;
|
||||||
|
field.name = member.name;
|
||||||
|
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_COLON, ": ", *col, *line);
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_IDENTIFIER, member.typename, *col, *line);
|
||||||
|
semicolon_tok := make_builtin_token(*ctx.tokens, .TOKEN_SEMICOLON, ";", *col, *line);
|
||||||
|
col = 0;
|
||||||
|
line += 1;
|
||||||
|
|
||||||
|
field_source_loc.end = semicolon_tok;
|
||||||
|
field.source_location = field_source_loc;
|
||||||
|
|
||||||
|
add_child(field_list, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
brace_token := make_builtin_token(*ctx.tokens, .TOKEN_RIGHTBRACE, "\n}", *col, *line);
|
||||||
|
|
||||||
|
source_location.end = brace_token;
|
||||||
|
|
||||||
|
node.source_location = source_location;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_builtin_function_node :: (ctx : *Compiler_Context, name : string, members : []Arg, return_var : Arg) -> *AST_Node {
|
||||||
|
sc := get_scratch(context.allocator);
|
||||||
|
defer scratch_end(sc);
|
||||||
|
|
||||||
|
node := make_node(*ctx.nodes, .Function);
|
||||||
|
|
||||||
|
source_location : Source_Range;
|
||||||
|
|
||||||
|
col := 0;
|
||||||
|
line := 0;
|
||||||
|
|
||||||
|
tok_index := ctx.tokens.count;
|
||||||
|
|
||||||
|
ident_token := make_builtin_token(*ctx.tokens, .TOKEN_IDENTIFIER, name, *col, *line);
|
||||||
|
source_location.begin = ident_token;
|
||||||
|
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_DOUBLECOLON, " :: ", *col, *line);
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_LEFTPAREN, "(", *col, *line);
|
||||||
|
field_list := make_node(*ctx.nodes, .FieldList);
|
||||||
|
add_child(node, field_list);
|
||||||
|
|
||||||
|
for member : members {
|
||||||
|
field := make_node(*ctx.nodes, .Field);
|
||||||
|
field_source_loc : Source_Range;
|
||||||
|
|
||||||
|
type_tok := make_builtin_token(*ctx.tokens, .TOKEN_IDENTIFIER, member.typename, *col, *line);
|
||||||
|
field_source_loc.begin = type_tok;
|
||||||
|
field.token = type_tok;
|
||||||
|
|
||||||
|
if it_index < members.count - 1 {
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_COMMA, ", ", *col, *line);
|
||||||
|
}
|
||||||
|
|
||||||
|
field_source_loc.end = type_tok;
|
||||||
|
field.source_location = field_source_loc;
|
||||||
|
|
||||||
|
add_child(field_list, field);
|
||||||
|
}
|
||||||
|
|
||||||
|
make_builtin_token(*ctx.tokens, .TOKEN_RIGHTPAREN, ")", *col, *line);
|
||||||
|
semicolon_tok := make_builtin_token(*ctx.tokens, .TOKEN_SEMICOLON, ";", *col, *line);
|
||||||
|
|
||||||
|
source_location.end = semicolon_tok;
|
||||||
|
|
||||||
|
node.source_location = source_location;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_field_list :: (struct_or_func : *AST_Node) -> *AST_Node {
|
||||||
|
assert(struct_or_func.kind == .Function || struct_or_func.kind == .Struct || struct_or_func.kind == .Properties);
|
||||||
|
return struct_or_func.children[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
add_child :: (node : *AST_Node, child : *AST_Node) {
|
add_child :: (node : *AST_Node, child : *AST_Node) {
|
||||||
@@ -316,7 +569,10 @@ advance :: (parse_state : *Parse_State) {
|
|||||||
parse_state.previous = parse_state.current;
|
parse_state.previous = parse_state.current;
|
||||||
|
|
||||||
while true {
|
while true {
|
||||||
parse_state.current = *parse_state.tokens[parse_state.current_token_index];
|
if parse_state.current_token_index >= parse_state.ctx.tokens.count {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
parse_state.current = *parse_state.ctx.tokens[parse_state.current_token_index];
|
||||||
parse_state.current_token_index += 1;
|
parse_state.current_token_index += 1;
|
||||||
if parse_state.current.kind != .TOKEN_ERROR break;
|
if parse_state.current.kind != .TOKEN_ERROR break;
|
||||||
|
|
||||||
@@ -338,9 +594,18 @@ check :: (parse_state : *Parse_State, kind : Token_Kind) -> bool {
|
|||||||
return parse_state.current.kind == kind;
|
return parse_state.current.kind == kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_any :: (parse_state : *Parse_State, kinds : ..Token_Kind) -> bool {
|
||||||
|
for kind : kinds {
|
||||||
|
if check(parse_state, kind) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//nb - Checks if the next token is of a certain kind
|
//nb - Checks if the next token is of a certain kind
|
||||||
check_next :: (parse_state : *Parse_State, kind : Token_Kind) -> bool {
|
check_next :: (parse_state : *Parse_State, kind : Token_Kind) -> bool {
|
||||||
return parse_state.tokens[parse_state.current_token_index].kind == kind;
|
return parse_state.ctx.tokens[parse_state.current_token_index].kind == kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
//nb - Consume a token if
|
//nb - Consume a token if
|
||||||
@@ -350,9 +615,14 @@ consume :: (parse_state : *Parse_State, kind : Token_Kind, message : string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
advance(parse_state);
|
token := parse_state.previous;
|
||||||
unexpected_token(parse_state, parse_state.current, message);
|
advance_to_sync_point(parse_state);
|
||||||
consume(parse_state, kind, message);
|
|
||||||
|
unexpected_token(parse_state, token, message);
|
||||||
|
|
||||||
|
if parse_state.current.kind == .TOKEN_EOF {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
@@ -361,13 +631,21 @@ get_rule :: (kind : Token_Kind) -> *Parse_Rule {
|
|||||||
return *parse_rules[kind];
|
return *parse_rules[kind];
|
||||||
}
|
}
|
||||||
|
|
||||||
precedence :: (parse_state : *Parse_State, precedence : Precedence) -> *AST_Node {
|
precedence :: (parse_state : *Parse_State, precedence : Precedence, message : string = "") -> *AST_Node {
|
||||||
|
prev := parse_state.previous;
|
||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
|
|
||||||
prefix_rule := get_rule(parse_state.previous.kind).prefix;
|
prefix_rule := get_rule(parse_state.previous.kind).prefix;
|
||||||
if prefix_rule == null {
|
if prefix_rule == null {
|
||||||
expected_expression(parse_state, parse_state.current, "Expected expression.");
|
tok_s : string;
|
||||||
// @Incomplete: Add error node here?
|
tok_s.data = prev.source;
|
||||||
|
tok_s.count = prev.length;
|
||||||
|
if message {
|
||||||
|
expected_expression(parse_state, prev, tprint("Expected expression after '%'. %", tok_s, message));
|
||||||
|
} else {
|
||||||
|
expected_expression(parse_state, prev, tprint("Expected expression after '%'.", tok_s));
|
||||||
|
}
|
||||||
|
|
||||||
return error_node(parse_state, "Expected expression.");
|
return error_node(parse_state, "Expected expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +654,10 @@ precedence :: (parse_state : *Parse_State, precedence : Precedence) -> *AST_Node
|
|||||||
while precedence <= get_rule(parse_state.current.kind).precedence {
|
while precedence <= get_rule(parse_state.current.kind).precedence {
|
||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
if parse_state.current.kind == .TOKEN_EOF {
|
if parse_state.current.kind == .TOKEN_EOF {
|
||||||
expected_expression(parse_state, parse_state.current, "Reached end of file. Expected expression.");
|
tok_s : string;
|
||||||
|
tok_s.data = parse_state.previous.source;
|
||||||
|
tok_s.count = parse_state.previous.length;
|
||||||
|
expected_expression(parse_state, parse_state.current, tprint("Reached end of file. Expected expression after '%'.", tok_s));
|
||||||
// @Incomplete: Add error node here?
|
// @Incomplete: Add error node here?
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -415,6 +696,10 @@ binary :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
|
|
||||||
if op.kind == {
|
if op.kind == {
|
||||||
case .TOKEN_PLUS; #through;
|
case .TOKEN_PLUS; #through;
|
||||||
|
case .TOKEN_PLUSEQUALS; #through;
|
||||||
|
case .TOKEN_MINUSEQUALS; #through;
|
||||||
|
case .TOKEN_TIMESEQUALS; #through;
|
||||||
|
case .TOKEN_DIVEQUALS; #through;
|
||||||
case .TOKEN_MINUS; #through;
|
case .TOKEN_MINUS; #through;
|
||||||
case .TOKEN_STAR; #through;
|
case .TOKEN_STAR; #through;
|
||||||
case .TOKEN_SLASH; #through;
|
case .TOKEN_SLASH; #through;
|
||||||
@@ -439,8 +724,8 @@ binary :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
array_access :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
array_access :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
||||||
identifier := parse_state.tokens[parse_state.current_token_index - 3];
|
identifier := parse_state.ctx.tokens[parse_state.current_token_index - 3];
|
||||||
left_bracket := parse_state.tokens[parse_state.current_token_index - 2];
|
left_bracket := parse_state.ctx.tokens[parse_state.current_token_index - 2];
|
||||||
|
|
||||||
array_access := make_node(parse_state, .Unary);
|
array_access := make_node(parse_state, .Unary);
|
||||||
array_access.token = left_bracket;
|
array_access.token = left_bracket;
|
||||||
@@ -465,6 +750,7 @@ array_access :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source_location.end = parse_state.previous;
|
source_location.end = parse_state.previous;
|
||||||
|
left.source_location = source_location;
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,6 +790,78 @@ directive :: (state : *Parse_State) -> *AST_Node {
|
|||||||
func := function_declaration(state, identifier_token, .None, false, false);
|
func := function_declaration(state, identifier_token, .None, false, false);
|
||||||
func.foreign_declaration = true;
|
func.foreign_declaration = true;
|
||||||
return func;
|
return func;
|
||||||
|
} else if state.current.ident_value == "if" {
|
||||||
|
if_directive := make_node(state, .If_Directive);
|
||||||
|
|
||||||
|
source_location : Source_Range;
|
||||||
|
if state.previous {
|
||||||
|
source_location.begin = state.previous;
|
||||||
|
} else {
|
||||||
|
source_location.begin = state.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance(state);
|
||||||
|
|
||||||
|
cond := expression(state);
|
||||||
|
add_child(if_directive, cond);
|
||||||
|
|
||||||
|
source_location.end = state.previous;
|
||||||
|
advance_to_sync_point(state);
|
||||||
|
|
||||||
|
if_body := block(state);
|
||||||
|
add_child(if_directive, if_body);
|
||||||
|
|
||||||
|
if match(state, .TOKEN_ELSE) {
|
||||||
|
else_node := else_statement(state);
|
||||||
|
add_child(if_directive, else_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if_directive.source_location = source_location;
|
||||||
|
|
||||||
|
return if_directive;
|
||||||
|
} else if state.current.ident_value == "load" {
|
||||||
|
advance(state);
|
||||||
|
|
||||||
|
if check(state, .TOKEN_STRING) {
|
||||||
|
// path_tok := state.current;
|
||||||
|
// path := path_tok.string_value;
|
||||||
|
|
||||||
|
// advance(state);
|
||||||
|
|
||||||
|
// result : Compiler_Context;
|
||||||
|
// ctx.allocator = state.ctx.allocator;
|
||||||
|
// ctx.environment = state.ctx.environment;
|
||||||
|
|
||||||
|
// ctx.file = make_file(*result, path);
|
||||||
|
|
||||||
|
// if ctx.file.source.count == 0 {
|
||||||
|
// unable_to_open_file(state, path, path_tok);
|
||||||
|
// advance_to_sync_point(state);
|
||||||
|
// advance(state);
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// consume(state, .TOKEN_SEMICOLON, "Expected ';' after #load directive");
|
||||||
|
|
||||||
|
// lex(*result);
|
||||||
|
|
||||||
|
// count := state.ctx.tokens..count;
|
||||||
|
// current_idx := state.current_token_index;
|
||||||
|
// result_count := ctx.tokens..count;
|
||||||
|
|
||||||
|
// // state.ctx.tokens..count -= 1;
|
||||||
|
// array_resize(*state.ctx.tokens., count + result_count - 1);
|
||||||
|
|
||||||
|
// memcpy(*state.ctx.tokens[current_idx + result_count - 1], *state.ctx.tokens[current_idx], size_of(Token) * (count - current_idx));
|
||||||
|
|
||||||
|
// for *tok : ctx.tokens. {
|
||||||
|
// if tok.kind == .TOKEN_EOF {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// tok.builtin = true;
|
||||||
|
// state.ctx.tokens[it_index] = tok.*;
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -542,7 +900,7 @@ dot :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
source_location : Source_Range;
|
source_location : Source_Range;
|
||||||
source_location.begin = left.source_location.begin;
|
source_location.begin = left.source_location.begin;
|
||||||
|
|
||||||
if check(parse_state, .TOKEN_ASSIGN) {
|
if check_any(parse_state, .TOKEN_ASSIGN, .TOKEN_MINUSEQUALS, .TOKEN_PLUSEQUALS, .TOKEN_DIVEQUALS, .TOKEN_MODEQUALS, .TOKEN_TIMESEQUALS) {
|
||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
variable := make_node(parse_state, .Variable);
|
variable := make_node(parse_state, .Variable);
|
||||||
variable.source_location = generate_source_location_from_token(parse_state, identifier);
|
variable.source_location = generate_source_location_from_token(parse_state, identifier);
|
||||||
@@ -555,12 +913,15 @@ dot :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
add_child(node, left);
|
add_child(node, left);
|
||||||
add_child(node, expression(parse_state));
|
add_child(node, expression(parse_state));
|
||||||
return node;
|
return node;
|
||||||
} else if check(parse_state, .TOKEN_DOT) {
|
|
||||||
// @Incomplete(nb): Another level of access
|
|
||||||
}
|
}
|
||||||
variable := make_node(parse_state, .Variable);
|
variable := make_node(parse_state, .Variable);
|
||||||
variable.name = identifier.ident_value;
|
variable.name = identifier.ident_value;
|
||||||
|
|
||||||
|
if check(parse_state, .TOKEN_DOT) {
|
||||||
|
advance(parse_state);
|
||||||
|
dot(parse_state, variable);
|
||||||
|
}
|
||||||
|
|
||||||
add_child(left, variable);
|
add_child(left, variable);
|
||||||
|
|
||||||
source_location.end = parse_state.previous;
|
source_location.end = parse_state.previous;
|
||||||
@@ -588,8 +949,8 @@ floating :: (parse_state : *Parse_State, left : *AST_Node) -> *AST_Node {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
expression :: (parse_state : *Parse_State) -> *AST_Node {
|
expression :: (parse_state : *Parse_State, message : string = "") -> *AST_Node {
|
||||||
expression := precedence(parse_state, .PREC_ASSIGNMENT);
|
expression := precedence(parse_state, .PREC_ASSIGNMENT, message);
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,8 +1004,12 @@ field_declaration :: (parse_state : *Parse_State, identifier_token : *Token) ->
|
|||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
node.array_field = true;
|
node.array_field = true;
|
||||||
} else {
|
} else {
|
||||||
missing_type_specifier(parse_state, identifier_token, "Expected type specifier after field name.");
|
if !check(parse_state, .TOKEN_ASSIGN) {
|
||||||
return node;
|
internal_error_message(*parse_state.ctx.messages, "Unimplemented error message.", parse_state.ctx.file.path);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
// missing_type_specifier(parse_state, identifier_token, "Expected type specifier after field name.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if check(parse_state, .TOKEN_AT) {
|
if check(parse_state, .TOKEN_AT) {
|
||||||
@@ -662,7 +1027,6 @@ field_declaration :: (parse_state : *Parse_State, identifier_token : *Token) ->
|
|||||||
advance(parse_state);
|
advance(parse_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if match(parse_state, .TOKEN_ASSIGN) {
|
} else if match(parse_state, .TOKEN_ASSIGN) {
|
||||||
add_child(node, expression(parse_state));
|
add_child(node, expression(parse_state));
|
||||||
}
|
}
|
||||||
@@ -685,6 +1049,9 @@ argument_list :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
|
|
||||||
source_location.main_token = parse_state.current;
|
source_location.main_token = parse_state.current;
|
||||||
|
|
||||||
|
error_before := parse_state.ctx.had_error;
|
||||||
|
parse_state.ctx.had_error = false;
|
||||||
|
|
||||||
while !check(parse_state, .TOKEN_RIGHTPAREN) {
|
while !check(parse_state, .TOKEN_RIGHTPAREN) {
|
||||||
arg := expression(parse_state);
|
arg := expression(parse_state);
|
||||||
if !node {
|
if !node {
|
||||||
@@ -695,8 +1062,14 @@ argument_list :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
|
|
||||||
if check(parse_state, .TOKEN_RIGHTPAREN) break;
|
if check(parse_state, .TOKEN_RIGHTPAREN) break;
|
||||||
consume(parse_state, .TOKEN_COMMA, "Expect ',' after function argument.");
|
consume(parse_state, .TOKEN_COMMA, "Expect ',' after function argument.");
|
||||||
|
|
||||||
|
if parse_state.ctx.had_error {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse_state.ctx.had_error = error_before || parse_state.ctx.had_error;
|
||||||
|
|
||||||
consume(parse_state, .TOKEN_RIGHTPAREN, "Expect ')' after function call.");
|
consume(parse_state, .TOKEN_RIGHTPAREN, "Expect ')' after function call.");
|
||||||
|
|
||||||
if node {
|
if node {
|
||||||
@@ -749,6 +1122,98 @@ statement :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
source_location.end = parse_state.previous;
|
source_location.end = parse_state.previous;
|
||||||
node.source_location = source_location;
|
node.source_location = source_location;
|
||||||
return node;
|
return node;
|
||||||
|
} else if match(parse_state, .TOKEN_IF) {
|
||||||
|
node := make_node(parse_state, .If);
|
||||||
|
|
||||||
|
source_location : Source_Range;
|
||||||
|
source_location.begin = parse_state.previous;
|
||||||
|
|
||||||
|
if_cond := expression(parse_state, "Expected if condition.");
|
||||||
|
add_child(node, if_cond);
|
||||||
|
source_location.end = parse_state.previous;
|
||||||
|
advance_to_sync_point(parse_state);
|
||||||
|
|
||||||
|
if_body := block(parse_state);
|
||||||
|
add_child(node, if_body);
|
||||||
|
|
||||||
|
if match(parse_state, .TOKEN_ELSE) {
|
||||||
|
else_node := else_statement(parse_state);
|
||||||
|
add_child(node, else_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.source_location = source_location;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
} else if match(parse_state, .TOKEN_ELSE) {
|
||||||
|
if check(parse_state, .TOKEN_IF) {
|
||||||
|
else_if_without_if(parse_state);
|
||||||
|
advance_to_sync_point(parse_state);
|
||||||
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
||||||
|
return block(parse_state);
|
||||||
|
}
|
||||||
|
return error_node(parse_state, "'else if' without 'if'.");
|
||||||
|
} else {
|
||||||
|
else_without_if(parse_state);
|
||||||
|
advance_to_sync_point(parse_state);
|
||||||
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
||||||
|
return block(parse_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error_node(parse_state, "'else' without 'if'.");
|
||||||
|
}
|
||||||
|
} else if match(parse_state, .TOKEN_FOR) {
|
||||||
|
if check(parse_state, .TOKEN_IDENTIFIER) {
|
||||||
|
node := make_node(parse_state, .For);
|
||||||
|
|
||||||
|
source_location : Source_Range;
|
||||||
|
source_location.begin = parse_state.previous;
|
||||||
|
|
||||||
|
loop_iterator := parse_state.current;
|
||||||
|
node.token = loop_iterator;
|
||||||
|
advance(parse_state);
|
||||||
|
consume(parse_state, .TOKEN_COLON, "Expect ':' after for loop iterator.");
|
||||||
|
|
||||||
|
snap := snapshot_state(parse_state);
|
||||||
|
|
||||||
|
begin_iter := expression(parse_state, "Expected beginning of iterator.");
|
||||||
|
if begin_iter.kind == .Error {
|
||||||
|
unable_to_parse_statement(parse_state, source_location.begin);
|
||||||
|
rewind_to_snapshot(parse_state, snap);
|
||||||
|
if parse_state.current.kind == .TOKEN_LEFTBRACE {
|
||||||
|
block(parse_state);
|
||||||
|
}
|
||||||
|
return error_node(parse_state, "'for' without well-formed iterator expression.");
|
||||||
|
}
|
||||||
|
add_child(node, begin_iter);
|
||||||
|
|
||||||
|
consume(parse_state, .TOKEN_DOTDOT, "Expect '..' after for loop iter left hand side.");
|
||||||
|
|
||||||
|
snap = snapshot_state(parse_state);
|
||||||
|
end_iter := expression(parse_state, "Expected end of iterator.");
|
||||||
|
if end_iter.kind == .Error {
|
||||||
|
unable_to_parse_statement(parse_state, source_location.begin);
|
||||||
|
rewind_to_snapshot(parse_state, snap);
|
||||||
|
|
||||||
|
if parse_state.current.kind == .TOKEN_LEFTBRACE {
|
||||||
|
|
||||||
|
block(parse_state);
|
||||||
|
}
|
||||||
|
return error_node(parse_state, "'for' without well-formed iterator expression.");
|
||||||
|
}
|
||||||
|
add_child(node, end_iter);
|
||||||
|
|
||||||
|
if check(parse_state, .TOKEN_LEFTBRACE) {
|
||||||
|
for_body := block(parse_state);
|
||||||
|
add_child(node, for_body);
|
||||||
|
} else {
|
||||||
|
unable_to_parse_statement(parse_state, source_location.begin, "'for' currently expects a brace-enclosed block as a body.");
|
||||||
|
return error_node(parse_state, "'for' currently expects a brace-enclosed block as a body.");
|
||||||
|
}
|
||||||
|
|
||||||
|
node.source_location = source_location;
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return expression_statement(parse_state);
|
return expression_statement(parse_state);
|
||||||
}
|
}
|
||||||
@@ -756,9 +1221,18 @@ statement :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
return error_node(parse_state, "Couldn't parse statement.");
|
return error_node(parse_state, "Couldn't parse statement.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else_statement :: (parse_state : *Parse_State) -> *AST_Node {
|
||||||
|
if check(parse_state, .TOKEN_IF) {
|
||||||
|
return statement(parse_state);
|
||||||
|
} else if check(parse_state, .TOKEN_DIRECTIVE) && parse_state.current.ident_value == "if" {
|
||||||
|
return directive(parse_state);
|
||||||
|
}
|
||||||
|
return block(parse_state);
|
||||||
|
}
|
||||||
|
|
||||||
block :: (parse_state : *Parse_State) -> *AST_Node {
|
block :: (parse_state : *Parse_State) -> *AST_Node {
|
||||||
node : *AST_Node = make_node(parse_state, .Block);
|
node : *AST_Node = make_node(parse_state, .Block);
|
||||||
array_reserve(*node.children, 1024);
|
array_reserve(*node.children, 32);
|
||||||
|
|
||||||
source_location : Source_Range;
|
source_location : Source_Range;
|
||||||
|
|
||||||
@@ -857,10 +1331,22 @@ function_declaration :: (parse_state : *Parse_State, identifier_token : *Token,
|
|||||||
case .Vertex; {
|
case .Vertex; {
|
||||||
node.vertex_entry_point = true;
|
node.vertex_entry_point = true;
|
||||||
name = sprint("vs_%", function_name_token.ident_value);
|
name = sprint("vs_%", function_name_token.ident_value);
|
||||||
|
|
||||||
|
// if return_type_token.kind == .TOKEN_INVALID {
|
||||||
|
// entry_point_requires_return_value(parse_state, function_name_token);
|
||||||
|
// advance_to_sync_point(parse_state);
|
||||||
|
// return error_node(parse_state, "");
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
case .Pixel; {
|
case .Pixel; {
|
||||||
node.pixel_entry_point = true;
|
node.pixel_entry_point = true;
|
||||||
name = sprint("ps_%", function_name_token.ident_value);
|
name = sprint("ps_%", function_name_token.ident_value);
|
||||||
|
|
||||||
|
// if return_type_token.kind == .TOKEN_INVALID {
|
||||||
|
// entry_point_requires_return_value(parse_state, function_name_token);
|
||||||
|
// advance_to_sync_point(parse_state);
|
||||||
|
// return error_node(parse_state, "");
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -945,6 +1431,18 @@ constant_buffer :: (parse_state : *Parse_State, identifier_token : *Token = null
|
|||||||
node : *AST_Node;
|
node : *AST_Node;
|
||||||
source_location : Source_Range;
|
source_location : Source_Range;
|
||||||
source_location.begin = parse_state.current;
|
source_location.begin = parse_state.current;
|
||||||
|
|
||||||
|
if check(parse_state, .TOKEN_AT) {
|
||||||
|
while check(parse_state, .TOKEN_AT) {
|
||||||
|
advance(parse_state);
|
||||||
|
// @Incomplete(niels): this is a mapping
|
||||||
|
if check(parse_state, .TOKEN_IDENTIFIER) {
|
||||||
|
array_add(*node.hint_tokens, parse_state.current);
|
||||||
|
advance(parse_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'constant_buffer' keyword");
|
consume(parse_state, .TOKEN_LEFTBRACE, "Expect '{' after 'constant_buffer' keyword");
|
||||||
buffer := field_list(parse_state, .Semicolon);
|
buffer := field_list(parse_state, .Semicolon);
|
||||||
|
|
||||||
@@ -999,6 +1497,7 @@ const_declaration :: (parse_state : *Parse_State, identifier_token : *Token) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
declaration :: (parse_state : *Parse_State) -> *AST_Node {
|
declaration :: (parse_state : *Parse_State) -> *AST_Node {
|
||||||
|
skip_statement := false;
|
||||||
decl_node : *AST_Node;
|
decl_node : *AST_Node;
|
||||||
if match(parse_state, .TOKEN_PROPERTIES) {
|
if match(parse_state, .TOKEN_PROPERTIES) {
|
||||||
decl_node = property_block(parse_state);
|
decl_node = property_block(parse_state);
|
||||||
@@ -1026,6 +1525,7 @@ declaration :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
decl_node = call(parse_state, null);
|
decl_node = call(parse_state, null);
|
||||||
} else if check(parse_state, .TOKEN_DIRECTIVE) {
|
} else if check(parse_state, .TOKEN_DIRECTIVE) {
|
||||||
decl_node = directive(parse_state);
|
decl_node = directive(parse_state);
|
||||||
|
skip_statement = true;
|
||||||
} else if check(parse_state, .TOKEN_IDENTIFIER) {
|
} else if check(parse_state, .TOKEN_IDENTIFIER) {
|
||||||
identifier := parse_state.current;
|
identifier := parse_state.current;
|
||||||
|
|
||||||
@@ -1048,7 +1548,7 @@ declaration :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
decl_node = error;
|
decl_node = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !decl_node {
|
if !decl_node && !skip_statement {
|
||||||
decl_node = statement(parse_state);
|
decl_node = statement(parse_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1059,16 +1559,28 @@ declaration :: (parse_state : *Parse_State) -> *AST_Node {
|
|||||||
return decl_node;
|
return decl_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
parse :: (result : *Compile_Result) {
|
parse :: (ctx : *Compiler_Context, allocator := temp) {
|
||||||
for *file : result.files {
|
if ctx.had_error {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_context := context;
|
||||||
|
new_context.allocator = allocator;
|
||||||
|
push_context new_context {
|
||||||
|
init_context_allocators();
|
||||||
|
defer clear_context_allocators();
|
||||||
|
|
||||||
parse_state : Parse_State;
|
parse_state : Parse_State;
|
||||||
init_parse_state(*parse_state, file.tokens.tokens, file.file.path);
|
array_reserve(*ctx.nodes, 4096);
|
||||||
|
parse_state.current_token_index = 0;
|
||||||
|
parse_state.ctx = ctx;
|
||||||
|
|
||||||
advance(*parse_state);
|
advance(*parse_state);
|
||||||
|
|
||||||
if !match(*parse_state, .TOKEN_EOF) {
|
if !match(*parse_state, .TOKEN_EOF) {
|
||||||
parse_state.result.root = make_node(*parse_state, .Program);
|
parse_state.ctx.root = make_node(*parse_state, .Program);
|
||||||
array_reserve(*parse_state.result.root.children, 1024);
|
array_reserve(*parse_state.ctx.root.children, 1024);
|
||||||
program := parse_state.result.root;
|
program := parse_state.ctx.root;
|
||||||
|
|
||||||
while !check(*parse_state, .TOKEN_EOF) {
|
while !check(*parse_state, .TOKEN_EOF) {
|
||||||
decl := declaration(*parse_state);
|
decl := declaration(*parse_state);
|
||||||
@@ -1077,32 +1589,7 @@ parse :: (result : *Compile_Result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//@Incomplete(nb): will this straight copy just work?
|
|
||||||
// Might need to rething how we do this.
|
|
||||||
file.ast_root = parse_state.result.root;
|
|
||||||
file.ast_nodes = parse_state.result.nodes;
|
|
||||||
copy_messages(parse_state.result.messages, *result.messages);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse :: (parse_state : *Parse_State) -> Parse_Result {
|
|
||||||
advance(parse_state);
|
|
||||||
|
|
||||||
if !match(parse_state, .TOKEN_EOF) {
|
|
||||||
parse_state.result.root = make_node(parse_state, .Program);
|
|
||||||
array_reserve(*parse_state.result.root.children, 1024);
|
|
||||||
program := parse_state.result.root;
|
|
||||||
|
|
||||||
while !check(parse_state, .TOKEN_EOF) {
|
|
||||||
decl := declaration(parse_state);
|
|
||||||
if decl {
|
|
||||||
add_child(program, decl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parse_state.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
#load "AST.jai";
|
#load "AST.jai";
|
||||||
|
|||||||
78
README.md
78
README.md
@@ -20,15 +20,15 @@ There is basic support for most HLSL built-in math functions for the following t
|
|||||||
- Vector types: float2, float3, float4, int2, int3, int4
|
- Vector types: float2, float3, float4, int2, int3, int4
|
||||||
- Matrices: float4x4
|
- Matrices: float4x4
|
||||||
All of the above can be constructed with their namesake constructors i.e. `float4(x, y, z, w);`.
|
All of the above can be constructed with their namesake constructors i.e. `float4(x, y, z, w);`.
|
||||||
We don't yet support textures and samplers.
|
We also support Samplers and Texture2D
|
||||||
|
|
||||||
If you want to declare and use variables you can do it as follows
|
If you want to declare and use variables you can do it as follows
|
||||||
```hlsl
|
```hlsl
|
||||||
x : float = 2.0; // no 'f' suffix required or even supported (it gives an error)
|
x : float = 2.0; // no 'f' suffix required or even supported (it gives an error)
|
||||||
y : float = 4.0;
|
y : float = 4.0;
|
||||||
v : float2 = float2(x, y);
|
v : float2 = float2(x, y);
|
||||||
|
v2 := float2(x, y);
|
||||||
```
|
```
|
||||||
For now it is required to specify the type of the variable (no type inference).
|
|
||||||
|
|
||||||
You can also do arithmetic as you would expect
|
You can also do arithmetic as you would expect
|
||||||
```
|
```
|
||||||
@@ -43,6 +43,7 @@ Camera_Data :: struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
And there is a special struct called `properties`, which is used for custom data you want to pass in.
|
And there is a special struct called `properties`, which is used for custom data you want to pass in.
|
||||||
|
#### ** Note: Properties will likely be deprecated, since the language now supports `@` hints to easily mark buffers and values with metadata.**
|
||||||
```hlsl
|
```hlsl
|
||||||
properties {
|
properties {
|
||||||
projection : float4x4;
|
projection : float4x4;
|
||||||
@@ -53,13 +54,14 @@ which will be exposed in the compiled result. `properties` can be renamed to a c
|
|||||||
```
|
```
|
||||||
p :: properties {
|
p :: properties {
|
||||||
...
|
...
|
||||||
}
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also define constant buffers
|
You can also define constant buffers
|
||||||
|
|
||||||
```
|
```
|
||||||
camera :: Constant_Buffer {
|
camera :: constant_buffer {
|
||||||
projection : float4x4;
|
projection : float4x4;
|
||||||
view : float4x4;
|
view : float4x4;
|
||||||
}
|
}
|
||||||
@@ -70,69 +72,68 @@ camera :: Constant_Buffer {
|
|||||||
To compile a shader and use the result, you can do the following in jai
|
To compile a shader and use the result, you can do the following in jai
|
||||||
```jai
|
```jai
|
||||||
|
|
||||||
parse_shader :: (path : string, allocator : Allocator) -> Compilation_Result {
|
// In the future, you can pass environment defines to the compiler.
|
||||||
// In the future, you can pass environment defines to the compiler.
|
ctx : Compiler_Context;
|
||||||
compiler : Shader_Compiler;
|
compile_file(*compiler, "shader.shd", allocator);
|
||||||
|
|
||||||
return compile_file(*compiler, path,, allocator);
|
if ctx.had_error {
|
||||||
}
|
log_error("%\n", report_messages(ctx.messages),, temp);
|
||||||
|
|
||||||
result := parse_shader("shader.shd", allocator);
|
|
||||||
if result.had_error {
|
|
||||||
log_error("%\n", report_messages(result.messages),, temp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
collection := result.collection;
|
// The ctx now contains all the needed information like the source text, entry points, constant buffers etc.
|
||||||
variant := collection.variants[0];
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
When parsing a shader you get the following struct as a result
|
When parsing a shader you get the following struct as a result
|
||||||
```
|
```
|
||||||
Compilation_Result :: struct {
|
Compiler_Context :: struct {
|
||||||
messages : [..]Compiler_Message;
|
file : Input_File;
|
||||||
|
|
||||||
had_error : bool;
|
environment : Environment;
|
||||||
|
|
||||||
collection : Shader_Variant_Collection;
|
tokens : [..]Token;;
|
||||||
}
|
root : *AST_Node;
|
||||||
```
|
nodes : [..]AST_Node;
|
||||||
|
|
||||||
A `Shader_Variant_Collection` looks as follows
|
codegen_result_text : string;
|
||||||
```
|
|
||||||
Shader_Variant_Collection :: struct {
|
|
||||||
properties : Properties;
|
|
||||||
|
|
||||||
max_constant_buffers :: 16;
|
constant_buffers : Static_Array(Type_Variable_Handle, 16);
|
||||||
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
|
|
||||||
|
|
||||||
variants : [..]Shader_Variant;
|
scope_stack : Scope_Stack;
|
||||||
}
|
type_variables : [..]Type_Variable;
|
||||||
|
|
||||||
Shader_Variant :: struct {
|
property_name : string;
|
||||||
text : string;
|
|
||||||
|
|
||||||
vertex_entry_point : struct {
|
vertex_entry_point : struct {
|
||||||
|
node : *AST_Node;
|
||||||
name : string;
|
name : string;
|
||||||
|
|
||||||
input : [..]Field;
|
input : [..]Field;
|
||||||
}
|
}
|
||||||
|
|
||||||
pixel_entry_point : struct {
|
pixel_entry_point : struct {
|
||||||
|
node : *AST_Node;
|
||||||
name : string;
|
name : string;
|
||||||
|
|
||||||
return_value : Field;
|
return_value : Field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
properties : Properties;
|
||||||
|
|
||||||
|
max_constant_buffers :: 16;
|
||||||
|
|
||||||
|
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
|
||||||
|
|
||||||
|
had_error : bool;
|
||||||
|
messages : [..]Compiler_Message;
|
||||||
}
|
}
|
||||||
|
|
||||||
Constant_Buffer :: struct {
|
Constant_Buffer :: struct {
|
||||||
register : int;
|
|
||||||
|
|
||||||
name : string;
|
name : string;
|
||||||
|
|
||||||
fields : Static_Array(Property_Field, 16);
|
fields : Static_Array(Property_Field, 16);
|
||||||
|
|
||||||
|
// hints : Field_Hint; // optional hint...
|
||||||
|
hints : [..]Field_Hint;
|
||||||
|
|
||||||
buffer_index : u32;
|
buffer_index : u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,11 +193,10 @@ Hint_Kind :: enum {
|
|||||||
|
|
||||||
## Notable missing features
|
## Notable missing features
|
||||||
|
|
||||||
- Control flow: if/else, for, while, switch etc.
|
- While
|
||||||
- Arrays
|
- Arrays
|
||||||
- Textures and samplers
|
|
||||||
- Multiple render targets
|
- Multiple render targets
|
||||||
- Custom buffers/structured buffers
|
- Custom buffers/structured buffers
|
||||||
- Interpolation specifiers
|
- Interpolation specifiers
|
||||||
- Proper variant handling with environment defines
|
- Proper variant handling with environment defines
|
||||||
- Include/importing files such as shared utils etc.
|
- Importing files such as shared utils etc. with something other than textual `#load`
|
||||||
File diff suppressed because it is too large
Load Diff
778
Test.jai
778
Test.jai
@@ -1,778 +0,0 @@
|
|||||||
/////////////////////////////////////
|
|
||||||
//~ nbr: General improvements
|
|
||||||
//
|
|
||||||
// [x] Print out all failed tests in a list at the end
|
|
||||||
// [ ] Use unix (posix? bash? ascii?) color codes for errors
|
|
||||||
// [ ] Print golden file as green and new output as red
|
|
||||||
|
|
||||||
#import "Basic";
|
|
||||||
#import "File";
|
|
||||||
#import "String";
|
|
||||||
#import "File_Utilities";
|
|
||||||
#import "Print_Color";
|
|
||||||
|
|
||||||
#load "module.jai";
|
|
||||||
|
|
||||||
GOLDEN_EXTENSION :: "golden";
|
|
||||||
LEXER_FOLDER :: "lex";
|
|
||||||
PARSER_FOLDER :: "parse";
|
|
||||||
CODEGEN_FOLDER :: "codegen";
|
|
||||||
COMPILED_FOLDER :: "compiled";
|
|
||||||
SEMANTIC_ANALYSIS_FOLDER :: "semant";
|
|
||||||
TESTS_FOLDER :: "test";
|
|
||||||
|
|
||||||
SHADER_EXTENSION :: "shd";
|
|
||||||
SUITE_EXTENSION :: "suite";
|
|
||||||
|
|
||||||
Stage_Flags :: enum_flags u16 {
|
|
||||||
Lexer :: 0x1;
|
|
||||||
Parser :: 0x2;
|
|
||||||
Semantic_Analysis :: 0x4;
|
|
||||||
Codegen :: 0x8;
|
|
||||||
Compile :: 0x10;
|
|
||||||
}
|
|
||||||
|
|
||||||
Output_Type :: enum_flags u16 {
|
|
||||||
Golden :: 0x1;
|
|
||||||
StdOut :: 0x2;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result_Type :: enum {
|
|
||||||
File_Read_Failed;
|
|
||||||
Golden_File_Not_Found;
|
|
||||||
StdOut;
|
|
||||||
Golden_Output;
|
|
||||||
Passed;
|
|
||||||
Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result :: struct {
|
|
||||||
type : Result_Type;
|
|
||||||
path : string;
|
|
||||||
stage : Stage_Flags;
|
|
||||||
|
|
||||||
golden_path : string;
|
|
||||||
info_text : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
Test_Case :: struct {
|
|
||||||
path : string;
|
|
||||||
stage_flags : Stage_Flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
Test_Suite :: struct {
|
|
||||||
name : string;
|
|
||||||
test_cases : [..]Test_Case;
|
|
||||||
|
|
||||||
results : [..]Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_golden_path :: (file_path : string, stage : Stage_Flags, allocator := context.allocator) -> string {
|
|
||||||
path := parse_path(file_path);
|
|
||||||
file_without_extension := split(path.words[path.words.count - 1], ".");
|
|
||||||
|
|
||||||
builder : String_Builder;
|
|
||||||
builder.allocator = temp;
|
|
||||||
|
|
||||||
final_path_length := file_path.count - SHADER_EXTENSION.count + GOLDEN_EXTENSION.count + 1; // +1 for dot
|
|
||||||
|
|
||||||
path.words.count -= 1;
|
|
||||||
|
|
||||||
if stage == {
|
|
||||||
case .Lexer; {
|
|
||||||
dir := tprint("%/%", TESTS_FOLDER, LEXER_FOLDER);
|
|
||||||
make_directory_if_it_does_not_exist(dir);
|
|
||||||
array_add(*path.words, LEXER_FOLDER);
|
|
||||||
}
|
|
||||||
case .Parser; {
|
|
||||||
dir := tprint("%/%", TESTS_FOLDER, PARSER_FOLDER);
|
|
||||||
make_directory_if_it_does_not_exist(dir);
|
|
||||||
array_add(*path.words, PARSER_FOLDER);
|
|
||||||
}
|
|
||||||
case .Semantic_Analysis; {
|
|
||||||
dir := tprint("%/%", TESTS_FOLDER, SEMANTIC_ANALYSIS_FOLDER);
|
|
||||||
make_directory_if_it_does_not_exist(dir);
|
|
||||||
array_add(*path.words, SEMANTIC_ANALYSIS_FOLDER);
|
|
||||||
}
|
|
||||||
case .Codegen; {
|
|
||||||
dir := tprint("%/%", TESTS_FOLDER, CODEGEN_FOLDER);
|
|
||||||
make_directory_if_it_does_not_exist(dir);
|
|
||||||
array_add(*path.words, CODEGEN_FOLDER);
|
|
||||||
}
|
|
||||||
case .Compile; {
|
|
||||||
dir := tprint("%/%", TESTS_FOLDER, COMPILED_FOLDER);
|
|
||||||
make_directory_if_it_does_not_exist(dir);
|
|
||||||
array_add(*path.words, COMPILED_FOLDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init_string_builder(*builder, file_without_extension.count + GOLDEN_EXTENSION.count + 1);
|
|
||||||
append(*builder, file_without_extension[0]);
|
|
||||||
append(*builder, ".");
|
|
||||||
append(*builder, GOLDEN_EXTENSION);
|
|
||||||
golden_path := builder_to_string(*builder);
|
|
||||||
array_add(*path.words, golden_path);
|
|
||||||
|
|
||||||
final_path := path_to_string(path);
|
|
||||||
|
|
||||||
return final_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_lexer_test :: (file_path : string, lexer : *Lexer, output_type : Output_Type = 0) -> Result {
|
|
||||||
ok := read_input_from_file(lexer, file_path);
|
|
||||||
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = file_path;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
result_data.type = .File_Read_Failed;
|
|
||||||
result_data.info_text = tprint("Unable to read file: %\n", file_path);
|
|
||||||
|
|
||||||
return result_data;
|
|
||||||
} else {
|
|
||||||
result_text : string;
|
|
||||||
result := lex(lexer, *temp);
|
|
||||||
|
|
||||||
if result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_text = report_messages(result.messages);
|
|
||||||
} else {
|
|
||||||
result_text = pretty_print_tokens(result.tokens, *temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.info_text = result_text;
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_path := get_golden_path(file_path, .Lexer);
|
|
||||||
do_golden_comparison(golden_path, result_text, *result_data, output_type);
|
|
||||||
return result_data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run_parser_test :: (file_path : string, output_type : Output_Type = 0) -> Result, *AST_Node {
|
|
||||||
lexer : Lexer;
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = file_path;
|
|
||||||
|
|
||||||
ok := read_input_from_file(*lexer, file_path);
|
|
||||||
if !ok {
|
|
||||||
log_error("Unable to read file: %\n", file_path);
|
|
||||||
result_data.type = .File_Read_Failed;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
return result_data, null;
|
|
||||||
}
|
|
||||||
|
|
||||||
result := lex(*lexer, *temp);
|
|
||||||
if result.had_error {
|
|
||||||
result_data.type = .Passed;
|
|
||||||
return result_data, null;
|
|
||||||
}
|
|
||||||
|
|
||||||
result_data =, root := run_parser_test(*lexer, output_type);
|
|
||||||
|
|
||||||
return result_data, root;
|
|
||||||
}
|
|
||||||
|
|
||||||
do_golden_comparison :: (golden_path : string, comparison_text : string, result_data : *Result, output_type : Output_Type) {
|
|
||||||
if output_type & .Golden {
|
|
||||||
// Output the comparison file
|
|
||||||
write_entire_file(golden_path, comparison_text);
|
|
||||||
result_data.golden_path = copy_string(golden_path);
|
|
||||||
result_data.type = .Golden_Output;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// Do the comparison
|
|
||||||
if !file_exists(golden_path) {
|
|
||||||
result_data.info_text = tprint("Golden file % does not exist. Please run with -output-as-golden at least once.\n", golden_path);
|
|
||||||
result_data.type = .Golden_File_Not_Found;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_text, ok := read_entire_file(golden_path);
|
|
||||||
if !ok {
|
|
||||||
result_data.info_text = tprint("Unable to open golden file %\n", golden_path);
|
|
||||||
result_data.type = .Golden_File_Not_Found;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
comp := replace(comparison_text, "\r\n", "\n");
|
|
||||||
gold := replace(golden_text, "\r\n", "\n");
|
|
||||||
result := compare(comp, gold) == 0;
|
|
||||||
if !result {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.info_text = tprint("Golden file:\n%\n===============\n%", gold, comp);
|
|
||||||
} else {
|
|
||||||
result_data.type = .Passed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run_parser_test :: (lexer : *Lexer, output_type : Output_Type = 0) -> Result, *AST_Node {
|
|
||||||
parse_state : Parse_State;
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = lexer.path;
|
|
||||||
result_data.stage = .Parser;
|
|
||||||
init_parse_state(*parse_state, lexer.result.tokens, lexer.path);
|
|
||||||
|
|
||||||
result := parse(*parse_state);
|
|
||||||
result_node : *AST_Node;
|
|
||||||
result_text : string;
|
|
||||||
|
|
||||||
if result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_text = report_messages(result.messages,, temp);
|
|
||||||
} else {
|
|
||||||
result_text = pretty_print_ast(parse_state.result.root, *temp);
|
|
||||||
result_node = parse_state.result.root;
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.info_text = result_text;
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data, result_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_path := get_golden_path(parse_state.path, .Parser);
|
|
||||||
do_golden_comparison(golden_path, result_text, *result_data, output_type);
|
|
||||||
return result_data, result_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_semantic_analysis_test :: (file_path : string, output_type : Output_Type = 0) -> Result, Semantic_Check_Result {
|
|
||||||
lexer : Lexer;
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = file_path;
|
|
||||||
|
|
||||||
ok := read_input_from_file(*lexer, file_path);
|
|
||||||
if !ok {
|
|
||||||
log_error("Unable to read file: %\n", file_path);
|
|
||||||
result_data.type = .File_Read_Failed;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
lex_result := lex(*lexer, *temp);
|
|
||||||
if lex_result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
result_data.info_text = report_messages(lex_result.messages);
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
golden_path := get_golden_path(file_path, .Semantic_Analysis);
|
|
||||||
do_golden_comparison(golden_path, result_data.info_text, *result_data, output_type);
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_state : Parse_State;
|
|
||||||
result_data.stage = .Parser;
|
|
||||||
init_parse_state(*parse_state, lex_result.tokens, lexer.path);
|
|
||||||
|
|
||||||
parse_result := parse(*parse_state);
|
|
||||||
if parse_result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.info_text = report_messages(parse_result.messages);
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_path := get_golden_path(file_path, .Semantic_Analysis);
|
|
||||||
do_golden_comparison(golden_path, result_data.info_text, *result_data, output_type);
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
result, check_result := run_semantic_analysis_test(file_path, parse_state.result.root, output_type);
|
|
||||||
return result, check_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_semantic_analysis_test :: (file_path : string, root : *AST_Node, output_type : Output_Type = 0) -> Result, Semantic_Check_Result {
|
|
||||||
result_data : Result;
|
|
||||||
|
|
||||||
result_data.path = file_path;
|
|
||||||
result_data.stage = .Semantic_Analysis;
|
|
||||||
checker : Semantic_Checker;
|
|
||||||
init_semantic_checker(*checker, root, file_path);
|
|
||||||
result_text : string;
|
|
||||||
|
|
||||||
result := check(*checker);
|
|
||||||
if result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_text = report_messages(checker.result.messages);
|
|
||||||
} else {
|
|
||||||
result_text = pretty_print_symbol_table(*checker, temp);
|
|
||||||
constraints := pretty_print_type_constraints(*checker, temp);
|
|
||||||
type_vars := pretty_print_type_variables(*checker, temp);
|
|
||||||
// print("Constraints\n%\n", constraints);
|
|
||||||
// print("Solution\n%\n", type_vars);
|
|
||||||
}
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.info_text = result_text;
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_path := get_golden_path(checker.path, .Semantic_Analysis);
|
|
||||||
do_golden_comparison(golden_path, result_text, *result_data, output_type);
|
|
||||||
return result_data, result;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_codegen_test :: (path : string, root : *AST_Node, check_result : Semantic_Check_Result, output_type : Output_Type = 0) -> Result, Codegen_Result {
|
|
||||||
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = path;
|
|
||||||
result_data.stage = .Codegen;
|
|
||||||
|
|
||||||
state : Codegen_State;
|
|
||||||
init_codegen_state(*state, root, check_result, .HLSL);
|
|
||||||
|
|
||||||
result_text : string;
|
|
||||||
|
|
||||||
result := codegen(*state);
|
|
||||||
|
|
||||||
if result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.info_text = report_messages(result.messages);
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
result_text = result.result_text;
|
|
||||||
|
|
||||||
if output_type & .StdOut {
|
|
||||||
result_data.info_text = result_text;
|
|
||||||
result_data.type = .StdOut;
|
|
||||||
return result_data, result;
|
|
||||||
}
|
|
||||||
|
|
||||||
golden_path := get_golden_path(path, .Codegen);
|
|
||||||
do_golden_comparison(golden_path, result_text, *result_data, output_type);
|
|
||||||
return result_data, result;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_codegen_test :: (path : string, root : *AST_Node, output_type : Output_Type = 0) -> Result, Codegen_Result {
|
|
||||||
checker : Semantic_Checker;
|
|
||||||
init_semantic_checker(*checker, root, path);
|
|
||||||
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = path;
|
|
||||||
result_data.stage = .Semantic_Analysis;
|
|
||||||
|
|
||||||
check_result := check(*checker);
|
|
||||||
if check_result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.info_text = report_messages(check_result.messages);
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
result, codegen_result := run_codegen_test(path, root, check_result, output_type);
|
|
||||||
return result, codegen_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_codegen_test :: (path : string, output_type : Output_Type = 0) -> Result, Codegen_Result {
|
|
||||||
lexer : Lexer;
|
|
||||||
result_data : Result;
|
|
||||||
result_data.path = path;
|
|
||||||
|
|
||||||
ok := read_input_from_file(*lexer, path);
|
|
||||||
if !ok {
|
|
||||||
log_error("Unable to read file: %\n", path);
|
|
||||||
result_data.type = .File_Read_Failed;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
lex_result := lex(*lexer, *temp);
|
|
||||||
if lex_result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.stage = .Lexer;
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_state : Parse_State;
|
|
||||||
result_data.stage = .Parser;
|
|
||||||
init_parse_state(*parse_state, lex_result.tokens, lexer.path);
|
|
||||||
|
|
||||||
parse_result := parse(*parse_state);
|
|
||||||
if parse_result.had_error {
|
|
||||||
result_data.type = .Failed;
|
|
||||||
result_data.info_text = pretty_print_ast(parse_result.root, *temp);
|
|
||||||
return result_data, .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
result, codegen_result := run_codegen_test(path, parse_result.root, output_type);
|
|
||||||
return result, codegen_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_compile_test :: (path : string, output_type : Output_Type = 0) -> Result, Compilation_Result {
|
|
||||||
compiler : Shader_Compiler;
|
|
||||||
result : Result;
|
|
||||||
compilation_result := compile_file(*compiler, path);
|
|
||||||
if compilation_result.had_error {
|
|
||||||
result.type = .Failed;
|
|
||||||
result.info_text = tprint("Failed compiling: %\n", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, compilation_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
make_test_case :: (path : string, stage_flags : Stage_Flags, allocator := context.allocator) -> Test_Case {
|
|
||||||
test_case : Test_Case;
|
|
||||||
test_case.path = copy_string(path,, allocator);
|
|
||||||
replace_chars(test_case.path, "\\", #char "/");
|
|
||||||
test_case.stage_flags = stage_flags;
|
|
||||||
|
|
||||||
return test_case;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_test :: (file_path : string, stage_flags : Stage_Flags, results : *[..]Result, output_type : Output_Type = 0) {
|
|
||||||
lexer : Lexer;
|
|
||||||
result : Result;
|
|
||||||
if stage_flags & .Lexer {
|
|
||||||
result = run_lexer_test(file_path, *lexer, output_type);
|
|
||||||
record_result(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
root_node : *AST_Node;
|
|
||||||
if stage_flags & .Parser {
|
|
||||||
if stage_flags & .Lexer && result.type == .Passed || result.type == .Golden_Output {
|
|
||||||
result, root_node = run_parser_test(*lexer, output_type);
|
|
||||||
} else {
|
|
||||||
result, root_node = run_parser_test(file_path, output_type);
|
|
||||||
}
|
|
||||||
record_result(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
check_result : Semantic_Check_Result;
|
|
||||||
if stage_flags & .Semantic_Analysis {
|
|
||||||
if stage_flags & .Parser && (result.type == .Passed || result.type == .Golden_Output) {
|
|
||||||
result, check_result = run_semantic_analysis_test(file_path, root_node, output_type);
|
|
||||||
} else {
|
|
||||||
result, check_result = run_semantic_analysis_test(file_path, output_type);
|
|
||||||
}
|
|
||||||
record_result(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if stage_flags & .Codegen {
|
|
||||||
if stage_flags & .Semantic_Analysis && (result.type == .Passed || result.type == .Golden_Output) {
|
|
||||||
result = run_codegen_test(file_path, root_node, check_result, output_type);
|
|
||||||
} else if root_node {
|
|
||||||
result = run_codegen_test(file_path, root_node, output_type);
|
|
||||||
} else {
|
|
||||||
result = run_codegen_test(file_path, output_type);
|
|
||||||
}
|
|
||||||
record_result(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
if stage_flags & .Compile {
|
|
||||||
result = run_compile_test(file_path, output_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run_test :: (test_case : Test_Case, results : *[..]Result, output_type : Output_Type = 0) {
|
|
||||||
print("%Running test: %......", cyan(), test_case.path);
|
|
||||||
|
|
||||||
// path 30
|
|
||||||
// len 35
|
|
||||||
// == 5
|
|
||||||
|
|
||||||
|
|
||||||
// path 20
|
|
||||||
// len = 35
|
|
||||||
// == 15
|
|
||||||
|
|
||||||
len := 50;
|
|
||||||
rest := len - test_case.path.count;
|
|
||||||
for i: 0..rest {
|
|
||||||
print(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
run_test(test_case.path, test_case.stage_flags, results, output_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
record_result :: (results : *[..]Result, result : Result) {
|
|
||||||
array_add(results, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
run_test_suite :: (using suite : *Test_Suite, output_type : Output_Type = 0) {
|
|
||||||
if suite.name.count > 0 {
|
|
||||||
print("%Running suite: %\n", green(), suite.name);
|
|
||||||
print("%", reset_color());
|
|
||||||
}
|
|
||||||
|
|
||||||
Fail_Data :: struct {
|
|
||||||
path : string;
|
|
||||||
stage : string;
|
|
||||||
}
|
|
||||||
|
|
||||||
failed_test_paths : [..]Fail_Data;
|
|
||||||
failed_test_paths.allocator = temp;
|
|
||||||
|
|
||||||
builder : String_Builder;
|
|
||||||
init_string_builder(*builder,, temp);
|
|
||||||
|
|
||||||
for test_case : test_cases {
|
|
||||||
run_test(test_case, *suite.results, output_type);
|
|
||||||
|
|
||||||
for < suite.results {
|
|
||||||
result := suite.results[it_index];
|
|
||||||
if compare(result.path, test_case.path) == 0 {
|
|
||||||
if result.type == {
|
|
||||||
case .Failed; {
|
|
||||||
array_add(*failed_test_paths, .{ result.path, stage_to_string(result.stage) });
|
|
||||||
}
|
|
||||||
case .File_Read_Failed; {
|
|
||||||
array_add(*failed_test_paths, .{ result.path, "file not found" });
|
|
||||||
}
|
|
||||||
case .Golden_File_Not_Found; {
|
|
||||||
array_add(*failed_test_paths, .{ result.path, tprint("golden file not found for %", stage_to_string(result.stage)) });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evaluate_result(result);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// print("\n");
|
|
||||||
}
|
|
||||||
print("\n");
|
|
||||||
|
|
||||||
if output_type == 0 {
|
|
||||||
if failed_test_paths.count == 0 {
|
|
||||||
green(*builder);
|
|
||||||
print_to_builder(*builder, "All % tests passed!\n", test_cases.count);
|
|
||||||
reset_color(*builder);
|
|
||||||
} else {
|
|
||||||
print_to_builder(*builder, "%/% tests passed\n", test_cases.count - failed_test_paths.count, test_cases.count);
|
|
||||||
red(*builder);
|
|
||||||
|
|
||||||
print_to_builder(*builder, "% failed\n", failed_test_paths.count);
|
|
||||||
for failed_test : failed_test_paths {
|
|
||||||
print_to_builder(*builder, "% failed with error: %\n", failed_test.path, failed_test.stage);
|
|
||||||
}
|
|
||||||
reset_color(*builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print("%\n", builder_to_string(*builder));
|
|
||||||
}
|
|
||||||
|
|
||||||
read_suite :: (file_path : string, suite : *Test_Suite) -> bool {
|
|
||||||
bytes, ok := read_entire_file(file_path);
|
|
||||||
if !ok {
|
|
||||||
log_error("Unable to read suite file %\n", file_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
path := parse_path(file_path);
|
|
||||||
file_without_extension := split(path.words[path.words.count - 1], ".");
|
|
||||||
suite.name = copy_string(file_without_extension[0]);
|
|
||||||
split_lines := split(bytes, "\n");
|
|
||||||
|
|
||||||
for split_line : split_lines {
|
|
||||||
line := split(split_line, " ");
|
|
||||||
if line[0].count == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if line[0].data[0] == #char "#" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if line.count == 1 {
|
|
||||||
log_error("Invalid line - % - %\n", it_index + 1, line);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
test_case_path := line[0];
|
|
||||||
stage_flags : Stage_Flags;
|
|
||||||
|
|
||||||
for i: 0..line.count - 1 {
|
|
||||||
trimmed := trim(line[i]);
|
|
||||||
if equal(trimmed, "lex") {
|
|
||||||
stage_flags |= .Lexer;
|
|
||||||
} else if equal(trimmed, "parse") {
|
|
||||||
stage_flags |= .Parser;
|
|
||||||
} else if equal(trimmed, "semant") {
|
|
||||||
stage_flags |= .Semantic_Analysis;
|
|
||||||
} else if equal(trimmed, "codegen") {
|
|
||||||
stage_flags |= .Codegen;
|
|
||||||
} else if equal(trimmed, "compile") {
|
|
||||||
stage_flags |= .Compile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test_case := make_test_case(test_case_path, stage_flags);
|
|
||||||
array_add(*suite.test_cases, test_case);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_test :: () {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
stage_to_string :: (stage : Stage_Flags) -> string {
|
|
||||||
if #complete stage == {
|
|
||||||
case .Lexer; return "lexing";
|
|
||||||
case .Parser; return "parsing";
|
|
||||||
case .Semantic_Analysis; return "semantic checking";
|
|
||||||
case .Codegen; return "codegen";
|
|
||||||
case .Compile; return "compiled";
|
|
||||||
case; return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluate_result :: (result : Result) {
|
|
||||||
stage : string = stage_to_string(result.stage);
|
|
||||||
|
|
||||||
if #complete result.type == {
|
|
||||||
case .File_Read_Failed; {
|
|
||||||
print(" %", red());
|
|
||||||
print("failed with File_Read_Failed\n");
|
|
||||||
}
|
|
||||||
case .Golden_File_Not_Found; {
|
|
||||||
print(" %", red());
|
|
||||||
print("failed with Golden File Not Found for stage %\n", stage);
|
|
||||||
}
|
|
||||||
case .StdOut; {
|
|
||||||
}
|
|
||||||
case .Golden_Output; {
|
|
||||||
print(" %", yellow());
|
|
||||||
print("output new golden file at %\n", result.golden_path);
|
|
||||||
}
|
|
||||||
case .Passed; {
|
|
||||||
print(" %", green());
|
|
||||||
print("passed %\n", stage);
|
|
||||||
}
|
|
||||||
case .Failed; {
|
|
||||||
print(" %", red());
|
|
||||||
print("failed %\n", stage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.info_text.count > 0 {
|
|
||||||
print("%", cyan());
|
|
||||||
print("--- Info text ---\n");
|
|
||||||
print("%", yellow());
|
|
||||||
print("%\n", result.info_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
print("%", reset_color());
|
|
||||||
}
|
|
||||||
|
|
||||||
main :: () {
|
|
||||||
lexer : Lexer;
|
|
||||||
|
|
||||||
args := get_command_line_arguments();
|
|
||||||
|
|
||||||
suites : [..]Test_Suite;
|
|
||||||
output_type : Output_Type = 0;
|
|
||||||
|
|
||||||
Argument_Parse_State :: enum {
|
|
||||||
None;
|
|
||||||
Run_Suite;
|
|
||||||
Run_Test;
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_parse_state : Argument_Parse_State;
|
|
||||||
current_suite : *Test_Suite;
|
|
||||||
|
|
||||||
path : string;
|
|
||||||
|
|
||||||
for i: 1..args.count - 1 {
|
|
||||||
arg := args[i];
|
|
||||||
if arg == "-output-as-golden" {
|
|
||||||
output_type |= .Golden;
|
|
||||||
continue;
|
|
||||||
} else if arg == "-output" {
|
|
||||||
output_type |= .StdOut;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if arg_parse_state == {
|
|
||||||
case .Run_Suite; {
|
|
||||||
if arg == "-output-as-golden" {
|
|
||||||
output_type |= .Golden;
|
|
||||||
} else if arg == "-output" {
|
|
||||||
output_type |= .StdOut;
|
|
||||||
} else {
|
|
||||||
print("%Unknown argument %\n", red(), arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .Run_Test; {
|
|
||||||
cases := current_suite.test_cases.count;
|
|
||||||
if arg == "-lex" {
|
|
||||||
current_suite.test_cases[cases - 1].stage_flags |= .Lexer;
|
|
||||||
} else if arg == "-parse" {
|
|
||||||
current_suite.test_cases[cases - 1].stage_flags |= .Parser;
|
|
||||||
} else if arg == "-semant" {
|
|
||||||
current_suite.test_cases[cases - 1].stage_flags |= .Semantic_Analysis;
|
|
||||||
} else if arg == "-codegen" {
|
|
||||||
current_suite.test_cases[cases - 1].stage_flags |= .Codegen;
|
|
||||||
} else if arg == "-compile" {
|
|
||||||
current_suite.test_cases[cases - 1].stage_flags |= .Compile;
|
|
||||||
} else if contains(arg, ".") {
|
|
||||||
split_path := split(arg, ".");
|
|
||||||
extension := split_path[1];
|
|
||||||
if extension == SHADER_EXTENSION {
|
|
||||||
path := copy_string(arg);
|
|
||||||
test_case := make_test_case(path, 0);
|
|
||||||
array_add(*current_suite.test_cases, test_case);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
print("%Unknown argument %\n", red, arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .None; {
|
|
||||||
if contains(arg, ".") {
|
|
||||||
split_path := split(arg, ".");
|
|
||||||
extension := split_path[1];
|
|
||||||
|
|
||||||
if extension == SHADER_EXTENSION {
|
|
||||||
if arg_parse_state == .Run_Suite {
|
|
||||||
log_error("Unable to run a test while already running suite.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !current_suite {
|
|
||||||
suite : Test_Suite;
|
|
||||||
array_add(*suites, suite);
|
|
||||||
current_suite = *suites[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_parse_state = .Run_Test;
|
|
||||||
path := copy_string(arg);
|
|
||||||
test_case := make_test_case(path, 0);
|
|
||||||
array_add(*current_suite.test_cases, test_case);
|
|
||||||
} else if extension == SUITE_EXTENSION {
|
|
||||||
if arg_parse_state == .Run_Test {
|
|
||||||
log_error("Unable to run a suite while already running test.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
arg_parse_state = .Run_Suite;
|
|
||||||
path := copy_string(arg);
|
|
||||||
|
|
||||||
suite : Test_Suite;
|
|
||||||
read_suite(path, *suite);
|
|
||||||
array_add(*suites, suite);
|
|
||||||
current_suite = *suites[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for suite : suites {
|
|
||||||
run_test_suite(*suite, output_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -74,8 +74,24 @@ int4x4 :: struct {
|
|||||||
|
|
||||||
//~ nbr: Constructors
|
//~ nbr: Constructors
|
||||||
#foreign float2 :: (float, float) -> float2;
|
#foreign float2 :: (float, float) -> float2;
|
||||||
|
#foreign float2 :: (float2) -> float2;
|
||||||
|
#foreign float2 :: (float) -> float2;
|
||||||
|
|
||||||
#foreign float3 :: (float, float, float) -> float3;
|
#foreign float3 :: (float, float, float) -> float3;
|
||||||
|
#foreign float3 :: (float3) -> float3;
|
||||||
|
#foreign float3 :: (float2, float) -> float3;
|
||||||
|
#foreign float3 :: (float, float2) -> float3;
|
||||||
|
#foreign float3 :: (float) -> float3;
|
||||||
|
|
||||||
#foreign float4 :: (float, float, float, float) -> float4;
|
#foreign float4 :: (float, float, float, float) -> float4;
|
||||||
|
#foreign float4 :: (float4) -> float4;
|
||||||
|
#foreign float4 :: (float2, float2) -> float4;
|
||||||
|
#foreign float4 :: (float2, float, float) -> float4;
|
||||||
|
#foreign float4 :: (float, float2, float) -> float4;
|
||||||
|
#foreign float4 :: (float, float, float2) -> float4;
|
||||||
|
#foreign float4 :: (float3, float) -> float4;
|
||||||
|
#foreign float4 :: (float, float3) -> float4;
|
||||||
|
#foreign float4 :: (float) -> float4;
|
||||||
|
|
||||||
//~ nbr: Vectors
|
//~ nbr: Vectors
|
||||||
#foreign cross :: (float3, float3) -> float3;
|
#foreign cross :: (float3, float3) -> float3;
|
||||||
@@ -83,6 +99,10 @@ int4x4 :: struct {
|
|||||||
#foreign distance :: (float3, float3) -> float;
|
#foreign distance :: (float3, float3) -> float;
|
||||||
#foreign distance :: (float4, float4) -> float;
|
#foreign distance :: (float4, float4) -> float;
|
||||||
|
|
||||||
|
#foreign length :: (float2) -> float;
|
||||||
|
#foreign length :: (float3) -> float;
|
||||||
|
#foreign length :: (float4) -> float;
|
||||||
|
|
||||||
#foreign dot :: (float2, float2) -> float;
|
#foreign dot :: (float2, float2) -> float;
|
||||||
#foreign dot :: (float3, float3) -> float;
|
#foreign dot :: (float3, float3) -> float;
|
||||||
#foreign dot :: (float4, float4) -> float;
|
#foreign dot :: (float4, float4) -> float;
|
||||||
@@ -260,3 +280,10 @@ int4x4 :: struct {
|
|||||||
#foreign atan2 :: (float4x4, float4x4) -> float4x4;
|
#foreign atan2 :: (float4x4, float4x4) -> float4x4;
|
||||||
|
|
||||||
#foreign sample :: (Texture2D, Sampler, float2) -> float4;
|
#foreign sample :: (Texture2D, Sampler, float2) -> float4;
|
||||||
|
|
||||||
|
#foreign lerp :: (float, float, float) -> float;
|
||||||
|
#foreign lerp :: (float2, float2, float) -> float2;
|
||||||
|
#foreign lerp :: (float3, float3, float) -> float3;
|
||||||
|
#foreign lerp :: (float4, float4, float) -> float4;
|
||||||
|
|
||||||
|
#foreign fmod :: (float, float) -> float;
|
||||||
81
first.jai
81
first.jai
@@ -1,21 +1,65 @@
|
|||||||
#import "Basic";
|
#import "Basic";
|
||||||
#import "File";
|
#import "File";
|
||||||
#import "Compiler";
|
#import "Compiler";
|
||||||
|
#import "Metaprogram_Plugins";
|
||||||
|
|
||||||
|
plugins: [..] *Metaprogram_Plugin;
|
||||||
|
|
||||||
build :: () {
|
build :: () {
|
||||||
w := compiler_create_workspace("Shader Compiler Test Build");
|
w := compiler_create_workspace("Ink Build");
|
||||||
if !w {
|
if !w {
|
||||||
print("Workspace creation failed.\n");
|
print("Workspace creation failed.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXECUTABLE_NAME :: "test";
|
EXECUTABLE_NAME :: "ink";
|
||||||
MAIN_FILE :: "Test.jai";
|
MAIN_FILE :: "Ink.jai";
|
||||||
|
|
||||||
options := get_build_options(w);
|
options := get_build_options(w);
|
||||||
|
|
||||||
options.write_added_strings = true;
|
options.write_added_strings = true;
|
||||||
|
|
||||||
|
args := options.compile_time_command_line;
|
||||||
|
|
||||||
|
intercept_flags: Intercept_Flags;
|
||||||
|
plugin_start_index := -1;
|
||||||
|
|
||||||
|
for arg : args {
|
||||||
|
if arg == {
|
||||||
|
case "check"; {
|
||||||
|
options.output_type = .NO_OUTPUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it := args[it_index];
|
||||||
|
|
||||||
|
if !it continue;
|
||||||
|
|
||||||
|
if it[0] == #char "+" {
|
||||||
|
plugin_start_index = it_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins_to_create: [..] Plugin_To_Create;
|
||||||
|
tracy : Plugin_To_Create;
|
||||||
|
tracy.name = "tracy";
|
||||||
|
array_add(*plugins_to_create, tracy);
|
||||||
|
// got_error := false;
|
||||||
|
// if plugin_start_index >= 0 {
|
||||||
|
// success:, plugins_to_create = parse_plugin_arguments(args, plugin_start_index);
|
||||||
|
// if !success got_error = true;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if got_error {
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
|
||||||
|
success := init_plugins(plugins_to_create, *plugins, w);
|
||||||
|
if !success {
|
||||||
|
log_error("A plugin init() failed. Exiting.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
new_path: [..] string;
|
new_path: [..] string;
|
||||||
array_add(*new_path, ..options.import_path);
|
array_add(*new_path, ..options.import_path);
|
||||||
array_add(*new_path, "modules");
|
array_add(*new_path, "modules");
|
||||||
@@ -25,18 +69,41 @@ build :: () {
|
|||||||
|
|
||||||
wd := get_working_directory();
|
wd := get_working_directory();
|
||||||
|
|
||||||
|
|
||||||
set_build_options(options, w);
|
set_build_options(options, w);
|
||||||
|
|
||||||
compiler_begin_intercept(w);
|
for plugins {
|
||||||
|
if it.before_intercept it.before_intercept(it, *intercept_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
compiler_begin_intercept(w, intercept_flags);
|
||||||
|
|
||||||
|
for plugins if it.add_source it.add_source(it);
|
||||||
|
|
||||||
add_build_file(MAIN_FILE, w);
|
add_build_file(MAIN_FILE, w);
|
||||||
|
|
||||||
|
// Call message_loop(), which is a routine of ours below that will receive the messages.
|
||||||
|
message_loop(w);
|
||||||
|
|
||||||
compiler_end_intercept(w);
|
compiler_end_intercept(w);
|
||||||
|
|
||||||
|
for plugins if it.finish it.finish (it);
|
||||||
|
for plugins if it.shutdown it.shutdown(it);
|
||||||
|
|
||||||
print("\nDone!\n\n");
|
print("\nDone!\n\n");
|
||||||
|
|
||||||
set_build_options_dc(.{do_output=false});
|
set_build_options_dc(.{do_output=false, write_added_strings=false});
|
||||||
}
|
}
|
||||||
|
|
||||||
#run build();
|
message_loop :: (w: Workspace) {
|
||||||
|
while true {
|
||||||
|
// We ask the compiler for the next message. If one is not available,
|
||||||
|
// we will wait until it becomes available.
|
||||||
|
message := compiler_wait_for_message();
|
||||||
|
// Pass the message to all plugins.
|
||||||
|
for plugins if it.message it.message(it, message);
|
||||||
|
|
||||||
|
if message.kind == .COMPLETE break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#run, stallable build();
|
||||||
|
|||||||
407
module.jai
407
module.jai
@@ -1,9 +1,28 @@
|
|||||||
#load "Lexing.jai";
|
#load "Lexing.jai";
|
||||||
#load "Error.jai";
|
#load "Error.jai";
|
||||||
#load "Parsing.jai";
|
#load "Parsing.jai";
|
||||||
#load "Semantic_Analysis.jai";
|
#load "Check.jai";
|
||||||
#load "Codegen.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
|
||||||
|
- [ ] Remove properties block and just use hinted constant buffers instead
|
||||||
|
```
|
||||||
|
props :: constant_buffer @properties {
|
||||||
|
[...]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- [ ] while loops
|
||||||
|
- [ ] for-each loops
|
||||||
|
- [ ]
|
||||||
|
*/
|
||||||
|
|
||||||
add_define :: (env : *Environment, key : string) {
|
add_define :: (env : *Environment, key : string) {
|
||||||
for define : env.defines {
|
for define : env.defines {
|
||||||
@@ -27,10 +46,6 @@ Environment :: struct {
|
|||||||
defines : [..]string;
|
defines : [..]string;
|
||||||
}
|
}
|
||||||
|
|
||||||
Shader_Compiler :: struct {
|
|
||||||
environment : Environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
Field_Kind :: enum {
|
Field_Kind :: enum {
|
||||||
Int :: 0;
|
Int :: 0;
|
||||||
Half :: 1;
|
Half :: 1;
|
||||||
@@ -58,10 +73,33 @@ Hint_Kind :: enum {
|
|||||||
Position;
|
Position;
|
||||||
UV;
|
UV;
|
||||||
Target;
|
Target;
|
||||||
|
Output_Position;
|
||||||
|
|
||||||
Custom;
|
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 {
|
Field_Hint :: struct {
|
||||||
kind : Hint_Kind;
|
kind : Hint_Kind;
|
||||||
|
|
||||||
@@ -84,22 +122,6 @@ Entry_Point :: struct {
|
|||||||
return_value : 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 {
|
Property_Field :: struct {
|
||||||
base_field : Field;
|
base_field : Field;
|
||||||
|
|
||||||
@@ -114,92 +136,117 @@ Properties :: struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Constant_Buffer :: struct {
|
Constant_Buffer :: struct {
|
||||||
register : int;
|
|
||||||
|
|
||||||
name : string;
|
name : string;
|
||||||
|
|
||||||
fields : Static_Array(Property_Field, 16);
|
fields : Static_Array(Property_Field, 16);
|
||||||
|
|
||||||
|
hints : [..]Field_Hint;
|
||||||
|
|
||||||
buffer_index : u32;
|
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 {
|
Input_File :: struct {
|
||||||
source : string;
|
source : string;
|
||||||
path : string;
|
path : string;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token_Stream :: struct {
|
Compiler_Context :: struct {
|
||||||
tokens : [..]Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
Compiled_File :: struct {
|
|
||||||
file : Input_File;
|
file : Input_File;
|
||||||
tokens : Token_Stream;
|
|
||||||
ast_root : *AST_Node;
|
|
||||||
ast_nodes : [..]AST_Node;
|
|
||||||
}
|
|
||||||
|
|
||||||
Compile_Result :: struct {
|
environment : Environment;
|
||||||
files : [..]Compiled_File;
|
|
||||||
|
tokens : [..]Token;;
|
||||||
|
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; //@Note(niels): We'll deprecate this in favor of just marking a constant buffer with a hint on how you'd want to use it
|
||||||
|
|
||||||
|
max_constant_buffers :: 16;
|
||||||
|
|
||||||
|
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
|
||||||
|
|
||||||
had_error : bool;
|
had_error : bool;
|
||||||
messages : [..]Compiler_Message;
|
messages : [..]Compiler_Message;
|
||||||
|
|
||||||
allocator : Allocator;
|
|
||||||
arena : Arena;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//@Incomplete(niels): need to consider allocation
|
#add_context scratch_allocators : [2]Allocator;
|
||||||
add_file :: (result : *Compile_Result, path : string) {
|
#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);
|
file_string, ok := read_entire_file(path);
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
// record_error(.File_Load_Failed, "Unable to load file: %", path);
|
record_error(result, "Unable to load file: %", path);
|
||||||
return;
|
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 : Input_File;
|
||||||
|
|
||||||
input_file.source = file_string;
|
input_file.source = source;
|
||||||
input_file.path = path;
|
input_file.path = path;
|
||||||
|
|
||||||
compiled_file : Compiled_File;
|
return input_file;
|
||||||
compiled_file.file = input_file;
|
|
||||||
|
|
||||||
array_add(*result.files, compiled_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Incomplete(nb): Will we ever even use this?
|
|
||||||
from_file :: (path : string) -> Compile_Result {
|
|
||||||
arr : [1]string;
|
|
||||||
arr[0] = path;
|
|
||||||
return from_files(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
from_files :: (paths : []string) -> Compile_Result {
|
|
||||||
result : Compile_Result;
|
|
||||||
for path : paths {
|
|
||||||
add_file(*result, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Compilation_Result :: struct {
|
|
||||||
messages : [..]Compiler_Message;
|
|
||||||
|
|
||||||
had_error : bool;
|
|
||||||
|
|
||||||
collection : Shader_Variant_Collection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pretty_print_field :: (field : *Field) -> string {
|
pretty_print_field :: (field : *Field) -> string {
|
||||||
@@ -278,11 +325,11 @@ pretty_print_field :: (builder : *String_Builder, field : *Field) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type_variable_to_field :: (checker : *Semantic_Checker, variable : Type_Variable_Handle) -> Field {
|
type_variable_to_field :: (checker : *Checker, variable : Type_Variable_Handle) -> Field {
|
||||||
return type_variable_to_field(checker, h2tv(checker, variable));
|
return type_variable_to_field(checker, from_handle(checker, variable));
|
||||||
}
|
}
|
||||||
|
|
||||||
type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variable) -> Field {
|
type_variable_to_field :: (type_variables : []Type_Variable, scope_stack : Scope_Stack, variable : *Type_Variable) -> Field {
|
||||||
field : Field;
|
field : Field;
|
||||||
|
|
||||||
field.name = variable.name;
|
field.name = variable.name;
|
||||||
@@ -315,14 +362,14 @@ type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variabl
|
|||||||
case .Struct; {
|
case .Struct; {
|
||||||
type.kind = Field_Kind.Struct;
|
type.kind = Field_Kind.Struct;
|
||||||
|
|
||||||
find_result := find_symbol(checker, variable.typename, xx 1);
|
find_result := find_symbol(scope_stack, variable.typename, xx 1);
|
||||||
assert(find_result != null, "Internal compiler error\n");
|
assert(find_result != null, "Internal compiler error\n");
|
||||||
|
|
||||||
type_var := h2tv(checker, find_result.type_variable);
|
type_var := from_handle(type_variables, find_result.type_variable);
|
||||||
|
|
||||||
for i : 0..type_var.children.count - 1 {
|
for i : 0..type_var.children.count - 1 {
|
||||||
child := type_var.children[i];
|
child := type_var.children[i];
|
||||||
child_field := type_variable_to_field(checker, h2tv(checker, child));
|
child_field := type_variable_to_field(type_variables, scope_stack, child);
|
||||||
array_add(*type.children, child_field);
|
array_add(*type.children, child_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -333,13 +380,11 @@ type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variabl
|
|||||||
for hint : variable.source_node.hint_tokens {
|
for hint : variable.source_node.hint_tokens {
|
||||||
field_hint : Field_Hint;
|
field_hint : Field_Hint;
|
||||||
|
|
||||||
if hint.ident_value == "position" {
|
if lookup_hint(hint.ident_value) == .Position {
|
||||||
// @Incomplete(nb): Should be a lookup table somewhere
|
|
||||||
field_hint.kind = .Position;
|
field_hint.kind = .Position;
|
||||||
} else if hint.ident_value == "uv" {
|
} else if lookup_hint(hint.ident_value) == .UV {
|
||||||
field_hint.kind = .UV;
|
field_hint.kind = .UV;
|
||||||
} else if starts_with(hint.ident_value, "target") {
|
} else if lookup_hint(hint.ident_value) == .Target {
|
||||||
// @Incomplete(nb): Should be a lookup table somewhere
|
|
||||||
index_str : string;
|
index_str : string;
|
||||||
index_str.data = *hint.ident_value.data[7];
|
index_str.data = *hint.ident_value.data[7];
|
||||||
index_str.count = 1;
|
index_str.count = 1;
|
||||||
@@ -350,7 +395,8 @@ type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variabl
|
|||||||
}
|
}
|
||||||
field_hint.kind = .Target;
|
field_hint.kind = .Target;
|
||||||
} else {
|
} else {
|
||||||
// @Incomplete(nb): custo hints
|
field_hint.custom_hint_name = hint.ident_value;
|
||||||
|
field_hint.kind = .Custom;
|
||||||
}
|
}
|
||||||
array_add(*field.hints, field_hint);
|
array_add(*field.hints, field_hint);
|
||||||
}
|
}
|
||||||
@@ -360,82 +406,23 @@ type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variabl
|
|||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_file :: (compiler : *Shader_Compiler, paths : []string) -> Compile_Result {
|
type_variable_to_field :: (type_variables : []Type_Variable, scope_stack : Scope_Stack, variable : Type_Variable_Handle) -> Field {
|
||||||
result : Compile_Result;
|
return type_variable_to_field(type_variables, scope_stack, from_handle(type_variables, variable));
|
||||||
|
|
||||||
for path : paths {
|
|
||||||
add_file(*result, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
lex(*result);
|
|
||||||
// parse(*result);
|
|
||||||
// check(*result);
|
|
||||||
// codegen(*result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_file :: (compiler : *Shader_Compiler, path : string) -> Compilation_Result {
|
type_variable_to_field :: (checker : *Checker, variable : *Type_Variable) -> Field {
|
||||||
result : Compilation_Result;
|
return type_variable_to_field(checker.ctx.type_variables, checker.ctx.scope_stack, variable);
|
||||||
|
}
|
||||||
|
|
||||||
lexer : Lexer;
|
generate_output_data :: (ctx : *Compiler_Context) {
|
||||||
|
if ctx.had_error {
|
||||||
init_lexer_from_file(*lexer, path);
|
return;
|
||||||
if lexer.result.had_error {
|
|
||||||
copy_messages(lexer.result.messages, *result.messages);
|
|
||||||
result.had_error = true;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lex_result := lex(*lexer,, *temp);
|
if ctx.vertex_entry_point.node {
|
||||||
if lex_result.had_error {
|
ctx.vertex_entry_point.name = ctx.vertex_entry_point.node.name;
|
||||||
copy_messages(lex_result.messages, *result.messages);
|
|
||||||
result.had_error = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_state : Parse_State;
|
type_variable := from_handle(ctx.type_variables, ctx.vertex_entry_point.node.type_variable);
|
||||||
init_parse_state(*parse_state, lex_result.tokens, lexer.path);
|
|
||||||
|
|
||||||
parse_result := parse(*parse_state);
|
|
||||||
if parse_result.had_error {
|
|
||||||
copy_messages(parse_result.messages, *result.messages);
|
|
||||||
result.had_error = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
checker : Semantic_Checker;
|
|
||||||
init_semantic_checker(*checker, parse_result.root, path);
|
|
||||||
|
|
||||||
check_result := check(*checker);
|
|
||||||
if check_result.had_error {
|
|
||||||
copy_messages(check_result.messages, *result.messages);
|
|
||||||
result.had_error = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
state : Codegen_State;
|
|
||||||
init_codegen_state(*state, parse_result.root, check_result, .HLSL);
|
|
||||||
|
|
||||||
result_text : string;
|
|
||||||
|
|
||||||
codegen_result := codegen(*state);
|
|
||||||
if codegen_result.had_error {
|
|
||||||
copy_messages(codegen_result.messages, *result.messages);
|
|
||||||
result.had_error = true;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @Incomplete(nb): Assumes only a single variant for now
|
|
||||||
|
|
||||||
variant : Shader_Variant;
|
|
||||||
variant.text = codegen_result.result_text;
|
|
||||||
|
|
||||||
if checker.result.vertex_entry_point {
|
|
||||||
variant.vertex_entry_point.name = checker.result.vertex_entry_point.name;
|
|
||||||
|
|
||||||
type_variable := h2tv(*checker, checker.result.vertex_entry_point.type_variable);
|
|
||||||
assert(type_variable.type == .Function);
|
assert(type_variable.type == .Function);
|
||||||
|
|
||||||
node := type_variable.source_node;
|
node := type_variable.source_node;
|
||||||
@@ -443,77 +430,97 @@ compile_file :: (compiler : *Shader_Compiler, path : string) -> Compilation_Resu
|
|||||||
if node.children[0].kind == .FieldList {
|
if node.children[0].kind == .FieldList {
|
||||||
field_list := node.children[0];
|
field_list := node.children[0];
|
||||||
for child : field_list.children {
|
for child : field_list.children {
|
||||||
tv := h2tv(*checker, child.type_variable);
|
tv := from_handle(ctx.type_variables, child.type_variable);
|
||||||
field := type_variable_to_field(*checker, tv);
|
field := type_variable_to_field(ctx.type_variables, ctx.scope_stack, tv);
|
||||||
array_add(*variant.vertex_entry_point.input, field);
|
array_add(*ctx.vertex_entry_point.input, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for buffer_variable : to_array(*check_result.constant_buffers) {
|
for buffer_variable : ctx.constant_buffers {
|
||||||
variable := h2tv(check_result.type_variables, buffer_variable);
|
variable := from_handle(ctx.type_variables, buffer_variable);
|
||||||
|
|
||||||
cb := array_add(*result.collection.cbuffers);
|
cb := array_add(*ctx.cbuffers);
|
||||||
|
|
||||||
for i : 0..variable.children.count - 1 {
|
for i : 0..variable.children.count - 1 {
|
||||||
child := variable.children[i];
|
child := variable.children[i];
|
||||||
field : Property_Field;
|
field : Property_Field;
|
||||||
field.base_field = type_variable_to_field(*checker, h2tv(*checker, child));;
|
field.base_field = type_variable_to_field(ctx.type_variables, ctx.scope_stack, from_handle(ctx.type_variables, child));
|
||||||
array_add(*cb.fields, field);
|
array_add(*cb.fields, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
cb.buffer_index = variable.resource_index;
|
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(*check_result.scope_stack, check_result.property_name, xx 1);
|
find_result := find_symbol(*ctx.scope_stack, ctx.property_name, xx 1);
|
||||||
if find_result {
|
if find_result {
|
||||||
property_variable := h2tv(check_result.type_variables, find_result.type_variable);
|
|
||||||
|
property_variable := from_handle(ctx.type_variables, find_result.type_variable);
|
||||||
|
|
||||||
for i : 0..property_variable.children.count - 1 {
|
for i : 0..property_variable.children.count - 1 {
|
||||||
child := property_variable.children[i];
|
child := property_variable.children[i];
|
||||||
field := type_variable_to_field(*checker, h2tv(*checker, child));
|
field := type_variable_to_field(ctx.type_variables, ctx.scope_stack, from_handle(ctx.type_variables, child));
|
||||||
prop_field : Property_Field;
|
prop_field : Property_Field;
|
||||||
prop_field.base_field = field;
|
prop_field.base_field = field;
|
||||||
array_add(*result.collection.properties.fields, prop_field);
|
array_add(*ctx.properties.fields, prop_field);
|
||||||
}
|
}
|
||||||
result.collection.properties.buffer_index = property_variable.resource_index;
|
ctx.properties.buffer_index = property_variable.resource_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if checker.result.pixel_entry_point {
|
if ctx.pixel_entry_point.node {
|
||||||
variant.pixel_entry_point.name = checker.result.pixel_entry_point.name;
|
ctx.pixel_entry_point.name = ctx.pixel_entry_point.node.name;
|
||||||
|
|
||||||
type_variable := h2tv(*checker, checker.result.pixel_entry_point.type_variable);
|
type_variable := from_handle(ctx.type_variables, ctx.pixel_entry_point.node.type_variable);
|
||||||
assert(type_variable.type == .Function);
|
assert(type_variable.type == .Function);
|
||||||
|
|
||||||
field := type_variable_to_field(*checker, type_variable.return_type_variable);
|
if type_variable.return_type_variable > 0 {
|
||||||
for hint : type_variable.source_node.hint_tokens {
|
field := type_variable_to_field(ctx.type_variables, ctx.scope_stack, type_variable.return_type_variable);
|
||||||
field_hint : Field_Hint;
|
for hint : type_variable.source_node.hint_tokens {
|
||||||
|
field_hint : Field_Hint;
|
||||||
|
|
||||||
if hint.ident_value == "position" {
|
if lookup_hint(hint.ident_value) == .Position {
|
||||||
// @Incomplete(nb): Should be a lookup table somewhere
|
field_hint.kind = .Position;
|
||||||
field_hint.kind = .Position;
|
} else if lookup_hint(hint.ident_value) == .Target {
|
||||||
} else if starts_with(hint.ident_value, "target") {
|
index_str : string;
|
||||||
// @Incomplete(nb): Should be a lookup table somewhere
|
index_str.data = *hint.ident_value.data[7];
|
||||||
index_str : string;
|
index_str.count = 1;
|
||||||
index_str.data = *hint.ident_value.data[7];
|
|
||||||
index_str.count = 1;
|
|
||||||
|
|
||||||
result, ok, remainder := string_to_int(index_str);
|
result, ok, remainder := string_to_int(index_str);
|
||||||
if ok {
|
if ok {
|
||||||
field_hint.target_index = result;
|
field_hint.target_index = result;
|
||||||
|
}
|
||||||
|
field_hint.kind = .Target;
|
||||||
|
} else {
|
||||||
|
// @Incomplete(nb): custom hints
|
||||||
}
|
}
|
||||||
field_hint.kind = .Target;
|
array_add(*field.hints, field_hint);
|
||||||
} else {
|
|
||||||
// @Incomplete(nb): custo hints
|
|
||||||
}
|
}
|
||||||
array_add(*field.hints, field_hint);
|
ctx.pixel_entry_point.return_value = field;
|
||||||
}
|
}
|
||||||
|
|
||||||
variant.pixel_entry_point.return_value = field;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
array_add(*result.collection.variants, variant);
|
|
||||||
|
compile_file :: (ctx : *Compiler_Context, path : string, allocator : Allocator = temp) {
|
||||||
return result;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Submodule modules/ncore updated: 9db7ff0940...788980c88f
1
modules/tracy
Submodule
1
modules/tracy
Submodule
Submodule modules/tracy added at 9668d7b8ab
BIN
output.tracy
Normal file
BIN
output.tracy
Normal file
Binary file not shown.
@@ -1,12 +1,12 @@
|
|||||||
test/assign_arithmetic_expression.shd lex parse
|
test/assign_arithmetic_expression.inx lex parse
|
||||||
test/empty_vertex_main.shd lex parse
|
test/empty_vertex_main.inx lex parse
|
||||||
test/empty_vertex_main_with_position_parameter.shd lex parse
|
test/empty_vertex_main_with_position_parameter.inx lex parse
|
||||||
test/meta_block.shd lex parse
|
test/meta_block.inx lex parse
|
||||||
test/basic_property_and_return_value.shd lex parse
|
test/basic_property_and_return_value.inx lex parse
|
||||||
test/function_call_return.shd lex parse
|
test/function_call_return.inx lex parse
|
||||||
test/struct_field_access_test.shd lex parse
|
test/struct_field_access_test.inx lex parse
|
||||||
test/pass_and_access_struct_fields_in_functions.shd lex parse
|
test/pass_and_access_struct_fields_in_functions.inx lex parse
|
||||||
test/field_without_type_specifier.shd lex parse
|
test/field_without_type_specifier.inx lex parse
|
||||||
test/functions_with_same_name.shd lex parse
|
test/functions_with_same_name.inx lex parse
|
||||||
test/function_with_int_return.shd lex parse
|
test/function_with_int_return.inx lex parse
|
||||||
test/type_as_variable_name.shd lex parse
|
test/type_as_variable_name.inx lex parse
|
||||||
8
test/binary_with_access.ink
Normal file
8
test/binary_with_access.ink
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
props :: properties {
|
||||||
|
resolution : float2;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex main :: (pos : float3 @position) -> float4 @position {
|
||||||
|
p := float2(1.0 - 2.0 * props.resolution.x, 1.0 - 2.0 * props.resolution.y);
|
||||||
|
return float4(p, 1.0, 1.0);
|
||||||
|
}
|
||||||
34
test/builtin_types.ink
Normal file
34
test/builtin_types.ink
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
vertex main :: () {
|
||||||
|
v2 : float2 = float2(2.0, 2.0);
|
||||||
|
v2 = float2(2.0);
|
||||||
|
v2 = float2(v2);
|
||||||
|
|
||||||
|
v3 : float3 = float3(2.0, 2.0, 2.0);
|
||||||
|
v3 = float3(v2, 1.0);
|
||||||
|
v3 = float3(1.0, v2);
|
||||||
|
v3 = float3(1.0);
|
||||||
|
v3 = float3(v3);
|
||||||
|
|
||||||
|
v4 : float4 = float4(2.0, 2.0, 2.0, 2.0);
|
||||||
|
v4 = float4(v4);
|
||||||
|
v4 = float4(v2, v2);
|
||||||
|
v4 = float4(v2, 1.0, 1.0);
|
||||||
|
v4 = float4(1.0, v2, 1.0);
|
||||||
|
v4 = float4(1.0, 1.0, v2);
|
||||||
|
v4 = float4(v3, 2.0);
|
||||||
|
v4 = float4(2.0, v3);
|
||||||
|
v4 = float4(2.0);
|
||||||
|
|
||||||
|
|
||||||
|
v4 = float4(1.0, 1.0, v2);
|
||||||
|
v4 = float4(2.0);
|
||||||
|
|
||||||
|
v2.x = 2.0;
|
||||||
|
v2.y = 2.0;
|
||||||
|
|
||||||
|
p := v2.x + v3.z;
|
||||||
|
q := v4.w + v2.x;
|
||||||
|
|
||||||
|
m : float4x4;
|
||||||
|
|
||||||
|
}
|
||||||
11
test/check/builtin_types.golden
Normal file
11
test/check/builtin_types.golden
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : ()
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[v2] : float2
|
||||||
|
[v4] : float4
|
||||||
|
[v3] : float3
|
||||||
|
[p] : float
|
||||||
|
[m] : float4x4
|
||||||
|
[q] : float
|
||||||
|
]
|
||||||
|
]
|
||||||
15
test/check/custom_hint.golden
Normal file
15
test/check/custom_hint.golden
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
scope (global) [
|
||||||
|
[pixel__ps_main] : (pos : float4) -> float4
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
[p] : {time : float}
|
||||||
|
scope (p) [
|
||||||
|
[time] : float
|
||||||
|
]
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float3
|
||||||
|
]
|
||||||
|
scope (pixel__ps_main) [
|
||||||
|
[t] : float
|
||||||
|
[pos] : float4
|
||||||
|
]
|
||||||
|
]
|
||||||
6
test/check/float_if_cond.golden
Normal file
6
test/check/float_if_cond.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[1;37mtest/float_if_cond.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||||
|
[96mif 1.0
|
||||||
|
^^^
|
||||||
|
1.0 has type float
|
||||||
|
|
||||||
|
[36m[37m
|
||||||
7
test/check/for_i_loop.golden
Normal file
7
test/check/for_i_loop.golden
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : ()
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[i] : int
|
||||||
|
[x] : int
|
||||||
|
]
|
||||||
|
]
|
||||||
8
test/check/functions_with_same_name.golden
Normal file
8
test/check/functions_with_same_name.golden
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[1;37mtest/functions_with_same_name.ink:2,0: [31merror: [37mRedeclaration of 'foo'
|
||||||
|
[96m foo :: () {
|
||||||
|
^^^
|
||||||
|
|
||||||
|
[97mtest/functions_with_same_name.ink:1,0: info: Here is the first declaration of 'foo'
|
||||||
|
[96m foo :: () {
|
||||||
|
^^^
|
||||||
|
[36m[37m
|
||||||
6
test/check/if_cond_assign.golden
Normal file
6
test/check/if_cond_assign.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[1;37mtest/if_cond_assign.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||||
|
[96mif 0 = 100
|
||||||
|
^^^^^^
|
||||||
|
if 0 = 100 { has type int
|
||||||
|
|
||||||
|
[36m[37m
|
||||||
7
test/check/if_def_block.golden
Normal file
7
test/check/if_def_block.golden
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
scope (global) [
|
||||||
|
[pixel__ps_main] : ()
|
||||||
|
scope (pixel__ps_main) [
|
||||||
|
[alpha_color] : float4
|
||||||
|
[f] : float
|
||||||
|
]
|
||||||
|
]
|
||||||
4
test/check/if_def_expression.golden
Normal file
4
test/check/if_def_expression.golden
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_console_main] : ()
|
||||||
|
scope (vertex__vs_console_main) []
|
||||||
|
]
|
||||||
8
test/check/ifdefs.golden
Normal file
8
test/check/ifdefs.golden
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_skinning_main] : ()
|
||||||
|
[pixel__ps_main] : ()
|
||||||
|
scope (vertex__vs_skinning_main) [
|
||||||
|
[x] : float
|
||||||
|
]
|
||||||
|
scope (pixel__ps_main) []
|
||||||
|
]
|
||||||
15
test/check/inferred_types.golden
Normal file
15
test/check/inferred_types.golden
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
scope (global) [
|
||||||
|
[foo] : () -> float
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
[bar] : () -> float
|
||||||
|
scope (bar) []
|
||||||
|
scope (foo) []
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[v2] : float2
|
||||||
|
[i] : int
|
||||||
|
[v4] : float4
|
||||||
|
[pos] : float3
|
||||||
|
[v3] : float3
|
||||||
|
[f] : float
|
||||||
|
]
|
||||||
|
]
|
||||||
6
test/check/nested_if.golden
Normal file
6
test/check/nested_if.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float3
|
||||||
|
]
|
||||||
|
]
|
||||||
6
test/check/non_bool_cond.golden
Normal file
6
test/check/non_bool_cond.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[1;37mtest/non_bool_cond.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||||
|
[96mif 1.0
|
||||||
|
^^^
|
||||||
|
1.0 has type float
|
||||||
|
|
||||||
|
[36m[37m
|
||||||
12
test/check/property_rename.golden
Normal file
12
test/check/property_rename.golden
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
scope (global) [
|
||||||
|
[pixel__ps_main] : () -> float4
|
||||||
|
[vertex__vs_main] : (pos : float4) -> float4
|
||||||
|
[props] : {color : float4}
|
||||||
|
scope (props) [
|
||||||
|
[color] : float4
|
||||||
|
]
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float4
|
||||||
|
]
|
||||||
|
scope (pixel__ps_main) []
|
||||||
|
]
|
||||||
8
test/check/redeclared_variable.golden
Normal file
8
test/check/redeclared_variable.golden
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[1;37mtest/redeclared_variable.ink:3,0: [31merror: [37mRedeclaration of 'x'
|
||||||
|
[96m x : float = 5.0
|
||||||
|
^
|
||||||
|
|
||||||
|
[97mtest/redeclared_variable.ink:2,0: info: Here is the first declaration of 'x'
|
||||||
|
[96m x : float = 1.0
|
||||||
|
^
|
||||||
|
[36m[37m
|
||||||
6
test/check/simple_else_if.golden
Normal file
6
test/check/simple_else_if.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float3
|
||||||
|
]
|
||||||
|
]
|
||||||
6
test/check/simple_if.golden
Normal file
6
test/check/simple_if.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float3
|
||||||
|
]
|
||||||
|
]
|
||||||
6
test/check/simple_if_else.golden
Normal file
6
test/check/simple_if_else.golden
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
scope (global) [
|
||||||
|
[vertex__vs_main] : (pos : float3) -> float4
|
||||||
|
scope (vertex__vs_main) [
|
||||||
|
[pos] : float3
|
||||||
|
]
|
||||||
|
]
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[1;37mtest/struct_access_primitive_type.shd:3,0: [31merror: [37mAttempting to access a field on a primitive type 'int'.
|
[1;37mtest/struct_access_primitive_type.ink:3,0: [31merror: [37mAttempting to access a field on a primitive type 'int'.
|
||||||
[96mx.d = 4;
|
[96mx.d = 4;
|
||||||
^
|
^
|
||||||
declaration:
|
declaration:
|
||||||
4
test/check/type_as_variable_name.golden
Normal file
4
test/check/type_as_variable_name.golden
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[1;37mtest/type_as_variable_name.ink:2,0: [31merror: [37mInvalid variable name 'int'
|
||||||
|
[36m int : float = 4.0
|
||||||
|
^^^
|
||||||
|
[37m
|
||||||
10
test/check/unary.golden
Normal file
10
test/check/unary.golden
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
scope (global) [
|
||||||
|
[pixel__ps_ps_main] : (position : float4) -> float4
|
||||||
|
[vertex__vs_vs_main] : (position : float3) -> float4
|
||||||
|
scope (vertex__vs_vs_main) [
|
||||||
|
[position] : float3
|
||||||
|
]
|
||||||
|
scope (pixel__ps_ps_main) [
|
||||||
|
[position] : float4
|
||||||
|
]
|
||||||
|
]
|
||||||
7
test/check/undeclared_function.golden
Normal file
7
test/check/undeclared_function.golden
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[1;37mtest/undeclared_function.ink:2,0: [31merror: [37mAttempt to call undeclared function 'foo'.
|
||||||
|
|
||||||
|
[96m foo();
|
||||||
|
^^^
|
||||||
|
|
||||||
|
|
||||||
|
[36m[37m
|
||||||
4
test/check/undeclared_symbol.golden
Normal file
4
test/check/undeclared_symbol.golden
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[1;37mtest/undeclared_symbol.ink:2,10: [31merror: [37mUse of undeclared symbol 'f'
|
||||||
|
[96m b : int = f;
|
||||||
|
^
|
||||||
|
[36m[37m
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[1;37mtest/unknown_overload.shd:6,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
[1;37mtest/unknown_overload.ink:6,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
||||||
[96m found:
|
[96m found:
|
||||||
foo(v, v);
|
foo(v, v);
|
||||||
^^^
|
^^^
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
[96m foo(v, v);
|
[96m foo(v, v);
|
||||||
^
|
^
|
||||||
[97m Possible overloads:
|
[97m Possible overloads:
|
||||||
[96m foo :: (v1 : float3, v2 : float3) { (test/unknown_overload.shd:1)
|
[96m foo :: (v1 : float3, v2 : float3) { (test/unknown_overload.ink:1)
|
||||||
[96m foo :: (v1 : float2, v2 : float2, v3 : float2) { (test/unknown_overload.shd:2)
|
[96m foo :: (v1 : float2, v2 : float2, v3 : float2) { (test/unknown_overload.ink:2)
|
||||||
|
|
||||||
[36m[37m[1;37mtest/unknown_overload.shd:6,4: [31merror: [37mType mismatch. Expected float3 got float
|
[36m[37m[1;37mtest/unknown_overload.ink:6,4: [31merror: [37mType mismatch. Expected float3 got float
|
||||||
[96m found:
|
[96m found:
|
||||||
foo(v, v);
|
foo(v, v);
|
||||||
^
|
^
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
got:
|
got:
|
||||||
v : float = 2.0
|
v : float = 2.0
|
||||||
|
|
||||||
[36m[37m[1;37mtest/unknown_overload.shd:6,7: [31merror: [37mType mismatch. Expected float3 got float
|
[36m[37m[1;37mtest/unknown_overload.ink:6,7: [31merror: [37mType mismatch. Expected float3 got float
|
||||||
[96m found:
|
[96m found:
|
||||||
foo(v, v);
|
foo(v, v);
|
||||||
^
|
^
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
[1;37mtest/wrong_argument_count.shd:5,19: [31merror: [37mUse of undeclared symbol 'w'
|
[1;37mtest/wrong_argument_count.ink:5,19: [31merror: [37mUse of undeclared symbol 'w'
|
||||||
[96m return x * y * z * w;
|
[96m return x * y * z * w;
|
||||||
^
|
^
|
||||||
[36m[37m[1;37mtest/wrong_argument_count.shd:9,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
[36m[37m[1;37mtest/wrong_argument_count.ink:9,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
||||||
[96m found:
|
[96m found:
|
||||||
foo(2.0, 3.0);
|
foo(2.0, 3.0);
|
||||||
^^^
|
^^^
|
||||||
[97m Possible overloads:
|
[97m Possible overloads:
|
||||||
[96m foo :: (x : float, y : float, z : float) -> float { (test/wrong_argument_count.shd:1)
|
[96m foo :: (x : float, y : float, z : float) -> float { (test/wrong_argument_count.ink:1)
|
||||||
[97m Not enough arguments: Wanted 3, got 2.
|
[97m Not enough arguments: Wanted 3, got 2.
|
||||||
|
|
||||||
[96m foo :: (x : float, y : float, z : float, w : float) -> float { (test/wrong_argument_count.shd:4)
|
[96m foo :: (x : float, y : float, z : float, w : float) -> float { (test/wrong_argument_count.ink:4)
|
||||||
[97m Not enough arguments: Wanted 4, got 2.
|
[97m Not enough arguments: Wanted 4, got 2.
|
||||||
|
|
||||||
|
|
||||||
30
test/check/wrong_multiply.golden
Normal file
30
test/check/wrong_multiply.golden
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
[1;37mtest/wrong_multiply.ink:4,18: [31merror: [37mProcedure call did not match any of the possible overloads for 'float4'
|
||||||
|
[96m found:
|
||||||
|
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||||
|
^^^^^^
|
||||||
|
|
||||||
|
[97m While matching argument 2 in function call.
|
||||||
|
[96m result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||||
|
^
|
||||||
|
[97m Possible overloads:
|
||||||
|
[96m float4 :: (float, float, float, float)
|
||||||
|
[96m float4 :: (float2, float2)
|
||||||
|
[96m float4 :: (float2, float, float)
|
||||||
|
[96m float4 :: (float, float2, float)
|
||||||
|
[96m float4 :: (float, float, float2)
|
||||||
|
[96m float4 :: (float, float3)
|
||||||
|
[96m float4 :: (float3, float)
|
||||||
|
[96m float4 :: (float4)
|
||||||
|
[96m float4 :: (float)
|
||||||
|
|
||||||
|
[36m[37m[1;37mtest/wrong_multiply.ink:4,34: [31merror: [37mType mismatch. Expected float got float2
|
||||||
|
[96m found:
|
||||||
|
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||||
|
^
|
||||||
|
expected:
|
||||||
|
float
|
||||||
|
|
||||||
|
got:
|
||||||
|
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||||
|
|
||||||
|
[36m[37m
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
[1;37mtest/wrong_type_for_function.shd:11,17: [31merror: [37mProcedure call did not match any of the possible overloads for 'float4'
|
[1;37mtest/wrong_type_for_function.ink:11,17: [31merror: [37mProcedure call did not match any of the possible overloads for 'float4'
|
||||||
[96m found:
|
[96m found:
|
||||||
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||||
^^^^^^
|
^^^^^^
|
||||||
@@ -7,9 +7,17 @@
|
|||||||
[96m color : float4 = float4(y, 1.0, 1.0, 1.0);
|
[96m color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||||
^
|
^
|
||||||
[97m Possible overloads:
|
[97m Possible overloads:
|
||||||
[96m foreign float4 :: (float, float, float, float) -> float4; (test/wrong_type_for_function.shd:78)
|
[96m float4 :: (float, float, float, float)
|
||||||
|
[96m float4 :: (float2, float2)
|
||||||
|
[96m float4 :: (float2, float, float)
|
||||||
|
[96m float4 :: (float, float2, float)
|
||||||
|
[96m float4 :: (float, float, float2)
|
||||||
|
[96m float4 :: (float, float3)
|
||||||
|
[96m float4 :: (float3, float)
|
||||||
|
[96m float4 :: (float4)
|
||||||
|
[96m float4 :: (float)
|
||||||
|
|
||||||
[36m[37m[1;37mtest/wrong_type_for_function.shd:11,24: [31merror: [37mType mismatch. Expected float got float2
|
[36m[37m[1;37mtest/wrong_type_for_function.ink:11,24: [31merror: [37mType mismatch. Expected float got float2
|
||||||
[96m found:
|
[96m found:
|
||||||
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||||
^
|
^
|
||||||
43
test/check_all.suite
Normal file
43
test/check_all.suite
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
test/assign_arithmetic_expression.ink check
|
||||||
|
test/basic_property_and_return_value.ink check
|
||||||
|
test/builtin_types.ink check
|
||||||
|
test/complicated_computation.ink check
|
||||||
|
test/constant_buffer.ink check
|
||||||
|
test/empty_struct.ink check
|
||||||
|
test/empty_vertex_main.ink check
|
||||||
|
test/empty_vertex_main_with_position_parameter.ink check
|
||||||
|
test/field_assignment.ink check
|
||||||
|
test/for_i_loop.ink check
|
||||||
|
test/function_call.ink check
|
||||||
|
test/function_call_out_of_order_declaration.ink check
|
||||||
|
test/function_call_return.ink check
|
||||||
|
test/functions_with_same_name.ink check
|
||||||
|
test/function_with_int_return.ink check
|
||||||
|
test/if_cond_assign.ink check
|
||||||
|
test/ifdefs.ink check
|
||||||
|
test/if_def_block.ink check
|
||||||
|
test/if_def_expression.ink check
|
||||||
|
test/inferred_types.ink check
|
||||||
|
test/multiple_functions.ink check
|
||||||
|
test/multiple_semicolons_everywhere.ink check
|
||||||
|
test/nested_if.ink check
|
||||||
|
test/non_bool_cond.ink check
|
||||||
|
test/pass_and_access_struct_fields_in_functions.ink check
|
||||||
|
test/passthrough.ink check
|
||||||
|
test/property_rename.ink check
|
||||||
|
test/redeclared_variable.ink check
|
||||||
|
test/simple_else_if.ink check
|
||||||
|
test/simple_if_else.ink check
|
||||||
|
test/simple_if.ink check
|
||||||
|
test/simple_struct_access.ink check
|
||||||
|
test/struct_access_primitive_type.ink check
|
||||||
|
test/struct_within_struct.ink check
|
||||||
|
test/type_as_variable_name.ink check
|
||||||
|
test/unary.ink check
|
||||||
|
test/undeclared_function.ink check
|
||||||
|
test/undeclared_symbol.ink check
|
||||||
|
test/unknown_overload.ink check
|
||||||
|
test/use_builtin_functions.ink check
|
||||||
|
test/wrong_argument_count.ink check
|
||||||
|
test/wrong_multiply.ink check
|
||||||
|
test/wrong_type_for_function.ink check
|
||||||
28
test/codegen/builtin_types.golden
Normal file
28
test/codegen/builtin_types.golden
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
void vs_main()
|
||||||
|
{
|
||||||
|
float2 v2 = float2(2.0f, 2.0f);
|
||||||
|
v2 = float2(2.0f, 2.0f);
|
||||||
|
v2 = float2(v2, v2);
|
||||||
|
float3 v3 = float3(2.0f, 2.0f, 2.0f);
|
||||||
|
v3 = float3(v2, 1.0f);
|
||||||
|
v3 = float3(1.0f, v2);
|
||||||
|
v3 = float3(1.0f, 1.0f, 1.0f);
|
||||||
|
v3 = float3(v3, v3, v3);
|
||||||
|
float4 v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||||
|
v4 = float4(v4, v4, v4, v4);
|
||||||
|
v4 = float4(v2, v2);
|
||||||
|
v4 = float4(v2, 1.0f, 1.0f);
|
||||||
|
v4 = float4(1.0f, v2, 1.0f);
|
||||||
|
v4 = float4(1.0f, 1.0f, v2);
|
||||||
|
v4 = float4(v3, 2.0f);
|
||||||
|
v4 = float4(2.0f, v3);
|
||||||
|
v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||||
|
v4 = float4(1.0f, 1.0f, v2);
|
||||||
|
v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||||
|
v2.x = 2.0f;
|
||||||
|
v2.y = 2.0f;
|
||||||
|
float p = (v2.x + v3.z);
|
||||||
|
float q = (v4.w + v2.x);
|
||||||
|
float4x4 m;
|
||||||
|
}
|
||||||
|
|
||||||
18
test/codegen/constant_buffer.golden
Normal file
18
test/codegen/constant_buffer.golden
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
cbuffer camera : register(b0)
|
||||||
|
{
|
||||||
|
float4x4 projection;
|
||||||
|
float4x4 view;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
float4 mv = mul(camera.view, pos);
|
||||||
|
float4 mvp = mul(camera.projection, mv);
|
||||||
|
return mvp;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps_main() : SV_TARGET
|
||||||
|
{
|
||||||
|
return float4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
17
test/codegen/custom_hint.golden
Normal file
17
test/codegen/custom_hint.golden
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
cbuffer __PROPERTIES : register(b0)
|
||||||
|
{
|
||||||
|
float __PROPERTIES__time;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
return float4(pos.x, pos.y, pos.z, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps_main(float4 pos : SV_POSITION) : SV_TARGET
|
||||||
|
{
|
||||||
|
float t = __PROPERTIES__time;
|
||||||
|
return float4(1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
7
test/codegen/if_def_block.golden
Normal file
7
test/codegen/if_def_block.golden
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
void ps_main()
|
||||||
|
{
|
||||||
|
|
||||||
|
float4 alpha_color = float4(1, 0, 0, 1);
|
||||||
|
float f = 2.0f;
|
||||||
|
}
|
||||||
|
|
||||||
4
test/codegen/if_def_expression.golden
Normal file
4
test/codegen/if_def_expression.golden
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
void vs_console_main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
9
test/codegen/ifdefs.golden
Normal file
9
test/codegen/ifdefs.golden
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
void ps_main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void vs_skinning_main()
|
||||||
|
{
|
||||||
|
float x = 5.0f;
|
||||||
|
}
|
||||||
|
|
||||||
24
test/codegen/inferred_types.golden
Normal file
24
test/codegen/inferred_types.golden
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
float bar();
|
||||||
|
float foo();
|
||||||
|
|
||||||
|
float bar()
|
||||||
|
{
|
||||||
|
return 5.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float foo()
|
||||||
|
{
|
||||||
|
return bar();
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
float f = 2.0f;
|
||||||
|
int i = 10;
|
||||||
|
f = foo();
|
||||||
|
float2 v2 = float2(2, 2);
|
||||||
|
float3 v3 = float3(2, 2, 3);
|
||||||
|
float4 v4 = float4(4, 5, 6, 7);
|
||||||
|
return float4(1, 1, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
18
test/codegen/nested_if.golden
Normal file
18
test/codegen/nested_if.golden
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
if (pos.x > 100)
|
||||||
|
{
|
||||||
|
if (pos.x > 50)
|
||||||
|
{
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
93
test/codegen/night_time.golden
Normal file
93
test/codegen/night_time.golden
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
float4 apply_night(float4 color);
|
||||||
|
float4 apply_twilight(float4 color);
|
||||||
|
float4 apply_morning(float4 color);
|
||||||
|
float4 apply_dawn(float4 color);
|
||||||
|
|
||||||
|
cbuffer __PROPERTIES : register(b0)
|
||||||
|
{
|
||||||
|
float __PROPERTIES__hour_of_day;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D __PROPERTIES__input_tex : register(t0);
|
||||||
|
SamplerState __PROPERTIES__tex_sampler : register(s0);
|
||||||
|
|
||||||
|
struct VS_Input
|
||||||
|
{
|
||||||
|
float3 pos : POSITION;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VS_Output
|
||||||
|
{
|
||||||
|
float4 pos : SV_POSITION;
|
||||||
|
float2 uv : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VS_Output vs_main(VS_Input input)
|
||||||
|
{
|
||||||
|
VS_Output output;
|
||||||
|
output.pos = float4(input.pos, 1.0f);
|
||||||
|
output.uv = input.uv;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 apply_night(float4 color)
|
||||||
|
{
|
||||||
|
float4 result = color;
|
||||||
|
(result -= float4(0.3f, 0.2f, 0.0f, 0.0f));
|
||||||
|
(result *= 0.8f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 apply_twilight(float4 color)
|
||||||
|
{
|
||||||
|
float4 result = color;
|
||||||
|
(result += float4(0.2f, -0.1f, 0.1f, 0.0f));
|
||||||
|
(result *= 0.9f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 apply_morning(float4 color)
|
||||||
|
{
|
||||||
|
return (color * 1.3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 apply_dawn(float4 color)
|
||||||
|
{
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps_main(VS_Output input)
|
||||||
|
{
|
||||||
|
float4 sampled_color = __PROPERTIES__input_tex.Sample(__PROPERTIES__tex_sampler, input.uv);
|
||||||
|
float t = 0.0f;
|
||||||
|
float4 a = float4(0, 0, 0, 0);
|
||||||
|
float4 b = float4(0, 0, 0, 0);
|
||||||
|
if (__PROPERTIES__hour_of_day > 16)
|
||||||
|
{
|
||||||
|
t = ((__PROPERTIES__hour_of_day - 16) / 6);
|
||||||
|
a = apply_twilight(sampled_color);
|
||||||
|
b = apply_night(sampled_color);
|
||||||
|
}
|
||||||
|
else if (__PROPERTIES__hour_of_day > 12)
|
||||||
|
{
|
||||||
|
t = ((__PROPERTIES__hour_of_day - 12) / 6);
|
||||||
|
a = sampled_color;
|
||||||
|
b = apply_twilight(sampled_color);
|
||||||
|
}
|
||||||
|
else if (__PROPERTIES__hour_of_day > 6)
|
||||||
|
{
|
||||||
|
t = ((__PROPERTIES__hour_of_day - 6) / 6);
|
||||||
|
a = apply_morning(sampled_color);
|
||||||
|
b = sampled_color;
|
||||||
|
}
|
||||||
|
else if (__PROPERTIES__hour_of_day >= 0)
|
||||||
|
{
|
||||||
|
t = (__PROPERTIES__hour_of_day / 6);
|
||||||
|
a = apply_night(sampled_color);
|
||||||
|
b = apply_morning(sampled_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lerp(a, b, t);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
cbuffer __PROPERTIES : register(b0)
|
cbuffer __PROPERTIES : register(b0)
|
||||||
{
|
{
|
||||||
float4 color;
|
float4 __PROPERTIES__color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
||||||
{
|
{
|
||||||
return pos;
|
return pos;
|
||||||
@@ -10,6 +11,6 @@ float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
|||||||
|
|
||||||
float4 ps_main() : SV_TARGET
|
float4 ps_main() : SV_TARGET
|
||||||
{
|
{
|
||||||
return color;
|
return __PROPERTIES__color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
test/codegen/simple_else_if.golden
Normal file
17
test/codegen/simple_else_if.golden
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
if (pos.x > 100)
|
||||||
|
{
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
else if (pos.x > 50)
|
||||||
|
{
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
10
test/codegen/simple_if.golden
Normal file
10
test/codegen/simple_if.golden
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
if (0 > 100)
|
||||||
|
{
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
13
test/codegen/simple_if_else.golden
Normal file
13
test/codegen/simple_if_else.golden
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
if (0 > 100)
|
||||||
|
{
|
||||||
|
return float4(pos, 1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return float4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
10
test/codegen/unary.golden
Normal file
10
test/codegen/unary.golden
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
float4 vs_vs_main(float3 position : POSITION) : SV_POSITION
|
||||||
|
{
|
||||||
|
return float4(position.x, position.y, position.z, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float4 ps_ps_main(float4 position : SV_POSITION) : SV_TARGET
|
||||||
|
{
|
||||||
|
return float4(0.5f, -1, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,20 +1,30 @@
|
|||||||
test/assign_arithmetic_expression.shd codegen
|
test/assign_arithmetic_expression.ink codegen
|
||||||
test/basic_property_and_return_value.shd codegen
|
test/basic_property_and_return_value.ink codegen
|
||||||
test/complicated_computation.shd codegen
|
test/builtin_types.ink codegen
|
||||||
test/constant_buffer.shd codegen
|
test/complicated_computation.ink codegen
|
||||||
test/empty_struct.shd codegen
|
test/constant_buffer.ink codegen
|
||||||
test/empty_vertex_main.shd codegen
|
test/empty_struct.ink codegen
|
||||||
test/empty_vertex_main_with_position_parameter.shd codegen
|
test/empty_vertex_main.ink codegen
|
||||||
test/field_assignment.shd codegen
|
test/empty_vertex_main_with_position_parameter.ink codegen
|
||||||
test/function_call.shd codegen
|
test/field_assignment.ink codegen
|
||||||
test/function_call_out_of_order_declaration.shd codegen
|
test/function_call.ink codegen
|
||||||
test/function_call_return.shd codegen
|
test/function_call_out_of_order_declaration.ink codegen
|
||||||
test/meta_block.shd codegen
|
test/function_call_return.ink codegen
|
||||||
test/multiple_functions.shd codegen
|
test/ifdefs.ink codegen
|
||||||
test/multiple_semicolons_everywhere.shd codegen
|
test/if_def_block.ink codegen
|
||||||
test/pass_and_access_struct_fields_in_functions.shd codegen
|
test/if_def_expression.ink codegen
|
||||||
test/passthrough.shd codegen
|
test/inferred_types.ink codegen
|
||||||
test/property_rename.shd codegen
|
test/meta_block.ink codegen
|
||||||
test/simple_struct_access.shd codegen
|
test/multiple_functions.ink codegen
|
||||||
test/struct_within_struct.shd codegen
|
test/multiple_semicolons_everywhere.ink codegen
|
||||||
test/use_builtin_functions.shd codegen
|
test/nested_if.ink codegen
|
||||||
|
test/pass_and_access_struct_fields_in_functions.ink codegen
|
||||||
|
test/passthrough.ink codegen
|
||||||
|
test/property_rename.ink codegen
|
||||||
|
test/simple_else_if.ink codegen
|
||||||
|
test/simple_if_else.ink codegen
|
||||||
|
test/simple_if.ink codegen
|
||||||
|
test/simple_struct_access.ink codegen
|
||||||
|
test/struct_within_struct.ink codegen
|
||||||
|
test/unary.ink codegen
|
||||||
|
test/use_builtin_functions.ink codegen
|
||||||
|
|||||||
39
test/colorful_circle.ink
Normal file
39
test/colorful_circle.ink
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
prop :: properties {
|
||||||
|
time : float @time;
|
||||||
|
resolution : float2 @resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex main :: (pos : float3 @position) -> float4 @position {
|
||||||
|
return float4(pos, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel main :: (pos : float4 @outposition) -> float4 @target {
|
||||||
|
p := float2(2.0 * pos.x - prop.resolution.x, 2.0 * pos.y - prop.resolution.y) / prop.resolution.y;
|
||||||
|
tau := 3.1415926535 * 2.0;
|
||||||
|
a := atan(p.x, p.y);
|
||||||
|
r := length(p) * 0.75;
|
||||||
|
|
||||||
|
uv := float2(a / tau, r);
|
||||||
|
|
||||||
|
x_col := (uv.x - (p.time / 3.0)) * 3.0;
|
||||||
|
x_col = mod(x_col, 3.0);
|
||||||
|
hor_colour := float3(0.25, 0.25, 0.25);
|
||||||
|
|
||||||
|
if x_col < 1.0 {
|
||||||
|
horColour.r += 1.0 - xCol;
|
||||||
|
horColour.g += xCol;
|
||||||
|
} else if x_col < 2.0 {
|
||||||
|
xCol -= 1.0;
|
||||||
|
horColour.g += 1.0 - xCol;
|
||||||
|
horColour.b += xCol;
|
||||||
|
} else {
|
||||||
|
x_col -= 2.0;
|
||||||
|
hor_colour.b += 1.0 - x_col;
|
||||||
|
hor_colour.r += x_col;
|
||||||
|
}
|
||||||
|
|
||||||
|
uv = (2.0 * uv) - 1.0;
|
||||||
|
beam_width = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
|
||||||
|
hor_beam = float3(beam_width);
|
||||||
|
result := float4(((hor_beam) * hor_colour), 1.0);
|
||||||
|
}
|
||||||
@@ -1,20 +1,29 @@
|
|||||||
test/assign_arithmetic_expression.shd compile
|
test/assign_arithmetic_expression.ink compile
|
||||||
test/basic_property_and_return_value.shd compile
|
test/basic_property_and_return_value.ink compile
|
||||||
test/complicated_computation.shd compile
|
test/builtin_types.ink compile
|
||||||
test/empty_struct.shd compile
|
test/complicated_computation.ink compile
|
||||||
test/empty_vertex_main.shd compile
|
test/empty_struct.ink compile
|
||||||
test/empty_vertex_main_with_position_parameter.shd compile
|
test/empty_vertex_main.ink compile
|
||||||
test/field_assignment.shd compile
|
test/empty_vertex_main_with_position_parameter.ink compile
|
||||||
test/float_suffix.shd compile
|
test/field_assignment.ink compile
|
||||||
test/function_call.shd compile
|
test/float_suffix.ink compile
|
||||||
test/function_call_out_of_order_declaration.shd compile
|
test/function_call.ink compile
|
||||||
test/function_call_return.shd compile
|
test/function_call_out_of_order_declaration.ink compile
|
||||||
test/functions_with_same_name.shd compile
|
test/function_call_return.ink compile
|
||||||
test/meta_block.shd compile
|
test/functions_with_same_name.ink compile
|
||||||
test/multiple_functions.shd compile
|
test/ifdefs.ink compile
|
||||||
test/multiple_semicolons_everywhere.shd compile
|
test/if_def_block.ink compile
|
||||||
test/pass_and_access_struct_fields_in_functions.shd compile
|
test/if_def_expression.ink compile
|
||||||
test/passthrough.shd compile
|
test/inferred_types.ink compile
|
||||||
test/simple_struct_access.shd compile
|
test/meta_block.ink compile
|
||||||
test/struct_within_struct.shd compile
|
test/multiple_functions.ink compile
|
||||||
test/use_builtin_functions.shd compile
|
test/multiple_semicolons_everywhere.ink compile
|
||||||
|
test/pass_and_access_struct_fields_in_functions.ink compile
|
||||||
|
test/passthrough.ink compile
|
||||||
|
test/simple_else_if.ink compile
|
||||||
|
test/simple_if_else.ink compile
|
||||||
|
test/simple_if.ink compile
|
||||||
|
test/simple_struct_access.ink compile
|
||||||
|
test/struct_within_struct.ink compile
|
||||||
|
test/unary.ink compile
|
||||||
|
test/use_builtin_functions.ink compile
|
||||||
1
test/compiled/assign_arithmetic_expression.golden
Normal file
1
test/compiled/assign_arithmetic_expression.golden
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[vertex entry point] - vs_main
|
||||||
1
test/compiled/builtin_types.golden
Normal file
1
test/compiled/builtin_types.golden
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[vertex entry point] - vs_main
|
||||||
1
test/compiled/if_def_block.golden
Normal file
1
test/compiled/if_def_block.golden
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[pixel entry point] - ps_main
|
||||||
1
test/compiled/if_def_expression.golden
Normal file
1
test/compiled/if_def_expression.golden
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[vertex entry point] - vs_console_main
|
||||||
2
test/compiled/ifdefs.golden
Normal file
2
test/compiled/ifdefs.golden
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[vertex entry point] - vs_skinning_main
|
||||||
|
[pixel entry point] - ps_main
|
||||||
12
test/custom_hint.ink
Normal file
12
test/custom_hint.ink
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
p :: properties {
|
||||||
|
time : float @time;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex main :: (pos : float3 @position) -> float4 @position {
|
||||||
|
return float4(pos.x, pos.y, pos.z, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pixel main :: (pos : float4 @outposition) -> float4 @target {
|
||||||
|
t := p.time;
|
||||||
|
return float4(1, 1, 1, 1);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user