Compare commits

86 Commits

Author SHA1 Message Date
0c0e31db38 Fix lvalue/rvalue binaries. Fix structured buffer output. 2025-09-29 22:22:00 +02:00
2e23b37405 Finalize struct gen for structured buffers. Rename buffer builtins. 2025-09-29 20:55:27 +02:00
63a68b70b4 More fixes to access and buffer compilation. 2025-09-26 07:01:06 +02:00
6528ca854b Merge branch 'main' of git.nbross.com:nielsbross/Ink-Shader-Language 2025-09-24 14:04:56 +02:00
940b58331d A bunch of array fixes and some buffer stuff that doesn't quite work yet 2025-09-24 14:04:50 +02:00
c26fa892ee Added todos to module 2025-09-18 11:08:52 +00:00
4c84437022 Added comment and todo on resource index in type var. 2025-09-18 11:02:35 +00:00
3fdaebf66d Added todo regarding for loop scoping. 2025-09-18 10:58:44 +00:00
50a404984d Started some fixes for array support. Not entirely there yet. 2025-09-17 21:37:53 +02:00
89904824bb Fixed test to fully deprecate properties. 2025-09-17 12:33:38 +02:00
94daf81a85 Merge branch 'main' of git.nbross.com:nielsbross/Ink-Shader-Language
# Conflicts:
#	AST.jai
#	Check.jai
#	module.jai
2025-09-17 12:32:47 +02:00
607a6a0bed Deprecate properties. Use hinted cbuffers instead. This opens up to use a structured buffer in that way as well if you want instead. 2025-09-17 12:31:37 +02:00
8b2141df16 Fix some if directive stuff. Fix a property output issue. Will be deprecated next commit anyway. 2025-09-17 12:30:36 +02:00
7fefe0ecf6 Ifdefs, moved semantic to check, fixed error reporting for builtins 2025-09-16 11:04:57 +02:00
f99f86bc37 Update readme 2025-09-14 20:21:43 +02:00
d5476b54d7 Fix tests. 2025-09-13 22:02:44 +02:00
622ce388fa Actually fix which allocator we're using in the tests. 2025-09-13 22:01:29 +02:00
b0653b6563 Tracy 2025-09-11 17:29:45 +02:00
d6ea2e4c2f Memory optimization for the general case by reserving significantly fewer tokens in lexer. It was reserving 1 million always! 2025-09-11 12:27:44 +02:00
361a310ed1 Actually pass the allocator in so it's used instead of temp. 2025-09-11 11:33:16 +02:00
78e6d6146e Remove compiler ctx allocator 2025-09-11 11:23:07 +02:00
79ec6cc42f Added the rest of current builtins. Started properly implementing compile tests. 2025-09-11 11:03:02 +02:00
9461fe626f Change result to context for clarity. Fix a bunch of stuff in builtin functions and structs. 2025-09-10 23:21:34 +02:00
ceafd197f5 A bunch of new allocation related stuff. 2025-09-10 06:59:29 +02:00
f4a9592f26 Much better allocation strategy with new ncore arenas. Add more builtin node gen stuff. It's kind of wonky. 2025-09-06 23:30:45 +02:00
9cf51a1534 Broke builtins. 2025-09-06 19:58:46 +02:00
11c936ba7f Started load directive. 2025-09-03 22:31:18 +02:00
4924b01eac Added some initial directive code. Don't quite like the way it's done 2025-09-03 21:05:00 +02:00
603b625e21 Rename of files, improved for handling, add cb hints
- Rename Test to Ink as main file
- Support more errors for for loops.
- Add hints to cbs
2025-09-02 11:55:27 +02:00
9e0728f952 Fixed some error handling and weird consume logic. 2025-09-01 12:58:45 +02:00
94fc3a4dad Added basic for i loops. Missing some breaking tests and more tests. Also want to add for each at some point and it_index. 2025-08-30 22:58:51 +02:00
14f8b20d5f More small changes 2025-08-27 22:12:19 +02:00
4825623c73 Merge branch 'main' of git.nbross.com:nielsbross/Ink-Shader-Language 2025-08-27 21:55:08 +02:00
da87209690 Move compile result stuff out of specific stages. 2025-08-27 21:55:01 +02:00
e0908a67c0 Add hint parsing on constant buffers. Not yet used in output. 2025-08-25 22:08:58 +02:00
ab711b5610 Minor experiment 2025-08-25 21:56:08 +02:00
f6801e3eeb Fix type result on binary compuations. 2025-08-24 11:37:16 +02:00
4f37ed03c0 Remove unnecessary error check in compile_file 2025-08-24 11:22:13 +02:00
5b2e2e936b Add record error to add file. Change compile_file to take varargs 2025-08-24 11:19:59 +02:00
b491a56409 Changed some naming in sem checker. 2025-08-23 22:01:25 +02:00
45f67e16a8 Clean up a little bit of API code. Fix an issue with missing operator. Port random semi complex shader. 2025-08-21 22:22:00 +02:00
01ffe9c73d Properly output compiled file instead of intermediate results in semcheck 2025-08-20 21:07:14 +02:00
382d790c5b Moved more sem result into file. Still feels weird. Whole thing should just be in the file being passed around directly. 2025-08-18 22:39:45 +02:00
27933e599a New ncore 2025-08-18 22:39:25 +02:00
b7e34a22b2 Fix error propagation error in parsing. Semantic checker fix for compile result struct. Test cleanup 2025-08-18 07:11:41 +02:00
c36712b3ed Merge branch 'main' of git.nbross.com:nielsbross/Ink-Shader-Language 2025-08-14 20:11:22 +02:00
6b6c7bce62 Fixed a bunch of semant back and forth but now it looks like I broke some parameter checking in functions. 2025-08-14 14:04:54 +02:00
e356c5a3a9 Merge branch 'main' of git.nbross.com:nielsbross/Ink-Shader-Language 2025-08-13 22:26:57 +02:00
5ec2186a42 Revert "More work towards the new API"
This reverts commit af3e298b29.
2025-08-13 22:26:43 +02:00
af3e298b29 More work towards the new API 2025-08-13 22:22:32 +02:00
af42b61ed6 Struct output shenanigans 2025-01-27 21:38:54 +01:00
cd167d1560 Add missing codegen for minusequals and etc. Do proper type checking on else blocks. Spread single arg constructors not supported in hlsl. 2025-01-23 22:47:09 +01:00
8ce8651d6b Fix some missing operators not getting parsed properly. Fixed field access type checking with depth more than 1. 2025-01-22 22:30:06 +01:00
42c5baa846 if else if else if if else if else 2025-01-20 22:20:47 +01:00
45ea54cf93 Fixed some error handling for invalid if statements. Started if codegen. 2025-01-18 22:22:16 +01:00
b4d119230b Proper pretty printing and error handling on non-cool if condition. 2025-01-15 21:32:18 +01:00
a72a9ff50d More print improvements. 2025-01-15 07:15:31 +01:00
41d1dd406d Merge branch 'error-lolg' 2025-01-14 09:39:09 +01:00
bc69a39570 Attempt to improved error log. 2025-01-14 09:38:05 +01:00
aaeda22fa3 Added broken check for bool if cond. Also added some wonky if/else pretty printing for AST. 2025-01-13 16:33:03 +01:00
4b927b6be9 Add else parsing. Still a few bugs with output. 2025-01-13 09:14:36 +01:00
85b23f90e5 Function overload check cleanup. Added if statement to parsing. 2025-01-12 22:15:02 +01:00
ec31046d30 Added inferred types and missing length function. 2025-01-10 22:44:15 +01:00
8bd766281e Added shader output data to compiled_file API 2025-01-08 22:38:01 +01:00
4053400152 Added unary expressions to semcheck and codegen. 2025-01-08 22:37:48 +01:00
1adb289c10 Add error propagation to lexer. 2025-01-06 22:17:44 +01:00
d65c6359db Fixed some sem check init. 2025-01-05 22:38:08 +01:00
d08529a3eb Simplification of API. 2025-01-04 23:12:54 +01:00
7787d1307b More extension bike shedding. 2025-01-01 23:14:30 +01:00
4deb07027f Fix some tests and do some cleanup. 2025-01-01 23:06:53 +01:00
f13508262b Remove dumbass type constraint crap. 2024-12-29 23:39:57 +01:00
90fb1a035e Add Properties prefix. Fix semantic check regression. 2024-09-26 16:47:34 +02:00
e365067354 Changed type var children to static array. 2024-09-25 13:01:56 +02:00
243d83663a Simplified shader test output 2024-09-20 13:00:17 +02:00
3f93e1a92d Add double access shader test 2024-09-20 12:48:47 +02:00
fca325b761 Deleted some stuff 2024-09-12 07:18:27 +02:00
d3aa4fffeb Quick fix for binary expr codegen. Add uv hint 2024-09-11 13:21:03 +02:00
6eba51cc8c Fix position output hint. 2024-09-11 12:42:59 +02:00
5b237d34de Merge branch 'dev' of git.nbross.com:nielsbross/Shader-Compiler into dev 2024-09-11 07:25:21 +02:00
ff668b6c95 Fix type checker not promoting ints. Fix some issues in the codegen. 2024-09-11 07:24:50 +02:00
c84516d39f Fix type checker not promoting ints. Fix some issues in the codegen. 2024-09-11 07:24:10 +02:00
517209c886 A bunch of allocator stuff 2024-09-10 07:21:27 +02:00
d01fca146c Opened pandoras box 2024-09-09 22:32:36 +02:00
d9dfcc6354 Beginning of better API 2024-09-02 12:34:48 +02:00
c8cd15456d Update ncore reference 2024-09-02 12:34:40 +02:00
c225217676 NCore update 2024-08-12 07:17:41 +02:00
257 changed files with 8878 additions and 4078 deletions

5
.gitmodules vendored
View File

@@ -1,3 +1,6 @@
[submodule "modules/nbrutil"] [submodule "modules/nbrutil"]
path = modules/ncore path = modules/ncore
url = git@git.nbross.com:nielsbross/nbrutil.git url = git@git.nbross.com:nielsbross/NCore.git
[submodule "modules/tracy"]
path = modules/tracy
url = https://github.com/rluba/jai-tracy.git

358
AST.jai
View File

@@ -8,19 +8,17 @@ AST_Kind :: enum {
Function; Function;
Return; Return;
// @Incomplete(nb): Should these three really be their own block types?
// Maybe they at least shouldn't need to have their own tokens...
Properties;
Meta;
Instance;
//== //==
// Directives
If_Directive;
// Hint; Access;
// Type;
// Operator;
Call; Call;
Struct; Struct;
If;
For;
CBuffer; CBuffer;
Buffer;
FieldList; FieldList;
ArgList; ArgList;
Variable; Variable;
@@ -71,22 +69,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 +95,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 +106,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 +132,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 +219,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,95 +251,206 @@ 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 {
append(builder, "("); indent(builder, indentation);
}
if node.token.kind == .TOKEN_LEFTBRACKET {
pretty_print_node(node.children[0], 0, builder);
append(builder, "[");
pretty_print_node(node.children[1], 0, builder);
append(builder, "]");
} else {
append(builder, "(");
op := node.token;
print_to_builder(builder, op_to_string(op));
append(builder, " ");
pretty_print_node(node.children[0], 0, builder);
append(builder, " ");
pretty_print_node(node.children[1], 0, builder);
append(builder, ")");
}
}
pretty_print_access :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
if !skip_indent {
indent(builder, indentation);
}
pretty_print_node(node.children[0], 0, builder);
append(builder, ".");
pretty_print_node(node.children[1], 0, builder);
}
pretty_print_unary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
if !skip_indent {
indent(builder, indentation);
}
op := node.token; op := node.token;
print_to_builder(builder, op_to_string(op)); print_to_builder(builder, op_to_string(op));
append(builder, " "); pretty_print_node(node.children[0], 0, builder);
pretty_print_children(node, 0, builder, flags = 0);
append(builder, ")");
} }
pretty_print_unary :: (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);
}
print_return_node :: (node : *AST_Node, indentation : int, builder : *String_Builder) {
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 .Access; {
pretty_print_access(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 {
print("%\n", child.kind);
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);
@@ -295,8 +459,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 {
@@ -315,23 +481,28 @@ pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder
append(builder, "pixel "); append(builder, "pixel ");
} }
if declaration.kind == .Properties { if declaration.kind == .If_Directive {
append(builder, "properties"); append(builder, "#if ");
if declaration.name.count > 0 {
print_to_builder(builder, " %", declaration.name);
}
} else if declaration.kind == .Instance {
append(builder, "instance");
} else if declaration.kind == .Meta {
append(builder, "meta");
} }
else {
if declaration.kind == .Struct { if declaration.kind == .Struct {
append(builder, "struct "); append(builder, "struct ");
} else if declaration.kind == .CBuffer { } else if declaration.kind == .CBuffer {
append(builder, "constant_buffer "); append(builder, "constant_buffer ");
} else if declaration.kind == .Buffer {
append(builder, "buffer ");
}
print_to_builder(builder, "%", declaration.name);
if declaration.kind == .CBuffer || declaration.kind == .Buffer{
for hint : declaration.hint_tokens {
if hint.string_value.count > 0 {
print_to_builder(builder, " (@%)", hint.string_value);
}
} }
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{
@@ -341,12 +512,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, ")");

2688
Check.jai Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,37 +1,50 @@
/////////////////////////////////////
//~ nbr:
//
/////////////////////////////////////
//~ nbr: Codegen TODOs
//
Output_Language :: enum { 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; ctx : *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, ctx : *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);
@@ -41,13 +54,54 @@ indent :: (state : *Codegen_State, indentation : int) {
for 1..indentation append(*state.builder, " "); for 1..indentation append(*state.builder, " ");
} }
emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) { hlsl_type_to_string :: (variables : []Type_Variable, type_handle : Type_Variable_Handle) -> string {
find_result := find_symbol(state.scope_stack, node.name, state.current_scope); return hlsl_type_to_string(variables, from_handle(variables, type_handle));
}
field := h2tv(state.type_variables, find_result.type_variable); hlsl_type_to_string :: (variables : []Type_Variable, type_variable : Type_Variable) -> string {
if type_variable.type == {
case .Invalid;
return "{{invalid}}";
case .Unit;
return "()";
case .Int; {
return "int";
}
case .Half; {
return "half";
}
case .Float; {
return "float";
}
case .Double; {
return "double";
}
case .Sampler; {
return "SamplerState";
}
case .Texture2D; {
return "Texture2D";
}
case .Function; #through;
case .Struct; {
return type_variable.typename;
}
case .Array;
return hlsl_type_to_string(variables, type_variable.element_type);
}
return "";
}
emit_field :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
find_result := find_symbol(state.ctx.scope_stack, node.name, state.current_scope);
field := from_handle(state.ctx.type_variables, find_result.type_variable);
indent(state, indentation); indent(state, indentation);
print_to_builder(*state.builder, "% ", type_to_string(field));
print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, field));
print_to_builder(*state.builder, "%", node.name); print_to_builder(*state.builder, "%", node.name);
if field.type == .Sampler { if field.type == .Sampler {
@@ -58,34 +112,54 @@ 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, " = "); if field.type == .Array {
emit_node(state, child, 0); append(*state.builder, "[");
emit_node(state, child, 0);
append(*state.builder, "]");
} else {
print_to_builder(*state.builder, " = ");
emit_node(state, child, 0);
}
} }
for i :0..field.child_count - 1 { if node.parent.kind == .Block {
child := h2tv(state.type_variables, field.children[i]); append(*state.builder, ";");
}
for i :0..field.children.count - 1 {
child := from_handle(state.ctx.type_variables, field.children[i]);
emit_node(state, child.source_node, 0); 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 lookup_hint(hint.ident_value) == .UV {
append(*state.builder, " : TEXCOORD0");
} else if lookup_hint(hint.ident_value) == .Output_Position {
append(*state.builder, " : SV_POSITION");
} }
} }
} }
emit_block :: (state : *Codegen_State, node : *AST_Node, indentation : int) { emit_block :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
previous_scope := state.current_scope;
for statement : node.children { for statement : node.children {
if statement.type_variable {
state.current_scope = from_handle(state.ctx.type_variables, statement.type_variable).scope;
}
emit_node(state, statement, indentation); 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");
} }
} }
state.current_scope = previous_scope;
} }
emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) { emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
@@ -97,7 +171,7 @@ emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
emit_node(state, args.children[0], 0); emit_node(state, args.children[0], 0);
append(*state.builder, "."); append(*state.builder, ".");
print_to_builder(*state.builder, "%(", node.name); print_to_builder(*state.builder, "Sample(");
for i : 1..args.children.count - 1 { for i : 1..args.children.count - 1 {
child := args.children[i]; child := args.children[i];
@@ -108,6 +182,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);
@@ -129,59 +221,9 @@ emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
append(*state.builder, ")"); append(*state.builder, ")");
} }
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);
if !find_result {
message : Compiler_Message;
message.message_kind = .Internal_Error;
message.path = state.path;
message.message = "Attempting to generate undeclared properties buffer. This should never happen at this stage.";
array_add(*state.result.messages, message);
}
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);
print_to_builder(*state.builder, "cbuffer __PROPERTIES : register(b%) \n{\n", variable.resource_index);
previous_scope := state.current_scope;
state.current_scope = variable.scope;
resources : Static_Array(*AST_Node, 8);
for child : node.children {
if child.kind == .FieldList {
for field : child.children {
tv := h2tv(state.type_variables, field.type_variable);
if tv.type == .Sampler || tv.type == .Texture2D {
array_add(*resources, field);
continue;
}
emit_node(state, field, 1);
append(*state.builder, ";\n");
}
}
}
append(*state.builder, "}\n\n");
for i : 0..resources.count - 1 {
resource := resources[i];
emit_node(state, resource, 0);
append(*state.builder, ";\n");
}
append(*state.builder, "\n");
state.current_scope = previous_scope;
}
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.ctx.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 {
@@ -189,17 +231,17 @@ emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, e
message.message_kind = .Internal_Error; message.message_kind = .Internal_Error;
message.path = state.path; message.path = state.path;
message.message = "Attempting to generate undeclared function. This should never happen at this stage."; message.message = "Attempting to generate undeclared function. This should never happen at this stage.";
array_add(*state.result.messages, message); array_add(*state.ctx.messages, message);
} }
for func : find_result.functions { for func : find_result.functions {
function_variable := h2tv(state.type_variables, func.type_variable); function_variable := from_handle(state.ctx.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.ctx.type_variables, function_variable.return_type_variable);
print_to_builder(*state.builder, "% ", type_to_string(return_variable)); print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, return_variable));
} else { } else {
append(*state.builder, "void "); append(*state.builder, "void ");
} }
@@ -272,6 +314,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, "==");
} }
@@ -309,49 +366,77 @@ emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
} }
case .Float; { case .Float; {
print_to_builder(*state.builder, "%f", formatFloat(node.float_value, zero_removal=.ONE_ZERO_AFTER_DECIMAL)); print_to_builder(*state.builder, "%f", formatFloat(node.float_value, zero_removal=.ONE_ZERO_AFTER_DECIMAL));
}
case .Properties; {
} }
case .Field; { case .Field; {
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.ctx.type_variables, node.type_variable);
is_properties := type_var.typename == "properties";
if !is_properties { print_to_builder(*state.builder, "%", node.name);
print_to_builder(*state.builder, "%", node.name);
}
if node.children.count > 0 { if node.children.count > 0 {
if !is_properties { append(*state.builder, ".");
append(*state.builder, ".");
}
emit_node(state, node.children[0], 0); emit_node(state, node.children[0], 0);
} }
} }
case .Binary; { case .Access; {
indent(*state.builder, indentation); indent(*state.builder, indentation);
lhs := node.children[0]; lhs := node.children[0];
rhs := node.children[1]; rhs := node.children[1];
emit_node(state, lhs, 0); emit_node(state, lhs, 0);
append(*state.builder, " "); print_to_builder(*state.builder, "%.", node.name);
emit_operator(state, node.token.kind);
append(*state.builder, " ");
emit_node(state, rhs, 0); emit_node(state, rhs, 0);
} }
case .Binary; {
indent(*state.builder, indentation);
if node.token.kind != .TOKEN_ASSIGN && node.token.kind != .TOKEN_LEFTBRACKET {
if (node.parent.kind == .Binary && node.parent.token.kind != .TOKEN_ASSIGN) || node.parent.kind == .Access {
append(*state.builder, "(");
}
}
lhs := node.children[0];
rhs := node.children[1];
if node.token.kind == .TOKEN_LEFTBRACKET {
emit_node(state, lhs, 0);
append(*state.builder, "[");
emit_node(state, rhs, 0);
append(*state.builder, "]");
} else {
emit_node(state, lhs, 0);
append(*state.builder, " ");
emit_operator(state, node.token.kind);
append(*state.builder, " ");
emit_node(state, rhs, 0);
}
if node.token.kind != .TOKEN_ASSIGN && node.token.kind != .TOKEN_LEFTBRACKET {
if (node.parent.kind == .Binary && node.parent.token.kind != .TOKEN_ASSIGN) || node.parent.kind == .Access {
append(*state.builder, ")");
}
}
}
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);
@@ -360,7 +445,72 @@ 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];
append(*state.builder, "(");
emit_node(state, cond, 0);
append(*state.builder, ")");
body := node.children[1];
append(*state.builder, "\n");
indent(*state.builder, indentation);
append(*state.builder, "{\n");
emit_block(state, body, indentation + 1);
indent(*state.builder, indentation);
append(*state.builder, "}\n");
if node.children.count == 3 {
emit_else(state, node.children[2], indentation);
}
}
}
}
emit_else :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
indent(*state.builder, indentation);
append(*state.builder, "else ");
if node.kind == .If {
emit_node(state, node, indentation);
} else if node.kind == .Block {
append(*state.builder, "\n");
indent(*state.builder, indentation);
append(*state.builder, "{\n");
emit_block(state, node, indentation + 1);
indent(*state.builder, indentation);
append(*state.builder, "}");
} }
} }
@@ -374,11 +524,37 @@ emit_field_list :: (state : *Codegen_State, field_list : *AST_Node, indentation
} }
} }
emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int) { emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int, name : string = "") {
print_to_builder(*state.builder, "struct %", node.name); if name.count > 0 {
print_to_builder(*state.builder, "struct %", name);
} else {
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.ctx.type_variables, node.type_variable).scope;
field_list := node.children[0];
if field_list.children.count > 0 {
append(*state.builder, "\n{\n");
} else {
append(*state.builder, " {");
}
emit_field_list(state, field_list, indentation);
append(*state.builder, "};\n\n");
state.current_scope = current_scope;
}
emit_cbuffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
variable := from_handle(state.ctx.type_variables, node.type_variable);
print_to_builder(*state.builder, "cbuffer % : register(b%)", variable.name, variable.resource_index);
current_scope := state.current_scope;
state.current_scope = from_handle(state.ctx.type_variables, node.type_variable).scope;
field_list := node.children[0]; field_list := node.children[0];
@@ -394,25 +570,13 @@ emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
state.current_scope = current_scope; state.current_scope = current_scope;
} }
emit_cbuffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) { emit_buffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
variable := h2tv(state.type_variables, node.type_variable); variable := from_handle(state.ctx.type_variables, node.type_variable);
print_to_builder(*state.builder, "cbuffer % : register(b%)", variable.name, variable.resource_index); element := from_handle(state.ctx.type_variables, variable.element_type);
current_scope := state.current_scope; emit_struct(state, node, indentation, element.typename);
state.current_scope = h2tv(state.type_variables, node.type_variable).scope;
field_list := node.children[0]; print_to_builder(*state.builder, "StructuredBuffer<%> % : register(t%);\n\n", element.typename, variable.name, variable.resource_index);
if field_list.children.count > 0 {
append(*state.builder, "\n{\n");
} else {
append(*state.builder, " {");
}
emit_field_list(state, field_list, indentation);
append(*state.builder, "}\n\n");
state.current_scope = current_scope;
} }
emit_declaration :: (state : *Codegen_State, node : *AST_Node) { emit_declaration :: (state : *Codegen_State, node : *AST_Node) {
@@ -420,36 +584,62 @@ emit_declaration :: (state : *Codegen_State, node : *AST_Node) {
case .Function; { case .Function; {
emit_function(state, node, 0); emit_function(state, node, 0);
} }
case .Properties; {
emit_properties(state, node, 0);
}
case .CBuffer; { case .CBuffer; {
emit_cbuffer(state, node, 0); emit_cbuffer(state, node, 0);
} }
case .Buffer; {
emit_buffer(state, node, 0);
}
case .Struct; { case .Struct; {
emit_struct(state, node, 0); emit_struct(state, node, 0);
} }
} }
} }
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.ctx = result;
state.current_scope = cast(Scope_Handle)1;
state.output_language = output_language;
init_string_builder(*state.builder);
codegen(*state);
}
}
#scope_file
codegen :: (state : *Codegen_State) {
found_function : bool = false; found_function : bool = false;
found_struct : bool = false; // found_struct : bool = false;
for variable : state.type_variables { // for variable : state.ctx.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;
print_to_builder(*state.builder, "struct %;\n", variable.source_node.name); // print_to_builder(*state.builder, "struct %;\n", variable.source_node.name);
found_struct = true; // found_struct = true;
} // }
} // }
if found_struct { // if found_struct {
append(*state.builder, "\n"); // append(*state.builder, "\n");
} // }
for variable : state.type_variables { for variable : state.ctx.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);
@@ -460,22 +650,14 @@ codegen :: (state : *Codegen_State) -> Codegen_Result {
append(*state.builder, "\n"); append(*state.builder, "\n");
} }
for declaration : state.root.children { for declaration : state.ctx.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.ctx.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

View File

@@ -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;

768
Ink.jai Normal file
View File

@@ -0,0 +1,768 @@
/////////////////////////////////////
/*~ 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);
}
for buf : ctx.buffers {
if buf.kind == {
case .Constant; {
print_to_builder(*sb, "[constant_buffer] - % - %", buf.name, buf.buffer_index);
}
case .Structured; {
print_to_builder(*sb, "[buffer] - % - %", buf.name, buf.buffer_index);
}
if buf.hints.count > 0 {
for hint : buf.hints {
print_to_builder(*sb, " (@%)", hint.custom_hint_name);
}
}
append(*sb, "\n");
indent(*sb, 1);
for field : buf.fields {
append(*sb, "[field] - ");
pretty_print_field(*sb, *field);
append(*sb, "\n");
indent(*sb, 1);
}
}
}
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);
}

View File

@@ -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,11 +49,13 @@ Token_Kind :: enum {
TOKEN_SEMICOLON; TOKEN_SEMICOLON;
TOKEN_COMMA; TOKEN_COMMA;
TOKEN_DOT; TOKEN_DOT;
TOKEN_DOTDOT;
TOKEN_IDENTIFIER; TOKEN_IDENTIFIER;
// Keywords // Keywords
TOKEN_BOOL; TOKEN_BOOL;
TOKEN_BUFFER;
TOKEN_CASE; TOKEN_CASE;
TOKEN_CBUFFER; TOKEN_CBUFFER;
@@ -95,11 +92,12 @@ Token_Kind :: enum {
TOKEN_OUT; TOKEN_OUT;
TOKEN_PIXEL; TOKEN_PIXEL;
TOKEN_PROPERTIES; TOKEN_PLEX;
TOKEN_RETURN; TOKEN_RETURN;
TOKEN_REGISTER; TOKEN_REGISTER;
TOKEN_STRING;
TOKEN_STRUCT; TOKEN_STRUCT;
TOKEN_SWITCH; TOKEN_SWITCH;
@@ -133,10 +131,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 {
@@ -217,10 +217,11 @@ identifier_kind :: (using lexer : *Lexer) -> Token_Kind {
identifier.count = length; identifier.count = length;
if identifier == "bool" return .TOKEN_BOOL; if identifier == "bool" return .TOKEN_BOOL;
if identifier == "Buffer" return .TOKEN_BUFFER;
if identifier == "case" return .TOKEN_CASE; if identifier == "case" return .TOKEN_CASE;
if identifier == "columnmajor" return .TOKEN_COLUMNMAJOR; if identifier == "columnmajor" return .TOKEN_COLUMNMAJOR;
if identifier == "const" return .TOKEN_CONST; if identifier == "const" return .TOKEN_CONST;
if identifier == "constant_buffer" return .TOKEN_CONSTANT_BUFFER; if identifier == "Constant_Buffer" return .TOKEN_CONSTANT_BUFFER;
if identifier == "continue" return .TOKEN_CONTINUE; if identifier == "continue" return .TOKEN_CONTINUE;
if identifier == "default" return .TOKEN_DEFAULT; if identifier == "default" return .TOKEN_DEFAULT;
if identifier == "directive" return .TOKEN_DIRECTIVE; if identifier == "directive" return .TOKEN_DIRECTIVE;
@@ -244,10 +245,10 @@ identifier_kind :: (using lexer : *Lexer) -> Token_Kind {
if identifier == "optional" return .TOKEN_OPTIONAL; if identifier == "optional" return .TOKEN_OPTIONAL;
if identifier == "out" return .TOKEN_OUT; if identifier == "out" return .TOKEN_OUT;
if identifier == "pixel" return .TOKEN_PIXEL; if identifier == "pixel" return .TOKEN_PIXEL;
if identifier == "properties" return .TOKEN_PROPERTIES;
if identifier == "return" return .TOKEN_RETURN; if identifier == "return" return .TOKEN_RETURN;
if identifier == "register" return .TOKEN_REGISTER; if identifier == "register" return .TOKEN_REGISTER;
if identifier == "struct" return .TOKEN_STRUCT; if identifier == "struct" return .TOKEN_STRUCT;
if identifier == "plex" return .TOKEN_STRUCT;
if identifier == "switch" return .TOKEN_SWITCH; if identifier == "switch" return .TOKEN_SWITCH;
if identifier == "true" return .TOKEN_TRUE; if identifier == "true" return .TOKEN_TRUE;
if identifier == "unorm" return .TOKEN_UNORM; if identifier == "unorm" return .TOKEN_UNORM;
@@ -264,12 +265,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 +312,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 +343,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 +359,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 +425,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 +480,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 +560,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,22 +572,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) {
lex :: (lexer : *Lexer, allocator : Allocator = context.allocator) -> Lexing_Result { if ctx.had_error {
lexer.result.tokens.allocator = allocator; return;
token : *Token = scan_next_token(lexer);
while token && token.kind != .TOKEN_EOF {
token = scan_next_token(lexer);
} }
return lexer.result; new_context := context;
new_context.allocator = allocator;
push_context new_context {
init_context_allocators();
defer clear_context_allocators();
lexer : Lexer;
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);
while token && token.kind != .TOKEN_EOF {
token = scan_next_token(*lexer);
}
}
} }
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;
} }
} }
@@ -522,7 +608,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;
} }
} }
@@ -661,40 +747,53 @@ 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;
begin_pos := 0;
token_string : string;
count := end.index - begin.index + end.length;
if indentation > 0 {
indent(builder, indentation);
for 0..count - 1 {
c := begin.source[it];
if c == #char "\n" {
append(builder, "\n");
indent(builder, indentation);
} else {
s : string;
s.count = 1;
s.data = *c;
print_to_builder(builder, "%", s);
}
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);
} }
} else { } else {
token_string = .{ count = count, data = begin.source }; begin_pos := 0;
indent(builder, indentation); token_string : string;
print_to_builder(builder, "%", token_string); count := end.index - begin.index + end.length;
if indentation > 0 {
indent(builder, indentation);
for 0..count - 1 {
c := begin.source[it];
if c == #char "\n" {
append(builder, "\n");
indent(builder, indentation);
} else {
s : string;
s.count = 1;
s.data = *c;
print_to_builder(builder, "%", s);
}
}
} else {
token_string = .{ count = count, data = begin.source };
indent(builder, indentation);
print_to_builder(builder, "%", token_string);
}
} }
} }
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);
} }

File diff suppressed because it is too large Load Diff

View File

@@ -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

762
Test.jai
View File

@@ -1,762 +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, context.allocator);
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, context.allocator);
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, context.allocator);
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: %\n", cyan(), test_case.path);
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", result.path);
}
case .Golden_File_Not_Found; {
print("%", red());
print("% failed with Golden File Not Found for stage %\n", result.path, stage);
}
case .StdOut; {
}
case .Golden_Output; {
print("%", yellow());
print("% output new golden file at %\n", result.path, result.golden_path);
}
case .Passed; {
print("%", green());
print("% passed %\n", result.path, stage);
}
case .Failed; {
print("%", red());
print("% failed %\n", result.path, 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);
}
}

View File

@@ -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;
@@ -259,4 +279,11 @@ int4x4 :: struct {
#foreign atan2 :: (float4, float4) -> float4; #foreign atan2 :: (float4, float4) -> float4;
#foreign atan2 :: (float4x4, float4x4) -> float4x4; #foreign atan2 :: (float4x4, float4x4) -> float4x4;
#foreign sample :: (Texture2D, float2, Sampler) -> 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;

3
check.bat Normal file
View File

@@ -0,0 +1,3 @@
@echo off
jai first.jai -natvis - check

View File

@@ -1,42 +1,95 @@
#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; args := options.compile_time_command_line;
profile : bool = false;
for arg : args {
if arg == {
case "check"; {
options.output_type = .NO_OUTPUT;
}
case "profile"; {
}
}
}
intercept_flags: Intercept_Flags;
plugins_to_create: [..] Plugin_To_Create;
if profile {
tracy : Plugin_To_Create;
tracy.name = "tracy";
array_add(*plugins_to_create, tracy);
}
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");
// array_add(*new_path, "modules/shader_parsing");
options.import_path = new_path; options.import_path = new_path;
options.output_executable_name = EXECUTABLE_NAME; options.output_executable_name = EXECUTABLE_NAME;
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();

View File

@@ -1,9 +1,34 @@
#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
- [x] Remove properties block and just use hinted constant buffers instead
```
props :: constant_buffer @properties {
[...]
}
```
- [ ] while loops
- [ ] for-each loops
- [ ] add parameters to hints (meta properties, resource binding indices if needed)
- [ ] consider @entry(stage) syntax instead of the forced keyword
- [ ] Add flags to compiler
- [ ] Generate output flag(s)
- [ ] Possibly final stage flag, so you can just call compile_file and it only does what you need.
- Probably this flag is about which stage you need as the _last_ and not which stages to do, as that doesn't make sense.
- [ ] Multiple output languages?
*/
add_define :: (env : *Environment, key : string) { add_define :: (env : *Environment, key : string) {
for define : env.defines { for define : env.defines {
@@ -27,10 +52,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;
@@ -56,11 +77,35 @@ Hint_Kind :: enum {
None; None;
Position; Position;
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;
@@ -83,60 +128,121 @@ Entry_Point :: struct {
return_value : Field; return_value : Field;
} }
Shader_Variant :: struct { Buffer_Kind :: enum {
text : string; Constant;
Structured;
}
Buffer :: struct {
kind : Buffer_Kind;
name : string;
fields : Static_Array(Field, 16);
hints : [..]Field_Hint;
buffer_index : u32;
}
Input_File :: struct {
source : string;
path : string;
}
Compiler_Context :: struct {
file : Input_File;
environment : Environment;
tokens : [..]Token;;
root : *AST_Node;
nodes : [..]AST_Node;
codegen_result_text : string;
typed_buffers : Static_Array(Type_Variable_Handle, 32);
// structured_buffers : Static_Array(Type_Variable_Handle, 16);
scope_stack : Scope_Stack;
type_variables : [..]Type_Variable;
vertex_entry_point : struct { 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;
} }
max_buffers :: 32;
buffers : Static_Array(Buffer, max_buffers);
had_error : bool;
messages : [..]Compiler_Message;
}
#add_context scratch_allocators : [2]Allocator;
#add_context scratch_id : int = 0;
init_context_allocators :: () {
if get_arena(context.scratch_allocators[0]) == null {
context.scratch_allocators[0] = make_arena(Megabytes(128));
context.scratch_allocators[1] = make_arena(Megabytes(128));
}
} }
Property_Field :: struct { clear_context_allocators :: () {
base_field : Field; if get_arena(context.scratch_allocators[0]) != null {
clear(context.scratch_allocators[0]);
// @Incomplete(nb): Editor information, min max, etc. clear(context.scratch_allocators[1]);
// This should also be compiled out for ship }
} }
Properties :: struct { get_scratch :: (conflict : Allocator = .{}) -> Scratch {
fields : [..]Property_Field; arena := cast(*Arena)conflict.data;
if arena == get_arena(context.scratch_allocators[0]) || context.scratch_id == 0 {
buffer_index : u32; context.scratch_id = 1;
return scratch_begin(*context.scratch_allocators[1]);
}
context.scratch_id = 0;
return scratch_begin(*context.scratch_allocators[0]);
} }
Constant_Buffer :: struct { record_error :: (result : *Compiler_Context, format : string, args : .. Any) {
register : int; error : Compiler_Message;
error.message_kind = .Error;
error.message = sprint(format, args);
name : string; array_add(*result.messages, error);
fields : Static_Array(Property_Field, 16);
buffer_index : u32;
} }
Shader_Variant_Collection :: struct { make_file :: (result : *Compiler_Context, path : string) -> Input_File {
properties : Properties; if !file_exists(path) {
record_error(result, "Unable to load file: %", path);
return .{};
}
file_string, ok := read_entire_file(path);
max_constant_buffers :: 16; if !ok {
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers); record_error(result, "Unable to load file: %", path);
return .{};
}
variants : [..]Shader_Variant; return make_file_from_string(file_string, path);
} }
Compilation_Result :: struct { make_file_from_string :: (source : string, path : string = "") -> Input_File {
messages : [..]Compiler_Message; input_file : Input_File;
had_error : bool; input_file.source = source;
input_file.path = path;
collection : Shader_Variant_Collection; return input_file;
} }
pretty_print_field :: (field : *Field) -> string { pretty_print_field :: (field : *Field) -> string {
@@ -153,7 +259,7 @@ Min_Field_Name :: 10;
pretty_print_field :: (builder : *String_Builder, field : *Field) { pretty_print_field :: (builder : *String_Builder, field : *Field) {
if field.name.count > 0 { if field.name.count > 0 {
print_to_builder(builder, "% ", field.name); print_to_builder(builder, "% ", field.name);
append(builder, "- "); append(builder, ": ");
} else { } else {
append(builder, "return - "); append(builder, "return - ");
} }
@@ -182,10 +288,17 @@ pretty_print_field :: (builder : *String_Builder, field : *Field) {
case .Struct; { case .Struct; {
print_to_builder(builder, "struct : % {", type.name); print_to_builder(builder, "struct : % {", type.name);
newline_after := type.children.count / 4;
for *child : type.children { for *child : type.children {
pretty_print_field(builder, child); pretty_print_field(builder, child);
if it_index < type.children.count - 1 { if it_index < type.children.count - 1 {
append(builder, " "); append(builder, ", ");
}
if it_index % newline_after == 0 {
append(builder, "\n");
indent(builder, 4);
} }
} }
@@ -215,11 +328,7 @@ pretty_print_field :: (builder : *String_Builder, field : *Field) {
} }
} }
type_variable_to_field :: (checker : *Semantic_Checker, variable : Type_Variable_Handle) -> Field { type_variable_to_field :: (ctx : *Compiler_Context, variable : *Type_Variable) -> Field {
return type_variable_to_field(checker, h2tv(checker, variable));
}
type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variable) -> Field {
field : Field; field : Field;
field.name = variable.name; field.name = variable.name;
@@ -252,14 +361,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(ctx.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(ctx.type_variables, find_result.type_variable);
for i : 0..type_var.child_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(ctx, child);
array_add(*type.children, child_field); array_add(*type.children, child_field);
} }
@@ -270,11 +379,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 starts_with(hint.ident_value, "target") { } else if lookup_hint(hint.ident_value) == .UV {
// @Incomplete(nb): Should be a lookup table somewhere field_hint.kind = .UV;
} else if lookup_hint(hint.ident_value) == .Target {
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;
@@ -285,7 +394,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);
} }
@@ -295,67 +405,50 @@ type_variable_to_field :: (checker : *Semantic_Checker, variable : *Type_Variabl
return field; return field;
} }
compile_file :: (compiler : *Shader_Compiler, path : string) -> Compilation_Result { type_variable_to_field :: (ctx : *Compiler_Context, variable : Type_Variable_Handle) -> Field {
result : Compilation_Result; return type_variable_to_field(ctx, from_handle(ctx.type_variables, variable));
}
lexer : Lexer; generate_buffer :: (ctx : *Compiler_Context, type_handle : Type_Variable_Handle, buffers : *Static_Array) {
variable := from_handle(ctx.type_variables, type_handle);
init_lexer_from_file(*lexer, path); buffer := array_add(buffers);
if lexer.result.had_error {
copy_messages(lexer.result.messages, *result.messages); if variable.type == {
result.had_error = true; case .CBuffer; {
return result; buffer.kind = .Constant;
}
case .Buffer; {
buffer.kind = .Structured;
}
}
buffer.name = variable.name;
for i : 0..variable.children.count - 1 {
child := variable.children[i];
field : Field = type_variable_to_field(ctx, from_handle(ctx.type_variables, child));
array_add(*buffer.fields, field);
} }
lex_result := lex(*lexer,, *temp); buffer.buffer_index = variable.resource_index;
if lex_result.had_error {
copy_messages(lex_result.messages, *result.messages); for hint : variable.source_node.hint_tokens {
result.had_error = true; field_hint : Field_Hint;
return result; field_hint.custom_hint_name = hint.ident_value;
field_hint.kind = .Custom;
array_add(*buffer.hints, field_hint);
}
}
generate_output_data :: (ctx : *Compiler_Context) {
if ctx.had_error {
return;
} }
parse_state : Parse_State; if ctx.vertex_entry_point.node {
init_parse_state(*parse_state, lex_result.tokens, lexer.path, context.allocator); ctx.vertex_entry_point.name = ctx.vertex_entry_point.node.name;
parse_result := parse(*parse_state); type_variable := from_handle(ctx.type_variables, ctx.vertex_entry_point.node.type_variable);
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;
@@ -363,77 +456,64 @@ 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, 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.typed_buffers {
variable := h2tv(check_result.type_variables, buffer_variable); generate_buffer(ctx, buffer_variable, *ctx.buffers);
cb := array_add(*result.collection.cbuffers);
for i : 0..variable.child_count - 1 {
child := variable.children[i];
field : Property_Field;
field.base_field = type_variable_to_field(*checker, h2tv(*checker, child));;
array_add(*cb.fields, field);
}
cb.buffer_index = variable.resource_index;
} }
find_result := find_symbol(*check_result.scope_stack, check_result.property_name, xx 1); if ctx.pixel_entry_point.node {
if find_result { ctx.pixel_entry_point.name = ctx.pixel_entry_point.node.name;
property_variable := h2tv(check_result.type_variables, find_result.type_variable);
for i : 0..property_variable.child_count - 1 { type_variable := from_handle(ctx.type_variables, ctx.pixel_entry_point.node.type_variable);
child := property_variable.children[i];
field := type_variable_to_field(*checker, h2tv(*checker, child));
prop_field : Property_Field;
prop_field.base_field = field;
array_add(*result.collection.properties.fields, prop_field);
}
result.collection.properties.buffer_index = property_variable.resource_index;
}
if checker.result.pixel_entry_point {
variant.pixel_entry_point.name = checker.result.pixel_entry_point.name;
type_variable := h2tv(*checker, checker.result.pixel_entry_point.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_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);
}
} }

1
modules/tracy Submodule

Submodule modules/tracy added at 9668d7b8ab

BIN
output.tracy Normal file

Binary file not shown.

View File

@@ -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

View File

@@ -0,0 +1,4 @@
vertex main :: () {
v : float2;
v.x = (2.0 + ((4.0 - 2.0) * 1.5)) * 3.0;
}

View File

@@ -1,5 +1,6 @@
vertex main :: () -> float4 @position { vertex main :: () -> float4 @position {
arr : [16].float4; arr : [16].float4;
arr[0] = float4(1,1,1); arr[0] = float4(1, 1, 1, 1);
return arr[0]; pos := arr[1];
return pos;
} }

View File

@@ -0,0 +1,5 @@
vertex main :: () {
a : float2;
b : float2;
(a + b).x = 2.0;
}

View File

@@ -0,0 +1,10 @@
P :: struct {
v : float2;
}
vertex main :: () {
p : P;
p.v.x.y = 2.0;
// v : float2;
// v.x.y.z = 2.0;
}

View File

@@ -1,4 +1,4 @@
properties { properties :: Constant_Buffer @properties {
color : float4; color : float4;
} }

View 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);
}

11
test/buffers.ink Normal file
View File

@@ -0,0 +1,11 @@
property_buffer :: Buffer {
color : float4;
}
const_buffer :: Constant_Buffer {
color : float4;
}
pixel main :: (index : int) {
return property_buffer[index].color;
}

34
test/builtin_types.ink Normal file
View 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;
}

View File

@@ -0,0 +1,6 @@
scope (global) [
[vertex__vs_main] : ()
scope (vertex__vs_main) [
[v] : float2
]
]

7
test/check/arrays.golden Normal file
View File

@@ -0,0 +1,7 @@
scope (global) [
[vertex__vs_main] : () -> float4
scope (vertex__vs_main) [
[pos] : float4
[arr] : [16].float4
]
]

View File

@@ -0,0 +1,6 @@
test/bad_double_access.ink:7,4: error: Attempting to access a field on a primitive type 'float'.
p.v.x.
^
declaration:
x: float


View 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
]
]

View 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
]
]

View File

@@ -0,0 +1,10 @@
scope (global) [
[vertex__vs_main] : ()
[p] : {v : float2}
scope (p) [
[v] : float2
]
scope (vertex__vs_main) [
[x] : float
]
]

View File

@@ -1,4 +1,4 @@
scope (global) [ scope (global) [
[Foo] : {} [Foo] : {}
scope (Foo) [] scope (Foo) []
] ]

View File

@@ -0,0 +1,6 @@
test/float_if_cond.ink:0,0: error: Type of expression in if condition has to be bool.
if 1.0
^^^
1.0 has type float


View File

@@ -0,0 +1,10 @@
scope (global) [
[vertex__vs_main] : ()
scope (vertex__vs_main) [
[x] : int
scope (block) [
[i] : int
scope (block) []
]
]
]

View File

@@ -0,0 +1,4 @@
test/for_index_outside.ink:6,0: error: Use of undeclared symbol 'i'
 i += 1;
^


View File

@@ -0,0 +1,8 @@
test/functions_with_same_name.ink:2,0: error: Redeclaration of 'foo'
 foo :: () {
^^^
test/functions_with_same_name.ink:1,0: info: Here is the first declaration of 'foo'
 foo :: () {
^^^


View File

@@ -0,0 +1,13 @@
scope (global) [
[vertex__vs_main] : (pos : float4) -> float4
[props] : {projection : float4x4, view : float4x4}
scope (props) [
[projection] : float4x4
[view] : float4x4
]
scope (vertex__vs_main) [
[pos] : float4
[mv] : float4
[mvp] : float4
]
]

View File

@@ -0,0 +1,6 @@
test/if_cond_assign.ink:0,0: error: Type of expression in if condition has to be bool.
if 0 = 100
^^^^^^
if 0 = 100 { has type int


View File

@@ -0,0 +1,8 @@
scope (global) [
[pixel__ps_main] : ()
scope (pixel__ps_main) [ scope (block) [
[alpha_color] : float4
[f] : float
]
]
]

View File

@@ -0,0 +1,4 @@
scope (global) [
[vertex__vs_console_main] : ()
scope (vertex__vs_console_main) []
]

8
test/check/ifdefs.golden Normal file
View 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) []
]

View 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
]
]

View File

@@ -0,0 +1,9 @@
scope (global) [
[vertex__vs_main] : (pos : float3) -> float4
scope (vertex__vs_main) [
[pos] : float3
scope (block) [ scope (block) []
scope (block) []
]
]
]

View File

@@ -0,0 +1,6 @@
test/non_bool_cond.ink:0,0: error: Type of expression in if condition has to be bool.
if 1.0
^^^
1.0 has type float


View File

@@ -1,7 +1,7 @@
scope (global) [ scope (global) [
[foo] : (f : Foo) -> float [foo] : (f : Foo) -> float
[vertex__vs_main] : () [vertex__vs_main] : ()
[Foo] : {some_data : float} [Foo] : {some_data : float}
scope (Foo) [ scope (Foo) [
[some_data] : float [some_data] : float
] ]

View 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) []
]

View File

@@ -0,0 +1,8 @@
test/redeclared_variable.ink:3,0: error: Redeclaration of 'x'
 x : float = 5.0
^
test/redeclared_variable.ink:2,0: info: Here is the first declaration of 'x'
 x : float = 1.0
^


View File

@@ -0,0 +1,8 @@
scope (global) [
[vertex__vs_main] : ()
scope (vertex__vs_main) [
[b] : float2
[x] : float
[a] : float2
]
]

View File

@@ -0,0 +1,9 @@
scope (global) [
[vertex__vs_main] : (pos : float3) -> float4
scope (vertex__vs_main) [
[pos] : float3
scope (block) []
scope (block) []
scope (block) []
]
]

View File

@@ -0,0 +1,7 @@
scope (global) [
[vertex__vs_main] : (pos : float3) -> float4
scope (vertex__vs_main) [
[pos] : float3
scope (block) []
]
]

View File

@@ -0,0 +1,8 @@
scope (global) [
[vertex__vs_main] : (pos : float3) -> float4
scope (vertex__vs_main) [
[pos] : float3
scope (block) []
scope (block) []
]
]

View File

@@ -1,5 +1,5 @@
scope (global) [ scope (global) [
[Data] : {color : float4} [Data] : {color : float4}
[vertex__vs_main] : () [vertex__vs_main] : ()
scope (Data) [ scope (Data) [
[color] : float4 [color] : float4

View File

@@ -1,6 +1,6 @@
test/struct_access_primitive_type.shd:3,0: error: Attempting to access a field on a primitive type 'int'. test/struct_access_primitive_type.ink:3,0: error: Attempting to access a field on a primitive type 'int'.
x.d = 4; x.d = 4;
^ ^
declaration: declaration:
x : int = 5 x : int = 5
 

View File

@@ -1,7 +1,7 @@
scope (global) [ scope (global) [
[Bar] : {t : Foo} [Bar] : {t : Foo}
[vertex__vs_main] : () [vertex__vs_main] : ()
[Foo] : {color : float4} [Foo] : {color : float4}
scope (Foo) [ scope (Foo) [
[color] : float4 [color] : float4
] ]

View File

@@ -0,0 +1,6 @@
test/temp_access.ink:5,10: error: Cannot assign to an lvalue.
 (a + b).x = 2.0;
^^^^^^^^^^^


View File

@@ -0,0 +1,4 @@
test/type_as_variable_name.ink:2,0: error: Invalid variable name 'int'
 int : float = 4.0
^^^


10
test/check/unary.golden Normal file
View 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
]
]

View File

@@ -0,0 +1,7 @@
test/undeclared_function.ink:2,0: error: Attempt to call undeclared function 'foo'.
 foo();
^^^


View File

@@ -0,0 +1,4 @@
test/undeclared_symbol.ink:2,10: error: Use of undeclared symbol 'f'
 b : int = f;
^


View File

@@ -1,4 +1,4 @@
test/unknown_overload.shd:6,0: error: Procedure call did not match any of the possible overloads for 'foo' test/unknown_overload.ink:6,0: error: Procedure call did not match any of the possible overloads for 'foo'
 found:  found:
foo(v, v); foo(v, v);
^^^ ^^^
@@ -7,10 +7,10 @@
 foo(v, v);  foo(v, v);
^ ^
 Possible overloads:  Possible overloads:
 foo :: (v1 : float3, v2 : float3) { (test/unknown_overload.shd:1)  foo :: (v1 : float3, v2 : float3) { (test/unknown_overload.ink:1)
 foo :: (v1 : float2, v2 : float2, v3 : float2) { (test/unknown_overload.shd:2)  foo :: (v1 : float2, v2 : float2, v3 : float2) { (test/unknown_overload.ink:2)
test/unknown_overload.shd:6,4: error: Type mismatch. Expected float3 got float test/unknown_overload.ink:6,4: error: Type mismatch. Expected float3 got float
 found:  found:
foo(v, v); foo(v, v);
^ ^
@@ -20,7 +20,7 @@
got: got:
v : float = 2.0 v : float = 2.0
test/unknown_overload.shd:6,7: error: Type mismatch. Expected float3 got float test/unknown_overload.ink:6,7: error: Type mismatch. Expected float3 got float
 found:  found:
foo(v, v); foo(v, v);
^ ^

View File

@@ -1,15 +1,15 @@
test/wrong_argument_count.shd:5,19: error: Use of undeclared symbol 'w' test/wrong_argument_count.ink:5,19: error: Use of undeclared symbol 'w'
 return x * y * z * w;  return x * y * z * w;
^ ^
test/wrong_argument_count.shd:9,0: error: Procedure call did not match any of the possible overloads for 'foo' test/wrong_argument_count.ink:9,0: error: Procedure call did not match any of the possible overloads for 'foo'
 found:  found:
foo(2.0, 3.0); foo(2.0, 3.0);
^^^ ^^^
 Possible overloads:  Possible overloads:
 foo :: (x : float, y : float, z : float) -> float { (test/wrong_argument_count.shd:1)  foo :: (x : float, y : float, z : float) -> float { (test/wrong_argument_count.ink:1)
 Not enough arguments: Wanted 3, got 2.  Not enough arguments: Wanted 3, got 2.
 foo :: (x : float, y : float, z : float, w : float) -> float { (test/wrong_argument_count.shd:4)  foo :: (x : float, y : float, z : float, w : float) -> float { (test/wrong_argument_count.ink:4)
 Not enough arguments: Wanted 4, got 2.  Not enough arguments: Wanted 4, got 2.

View File

@@ -0,0 +1,30 @@
test/wrong_multiply.ink:4,18: error: Procedure call did not match any of the possible overloads for 'float4'
 found:
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
^^^^^^
 While matching argument 2 in function call.
 result : float4 = float4(1.0, foo * res, 0.0, 1.0);
^
 Possible overloads:
 float4 :: (float, float, float, float)
 float4 :: (float2, float2)
 float4 :: (float2, float, float)
 float4 :: (float, float2, float)
 float4 :: (float, float, float2)
 float4 :: (float, float3)
 float4 :: (float3, float)
 float4 :: (float4)
 float4 :: (float)
test/wrong_multiply.ink:4,34: error: Type mismatch. Expected float got float2
 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);


View File

@@ -1,4 +1,4 @@
test/wrong_type_for_function.shd:11,17: error: Procedure call did not match any of the possible overloads for 'float4' test/wrong_type_for_function.ink:11,17: error: Procedure call did not match any of the possible overloads for 'float4'
 found:  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 @@
 color : float4 = float4(y, 1.0, 1.0, 1.0);  color : float4 = float4(y, 1.0, 1.0, 1.0);
^ ^
 Possible overloads:  Possible overloads:
 foreign float4 :: (float, float, float, float) -> float4; (test/wrong_type_for_function.shd:78)  float4 :: (float, float, float, float)
 float4 :: (float2, float2)
 float4 :: (float2, float, float)
 float4 :: (float, float2, float)
 float4 :: (float, float, float2)
 float4 :: (float, float3)
 float4 :: (float3, float)
 float4 :: (float4)
 float4 :: (float)
test/wrong_type_for_function.shd:11,24: error: Type mismatch. Expected float got float2 test/wrong_type_for_function.ink:11,24: error: Type mismatch. Expected float got float2
 found:  found:
color : float4 = float4(y, 1.0, 1.0, 1.0); color : float4 = float4(y, 1.0, 1.0, 1.0);
^ ^

47
test/check_all.suite Normal file
View File

@@ -0,0 +1,47 @@
test/assign_arithmetic_expression.ink check
test/arithmetic_parens.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/bad_double_access.ink check
test/double_access.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/redeclared_variable.ink check
test/rvalue_binary.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/temp_access.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

View File

@@ -0,0 +1,6 @@
void vs_main()
{
float2 v;
v.x = (2.0f + ((4.0f - 2.0f) * 1.5f)) * 3.0f;
}

View File

@@ -0,0 +1,6 @@
float4 vs_main() : SV_POSITION
{
float4 arr[16];
return arr[0];
}

View File

@@ -1,4 +1,4 @@
cbuffer __PROPERTIES : register(b0) cbuffer properties : register(b0)
{ {
float4 color; float4 color;
} }
@@ -10,6 +10,6 @@ float3 vs_main(float3 pos : POSITION) : SV_POSITION
float4 ps_main() : SV_TARGET float4 ps_main() : SV_TARGET
{ {
return color; return properties.color;
} }

View 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;
}

View File

@@ -2,6 +2,6 @@ void vs_main()
{ {
float x = 5.0f; float x = 5.0f;
float y = 3000.0f; float y = 3000.0f;
float z = y * y + x; float z = (y * y) + x;
} }

View 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);
}

View 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);
}

View File

@@ -1,4 +1,2 @@
struct Foo; struct Foo {};
struct Foo {}

View File

@@ -0,0 +1,13 @@
cbuffer props : register(b0)
{
float4x4 projection;
float4x4 view;
}
float4 vs_main(float4 pos : POSITION) : SV_POSITION
{
float4 mv = mul(props.view, pos);
float4 mvp = mul(props.projection, mv);
return mvp;
}

View File

@@ -0,0 +1,7 @@
void ps_main()
{
float4 alpha_color = float4(1, 0, 0, 1);
float f = 2.0f;
}

View File

@@ -0,0 +1,4 @@
void vs_console_main()
{
}

View File

@@ -0,0 +1,9 @@
void ps_main()
{
}
void vs_skinning_main()
{
float x = 5.0f;
}

View 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);
}

Some files were not shown because too many files have changed in this diff Show More