Added Ink and ncore to modules
This commit is contained in:
202
README.md
Normal file
202
README.md
Normal file
@@ -0,0 +1,202 @@
|
||||
# Shader Compiler
|
||||
|
||||
A compiler for a custom, platform-agnostic shader language that compiles to platform specific shader languages (currently only HLSL).
|
||||
|
||||
## Features
|
||||
|
||||
A simple passthrough shader (`passthrough.shd`) can look as follows
|
||||
```hlsl
|
||||
vertex main :: (position : float4 @position) -> float4 {
|
||||
return position;
|
||||
}
|
||||
|
||||
pixel main :: () -> float4 @target {
|
||||
return float4(1, 1, 1, 1);
|
||||
}
|
||||
```
|
||||
The shader contains both vertex and pixel shader code, where you specify the entry points as above. Entry points can have any and overlapping names, which will be exposed in the meta data of a compiled shader.
|
||||
There is basic support for most HLSL built-in math functions for the following types:
|
||||
- Scalar types: int, float
|
||||
- Vector types: float2, float3, float4, int2, int3, int4
|
||||
- Matrices: float4x4
|
||||
All of the above can be constructed with their namesake constructors i.e. `float4(x, y, z, w);`.
|
||||
We also support Samplers and Texture2D
|
||||
|
||||
If you want to declare and use variables you can do it as follows
|
||||
```hlsl
|
||||
x : float = 2.0; // no 'f' suffix required or even supported (it gives an error)
|
||||
y : float = 4.0;
|
||||
v : float2 = float2(x, y);
|
||||
v2 := float2(x, y);
|
||||
```
|
||||
|
||||
You can also do arithmetic as you would expect
|
||||
```
|
||||
x : float = 2.0 * 4.0 + (3 * 2); // int literals automatically convert to floats.
|
||||
```
|
||||
|
||||
There is basic struct support
|
||||
```hlsl
|
||||
Camera_Data :: struct {
|
||||
projection : float4x4;
|
||||
view : float4x4;
|
||||
}
|
||||
```
|
||||
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
|
||||
properties {
|
||||
projection : float4x4;
|
||||
view : float4x4;
|
||||
}
|
||||
```
|
||||
which will be exposed in the compiled result. `properties` can be renamed to a custom/shorter name like
|
||||
```
|
||||
p :: properties {
|
||||
...
|
||||
|
||||
|
||||
```
|
||||
|
||||
You can also define constant buffers
|
||||
|
||||
```
|
||||
camera :: constant_buffer {
|
||||
projection : float4x4;
|
||||
view : float4x4;
|
||||
}
|
||||
```
|
||||
|
||||
## Jai Usage Example
|
||||
|
||||
To compile a shader and use the result, you can do the following in jai
|
||||
```jai
|
||||
|
||||
// In the future, you can pass environment defines to the compiler.
|
||||
ctx : Compiler_Context;
|
||||
compile_file(*compiler, "shader.shd", allocator);
|
||||
|
||||
if ctx.had_error {
|
||||
log_error("%\n", report_messages(ctx.messages),, temp);
|
||||
return;
|
||||
}
|
||||
|
||||
// The ctx now contains all the needed information like the source text, entry points, constant buffers etc.
|
||||
```
|
||||
|
||||
When parsing a shader you get the following struct as a result
|
||||
```
|
||||
Compiler_Context :: struct {
|
||||
file : Input_File;
|
||||
|
||||
environment : Environment;
|
||||
|
||||
tokens : [..]Token;;
|
||||
root : *AST_Node;
|
||||
nodes : [..]AST_Node;
|
||||
|
||||
codegen_result_text : string;
|
||||
|
||||
constant_buffers : Static_Array(Type_Variable_Handle, 16);
|
||||
|
||||
scope_stack : Scope_Stack;
|
||||
type_variables : [..]Type_Variable;
|
||||
|
||||
property_name : string;
|
||||
|
||||
vertex_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
input : [..]Field;
|
||||
}
|
||||
|
||||
pixel_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
return_value : Field;
|
||||
}
|
||||
|
||||
properties : Properties;
|
||||
|
||||
max_constant_buffers :: 16;
|
||||
|
||||
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
|
||||
|
||||
had_error : bool;
|
||||
messages : [..]Compiler_Message;
|
||||
}
|
||||
|
||||
Constant_Buffer :: struct {
|
||||
name : string;
|
||||
|
||||
fields : Static_Array(Property_Field, 16);
|
||||
|
||||
// hints : Field_Hint; // optional hint...
|
||||
hints : [..]Field_Hint;
|
||||
|
||||
buffer_index : u32;
|
||||
}
|
||||
|
||||
Properties :: struct {
|
||||
fields : [..]Property_Field;
|
||||
|
||||
buffer_index : u32;
|
||||
}
|
||||
```
|
||||
|
||||
A field is just a simple struct with a name and type (and hints such as semantics or custom hints in the future)
|
||||
```
|
||||
Field_Hint :: struct {
|
||||
kind : Hint_Kind;
|
||||
|
||||
target_index : int;
|
||||
custom_hint_name : string;
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
name : string;
|
||||
|
||||
type : Field_Type;
|
||||
hints : [..]Field_Hint;
|
||||
}
|
||||
|
||||
Field_Kind :: enum {
|
||||
Int :: 0;
|
||||
Half :: 1;
|
||||
Float :: 2;
|
||||
Double :: 3;
|
||||
Texture2D :: 8;
|
||||
Sampler :: 9;
|
||||
|
||||
Function;
|
||||
Struct;
|
||||
Array; // Not yet supported
|
||||
}
|
||||
|
||||
Field_Type :: struct {
|
||||
kind : Field_Kind;
|
||||
|
||||
name : string; //@Note(niels): for structs
|
||||
|
||||
children : [..]Field;
|
||||
}
|
||||
|
||||
Hint_Kind :: enum {
|
||||
None;
|
||||
|
||||
Position;
|
||||
Target;
|
||||
|
||||
Custom;
|
||||
}
|
||||
```
|
||||
|
||||
## Notable missing features
|
||||
|
||||
- While
|
||||
- Arrays
|
||||
- Multiple render targets
|
||||
- Custom buffers/structured buffers
|
||||
- Interpolation specifiers
|
||||
- Proper variant handling with environment defines
|
||||
- Importing files such as shared utils etc. with something other than textual `#load`
|
||||
581
ast.jai
Normal file
581
ast.jai
Normal file
@@ -0,0 +1,581 @@
|
||||
/////////////////////////////////////
|
||||
//~ nbr: Node data structure
|
||||
//
|
||||
// [ ] Add a way to infer or get file path from a node
|
||||
|
||||
AST_Kind :: enum {
|
||||
Program;
|
||||
Function;
|
||||
Return;
|
||||
|
||||
//==
|
||||
// Directives
|
||||
If_Directive;
|
||||
|
||||
Access;
|
||||
Call;
|
||||
Struct;
|
||||
If;
|
||||
For;
|
||||
CBuffer;
|
||||
Buffer;
|
||||
FieldList;
|
||||
ArgList;
|
||||
Variable;
|
||||
Binary;
|
||||
Unary;
|
||||
Integer;
|
||||
Float;
|
||||
Expression_Statement;
|
||||
Field;
|
||||
Unnamed_Field;
|
||||
Block;
|
||||
Error;
|
||||
}
|
||||
|
||||
AST_Node :: struct {
|
||||
kind : AST_Kind;
|
||||
|
||||
// @Note(niels): Children nodes can be interpreted as anything useful.
|
||||
// for an if-statement we would have at most 2 children
|
||||
// a property block has a field list child node which has
|
||||
// a child node for each field declaration etc.
|
||||
children : [..]*AST_Node;
|
||||
parent : *AST_Node;
|
||||
|
||||
// @Note(niels): Every node can have a name, but most nodes don't. A function or field declaration has one,
|
||||
// but an if-statement does not
|
||||
name : string;
|
||||
|
||||
integer_value : int;
|
||||
float_value : float;
|
||||
|
||||
token : Token;
|
||||
|
||||
array_field : bool;
|
||||
|
||||
source_location : Source_Range;
|
||||
|
||||
type_variable : Type_Variable_Handle;
|
||||
|
||||
foreign_declaration : bool;
|
||||
|
||||
// @Incomplete(nb): Change this to just be children and a single token_data field,
|
||||
// then we can add new node types (hint, typespec, operator) and have them
|
||||
// as children instead.
|
||||
hint_tokens : [..]Token;
|
||||
|
||||
vertex_entry_point : bool;
|
||||
pixel_entry_point : bool;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Pretty printing
|
||||
pretty_print_call :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
append(builder, "(");
|
||||
append(builder, node.name);
|
||||
if node.children.count > 0 {
|
||||
append(builder, " ");
|
||||
pretty_print_children(node.children[0], indentation, builder, flags = 0, skip_indent = true);
|
||||
}
|
||||
append(builder, ")");
|
||||
}
|
||||
|
||||
pretty_print_arglist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
append(builder, "[");
|
||||
|
||||
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
||||
|
||||
append(builder, "]");
|
||||
}
|
||||
|
||||
pretty_print_fieldlist :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
|
||||
append(builder, "[");
|
||||
pretty_print_children(node, indentation + 1, builder, flags = .NewLine);
|
||||
|
||||
append(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));
|
||||
|
||||
if node.kind != .Unnamed_Field && node.token.ident_value.count > 0 {
|
||||
if node.array_field {
|
||||
append(builder, " [");
|
||||
pretty_print_node(node.children[0], indentation, builder, true);
|
||||
append(builder, "].");
|
||||
print_to_builder(builder, "%", node.token.ident_value);
|
||||
} else {
|
||||
print_to_builder(builder, " %", node.token.ident_value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for hint : node.hint_tokens {
|
||||
if hint.string_value.count > 0 {
|
||||
print_to_builder(builder, " (@%)", hint.string_value);
|
||||
}
|
||||
}
|
||||
|
||||
if !node.array_field && node.children.count > 0 {
|
||||
append(builder, " ");
|
||||
pretty_print_node(node.children[0], indentation, builder, true);
|
||||
}
|
||||
|
||||
append(builder, ")");
|
||||
}
|
||||
|
||||
Children_Print_Flags :: enum_flags {
|
||||
NewLine :: 1 << 0;
|
||||
Separator :: 1 << 1;
|
||||
Space :: 1 << 2;
|
||||
Dont_Skip_Indent_On_First :: 1 << 3;
|
||||
}
|
||||
|
||||
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 {
|
||||
return;
|
||||
}
|
||||
|
||||
children := parent.children;
|
||||
for child : children {
|
||||
// if it_index > 0 {
|
||||
// indent(builder, indentation);
|
||||
// }
|
||||
|
||||
if !child continue;
|
||||
|
||||
|
||||
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 flags & .Separator {
|
||||
append(builder, ",");
|
||||
}
|
||||
|
||||
append(builder, " ");
|
||||
|
||||
if flags & .NewLine {
|
||||
append(builder, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
op_to_string :: (oper : Token) -> string {
|
||||
if oper.kind == {
|
||||
case .TOKEN_PLUS;
|
||||
return "+";
|
||||
case .TOKEN_MINUS;
|
||||
return "-";
|
||||
case .TOKEN_STAR;
|
||||
return "*";
|
||||
case .TOKEN_SLASH;
|
||||
return "/";
|
||||
case .TOKEN_MINUSEQUALS;
|
||||
return "-=";
|
||||
case .TOKEN_PLUSEQUALS;
|
||||
return "+=";
|
||||
case .TOKEN_DIVEQUALS;
|
||||
return "/=";
|
||||
case .TOKEN_TIMESEQUALS;
|
||||
return "*=";
|
||||
case .TOKEN_MODEQUALS;
|
||||
return "%=";
|
||||
case .TOKEN_ISEQUAL;
|
||||
return "==";
|
||||
case .TOKEN_ASSIGN;
|
||||
return "=";
|
||||
case .TOKEN_ISNOTEQUAL;
|
||||
return "!=";
|
||||
case .TOKEN_LOGICALOR;
|
||||
return "||";
|
||||
case .TOKEN_LOGICALAND;
|
||||
return "&&";
|
||||
case .TOKEN_LESS;
|
||||
return "<";
|
||||
case .TOKEN_LESSEQUALS;
|
||||
return "<=";
|
||||
case .TOKEN_GREATER;
|
||||
return ">";
|
||||
case .TOKEN_GREATEREQUALS;
|
||||
return ">=";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
pretty_print_binary :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
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;
|
||||
|
||||
print_to_builder(builder, op_to_string(op));
|
||||
pretty_print_node(node.children[0], 0, builder);
|
||||
}
|
||||
|
||||
print_return_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
append(builder, "(return ");
|
||||
|
||||
pretty_print_children(node, indentation, builder);
|
||||
|
||||
append(builder, ")");
|
||||
}
|
||||
|
||||
pretty_print_if :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
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] {
|
||||
pretty_print_node(node.children[0], 0, builder);
|
||||
}
|
||||
}
|
||||
|
||||
pretty_print_node :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if node.kind == {
|
||||
case .Return; {
|
||||
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 .ArgList; {
|
||||
pretty_print_arglist(node, indentation + 2, builder, skip_indent);
|
||||
}
|
||||
case .FieldList; {
|
||||
pretty_print_fieldlist(node, indentation + 2, builder, skip_indent);
|
||||
}
|
||||
case .Field; {
|
||||
pretty_print_field(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Unnamed_Field; {
|
||||
pretty_print_field(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Block; {
|
||||
pretty_print_block(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Binary; {
|
||||
pretty_print_binary(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Access; {
|
||||
pretty_print_access(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Unary; {
|
||||
pretty_print_unary(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Variable; {
|
||||
pretty_print_variable(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Expression_Statement; {
|
||||
print_expression_statement(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Integer; {
|
||||
print_to_builder(builder, "%", node.integer_value, skip_indent);
|
||||
}
|
||||
case .Float; {
|
||||
print_to_builder(builder, "%", node.float_value, skip_indent);
|
||||
}
|
||||
case .Call; {
|
||||
pretty_print_call(node, indentation, builder, skip_indent);
|
||||
}
|
||||
case .Error; {
|
||||
print_to_builder(builder, "(error \"%\")", node.name, skip_indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pretty_print_variable :: (node : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
print_to_builder(builder, "%", node.name);
|
||||
|
||||
for child : node.children {
|
||||
if child.kind == .Variable {
|
||||
append(builder, ".");
|
||||
pretty_print_variable(child, indentation, builder, skip_indent = true);
|
||||
} else if child.kind == .Unary {
|
||||
append(builder, "[");
|
||||
pretty_print_node(child.children[0], 0, builder);
|
||||
append(builder, "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pretty_print_declaration :: (declaration : *AST_Node, indentation : int, builder : *String_Builder, skip_indent := false) {
|
||||
if !skip_indent {
|
||||
indent(builder, indentation);
|
||||
}
|
||||
append(builder, "(");
|
||||
|
||||
if declaration.foreign_declaration {
|
||||
append(builder, "foreign ");
|
||||
}
|
||||
|
||||
if declaration.kind == .Function {
|
||||
append(builder, "fun ");
|
||||
}
|
||||
|
||||
if declaration.vertex_entry_point {
|
||||
append(builder, "vertex ");
|
||||
}
|
||||
|
||||
if declaration.pixel_entry_point {
|
||||
append(builder, "pixel ");
|
||||
}
|
||||
|
||||
if declaration.kind == .If_Directive {
|
||||
append(builder, "#if ");
|
||||
}
|
||||
|
||||
if declaration.kind == .Struct {
|
||||
append(builder, "struct ");
|
||||
} else if declaration.kind == .CBuffer {
|
||||
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);
|
||||
}
|
||||
}
|
||||
// if declaration.kind != .If_Directive {
|
||||
// print_to_builder(builder, "%", declaration.name);
|
||||
// }
|
||||
}
|
||||
|
||||
if declaration.kind == .Function && declaration.token.kind == .TOKEN_IDENTIFIER{
|
||||
print_to_builder(builder, " -> %", declaration.token.ident_value);
|
||||
for hint : declaration.hint_tokens {
|
||||
if hint.string_value.count > 0 {
|
||||
print_to_builder(builder, " (@%)", hint.string_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if declaration.children.count > 0 {
|
||||
if declaration.kind == .If_Directive {
|
||||
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, ")");
|
||||
|
||||
}
|
||||
|
||||
pretty_print_ast :: (root : *AST_Node, allocator : Allocator) -> string {
|
||||
builder : String_Builder;
|
||||
init_string_builder(*builder,, allocator);
|
||||
|
||||
indentation := 0;
|
||||
|
||||
append(*builder, "(");
|
||||
append(*builder, "program\t\n");
|
||||
|
||||
indentation += 1;
|
||||
|
||||
declarations := root.children;
|
||||
|
||||
for declaration : declarations {
|
||||
pretty_print_declaration(declaration, indentation, *builder);
|
||||
|
||||
if it_index < declarations.count - 1 {
|
||||
append(*builder, "\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
append(*builder, ")");
|
||||
|
||||
return builder_to_string(*builder,, allocator);
|
||||
}
|
||||
|
||||
#scope_file
|
||||
indent :: (builder : *String_Builder, indentation : int) {
|
||||
for 0..indentation - 1 {
|
||||
append(builder, " ");
|
||||
}
|
||||
}
|
||||
664
codegen.jai
Normal file
664
codegen.jai
Normal file
@@ -0,0 +1,664 @@
|
||||
/////////////////////////////////////
|
||||
//~ nbr:
|
||||
//
|
||||
|
||||
/////////////////////////////////////
|
||||
//~ nbr: Codegen TODOs
|
||||
//
|
||||
|
||||
Output_Language :: enum {
|
||||
HLSL;
|
||||
GLSL; // @Incomplete
|
||||
MLSL; // @Incomplete
|
||||
// SPIRV; // @Incomplete: Should we do this?
|
||||
}
|
||||
|
||||
Codegen_State :: struct {
|
||||
path : string;
|
||||
|
||||
current_scope : Scope_Handle;
|
||||
|
||||
output_language : Output_Language;
|
||||
|
||||
builder : String_Builder;
|
||||
|
||||
ctx : *Compiler_Context;
|
||||
}
|
||||
|
||||
Reserved_HLSL_Words :: string.[
|
||||
"texture",
|
||||
"sampler",
|
||||
"matrix",
|
||||
"line",
|
||||
"precise",
|
||||
"shared",
|
||||
"triangle",
|
||||
"triangleadj",
|
||||
];
|
||||
|
||||
Reserved_MLSL_Words :: string.[
|
||||
""
|
||||
];
|
||||
|
||||
Reserved_GLSL_Words :: string.[
|
||||
""
|
||||
];
|
||||
|
||||
init_codegen_state :: (state : *Codegen_State, ctx : *Compiler_Context, output_language : Output_Language) {
|
||||
state.current_scope = cast(Scope_Handle)1;
|
||||
state.output_language = output_language;
|
||||
init_string_builder(*state.builder);
|
||||
}
|
||||
|
||||
indent :: (state : *Codegen_State, indentation : int) {
|
||||
for 1..indentation append(*state.builder, " ");
|
||||
}
|
||||
|
||||
hlsl_type_to_string :: (variables : []Type_Variable, type_handle : Type_Variable_Handle) -> string {
|
||||
return hlsl_type_to_string(variables, from_handle(variables, type_handle));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, field));
|
||||
|
||||
print_to_builder(*state.builder, "%", node.name);
|
||||
|
||||
if field.type == .Sampler {
|
||||
print_to_builder(*state.builder, " : register(s%)", field.resource_index);
|
||||
}
|
||||
|
||||
if field.type == .Texture2D {
|
||||
print_to_builder(*state.builder, " : register(t%)", field.resource_index);
|
||||
}
|
||||
|
||||
if node.children.count == 1 {
|
||||
child := node.children[0];
|
||||
|
||||
if field.type == .Array {
|
||||
append(*state.builder, "[");
|
||||
emit_node(state, child, 0);
|
||||
append(*state.builder, "]");
|
||||
} else {
|
||||
print_to_builder(*state.builder, " = ");
|
||||
emit_node(state, child, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if node.parent.kind == .Block {
|
||||
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);
|
||||
}
|
||||
|
||||
for hint : node.hint_tokens {
|
||||
if lookup_hint(hint.ident_value) == .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) {
|
||||
previous_scope := state.current_scope;
|
||||
|
||||
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);
|
||||
|
||||
if it_index < node.children.count {
|
||||
append(*state.builder, "\n");
|
||||
}
|
||||
}
|
||||
state.current_scope = previous_scope;
|
||||
}
|
||||
|
||||
emit_call :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||
indent(state, indentation);
|
||||
|
||||
if node.name == "sample" {
|
||||
assert(node.children.count > 0);
|
||||
args := node.children[0];
|
||||
|
||||
emit_node(state, args.children[0], 0);
|
||||
append(*state.builder, ".");
|
||||
print_to_builder(*state.builder, "Sample(");
|
||||
|
||||
for i : 1..args.children.count - 1 {
|
||||
child := args.children[i];
|
||||
|
||||
emit_node(state, child, 0);
|
||||
|
||||
if i != args.children.count - 1 {
|
||||
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 {
|
||||
print_to_builder(*state.builder, "%(", node.name);
|
||||
|
||||
if node.children.count > 0 {
|
||||
args := node.children[0];
|
||||
|
||||
for child : args.children {
|
||||
emit_node(state, child, 0);
|
||||
|
||||
if it_index != args.children.count - 1 {
|
||||
append(*state.builder, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
append(*state.builder, ")");
|
||||
}
|
||||
|
||||
emit_function :: (state : *Codegen_State, node : *AST_Node, indentation : int, emit_body := true) {
|
||||
name := get_actual_function_name(node);
|
||||
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.");
|
||||
if !find_result {
|
||||
message : Compiler_Message;
|
||||
message.message_kind = .Internal_Error;
|
||||
message.path = state.path;
|
||||
message.message = "Attempting to generate undeclared function. This should never happen at this stage.";
|
||||
array_add(*state.ctx.messages, message);
|
||||
}
|
||||
|
||||
for func : find_result.functions {
|
||||
function_variable := from_handle(state.ctx.type_variables, func.type_variable);
|
||||
|
||||
indent(state, indentation);
|
||||
|
||||
if function_variable.return_type_variable {
|
||||
return_variable := from_handle(state.ctx.type_variables, function_variable.return_type_variable);
|
||||
print_to_builder(*state.builder, "% ", hlsl_type_to_string(state.ctx.type_variables, return_variable));
|
||||
} else {
|
||||
append(*state.builder, "void ");
|
||||
}
|
||||
|
||||
print_to_builder(*state.builder, "%", node.name);
|
||||
|
||||
previous_scope := state.current_scope;
|
||||
state.current_scope = function_variable.scope;
|
||||
|
||||
append(*state.builder, "(");
|
||||
|
||||
if node.children.count > 0 && node.children[0].kind == .FieldList {
|
||||
params := node.children[0];
|
||||
|
||||
for child : params.children {
|
||||
emit_node(state, child, 0);
|
||||
|
||||
if it_index != params.children.count - 1 {
|
||||
append(*state.builder, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
append(*state.builder, ")");
|
||||
|
||||
for hint : node.hint_tokens {
|
||||
if hint.ident_value == "position" {
|
||||
// @Incomplete(nb): Should be a lookup table somewhere
|
||||
append(*state.builder, " : SV_POSITION");
|
||||
}
|
||||
|
||||
if starts_with(hint.ident_value, "target") {
|
||||
// @Incomplete(nb): Should be a lookup table somewhere
|
||||
append(*state.builder, " : SV_TARGET");
|
||||
}
|
||||
}
|
||||
|
||||
if emit_body {
|
||||
append(*state.builder, "\n{\n");
|
||||
|
||||
|
||||
if node.children.count > 1 {
|
||||
emit_block(state, node.children[1], indentation + 1);
|
||||
}
|
||||
|
||||
append(*state.builder, "}\n");
|
||||
} else {
|
||||
append(*state.builder, ";");
|
||||
}
|
||||
|
||||
append(*state.builder, "\n");
|
||||
|
||||
|
||||
state.current_scope = previous_scope;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
emit_operator :: (state : *Codegen_State, op_kind : Token_Kind) {
|
||||
if op_kind == {
|
||||
case .TOKEN_PLUS; {
|
||||
append(*state.builder, "+");
|
||||
}
|
||||
case .TOKEN_MINUS; {
|
||||
append(*state.builder, "-");
|
||||
}
|
||||
case .TOKEN_STAR; {
|
||||
append(*state.builder, "*");
|
||||
}
|
||||
case .TOKEN_SLASH; {
|
||||
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; {
|
||||
append(*state.builder, "==");
|
||||
}
|
||||
case .TOKEN_ASSIGN; {
|
||||
append(*state.builder, "=");
|
||||
}
|
||||
case .TOKEN_ISNOTEQUAL; {
|
||||
append(*state.builder, "!=");
|
||||
}
|
||||
case .TOKEN_LOGICALOR; {
|
||||
append(*state.builder, "||");
|
||||
}
|
||||
case .TOKEN_LOGICALAND; {
|
||||
append(*state.builder, "&&");
|
||||
}
|
||||
case .TOKEN_LESS; {
|
||||
append(*state.builder, "<");
|
||||
}
|
||||
case .TOKEN_LESSEQUALS; {
|
||||
append(*state.builder, "<=");
|
||||
}
|
||||
case .TOKEN_GREATER; {
|
||||
append(*state.builder, ">");
|
||||
}
|
||||
case .TOKEN_GREATEREQUALS; {
|
||||
append(*state.builder, ">=");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit_node :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||
if node.kind == {
|
||||
case .Integer; {
|
||||
print_to_builder(*state.builder, "%", node.integer_value);
|
||||
}
|
||||
case .Float; {
|
||||
print_to_builder(*state.builder, "%f", formatFloat(node.float_value, zero_removal=.ONE_ZERO_AFTER_DECIMAL));
|
||||
}
|
||||
case .Field; {
|
||||
emit_field(state, node, indentation);
|
||||
}
|
||||
case .Block; {
|
||||
|
||||
assert(false, "Not implemented yet: block");
|
||||
}
|
||||
case .Variable; {
|
||||
indent(*state.builder, indentation);
|
||||
|
||||
type_var := from_handle(state.ctx.type_variables, node.type_variable);
|
||||
|
||||
print_to_builder(*state.builder, "%", node.name);
|
||||
|
||||
if node.children.count > 0 {
|
||||
append(*state.builder, ".");
|
||||
emit_node(state, node.children[0], 0);
|
||||
}
|
||||
}
|
||||
case .Access; {
|
||||
indent(*state.builder, indentation);
|
||||
|
||||
lhs := node.children[0];
|
||||
rhs := node.children[1];
|
||||
|
||||
emit_node(state, lhs, 0);
|
||||
|
||||
print_to_builder(*state.builder, "%.", node.name);
|
||||
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; {
|
||||
indent(*state.builder, indentation);
|
||||
|
||||
emit_operator(state, node.token.kind);
|
||||
emit_node(state, node.children[0], 0);
|
||||
}
|
||||
case .Expression_Statement; {
|
||||
emit_node(state, node.children[0], indentation);
|
||||
append(*state.builder, ";");
|
||||
}
|
||||
case .Call; {
|
||||
emit_call(state, node, indentation);
|
||||
}
|
||||
case .Return; {
|
||||
indent(*state.builder, indentation);
|
||||
append(*state.builder, "return ");
|
||||
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, "}");
|
||||
}
|
||||
}
|
||||
|
||||
emit_field_list :: (state : *Codegen_State, field_list : *AST_Node, indentation : int) {
|
||||
for child : field_list.children {
|
||||
emit_node(state, child, 1);
|
||||
|
||||
if it_index < field_list.children.count {
|
||||
append(*state.builder, ";\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit_struct :: (state : *Codegen_State, node : *AST_Node, indentation : int, name : string = "") {
|
||||
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;
|
||||
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];
|
||||
|
||||
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_buffer :: (state : *Codegen_State, node : *AST_Node, indentation : int) {
|
||||
variable := from_handle(state.ctx.type_variables, node.type_variable);
|
||||
element := from_handle(state.ctx.type_variables, variable.element_type);
|
||||
|
||||
emit_struct(state, node, indentation, element.typename);
|
||||
|
||||
print_to_builder(*state.builder, "StructuredBuffer<%> % : register(t%);\n\n", element.typename, variable.name, variable.resource_index);
|
||||
}
|
||||
|
||||
emit_declaration :: (state : *Codegen_State, node : *AST_Node) {
|
||||
if node.kind == {
|
||||
case .Function; {
|
||||
emit_function(state, node, 0);
|
||||
}
|
||||
case .CBuffer; {
|
||||
emit_cbuffer(state, node, 0);
|
||||
}
|
||||
case .Buffer; {
|
||||
emit_buffer(state, node, 0);
|
||||
}
|
||||
case .Struct; {
|
||||
emit_struct(state, node, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_struct : bool = false;
|
||||
|
||||
// for variable : state.ctx.type_variables {
|
||||
// if variable.type == .Struct && variable.kind == .Declaration && !variable.builtin {
|
||||
// if variable.source_node.kind == .Properties continue;
|
||||
// if variable.source_node.kind == .Meta continue;
|
||||
// print_to_builder(*state.builder, "struct %;\n", variable.source_node.name);
|
||||
// found_struct = true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if found_struct {
|
||||
// append(*state.builder, "\n");
|
||||
// }
|
||||
|
||||
for variable : state.ctx.type_variables {
|
||||
if variable.type == .Function && !variable.builtin
|
||||
&& !variable.source_node.vertex_entry_point && !variable.source_node.pixel_entry_point {
|
||||
emit_function(state, variable.source_node, 0, false);
|
||||
found_function = true;
|
||||
}
|
||||
}
|
||||
if found_function {
|
||||
append(*state.builder, "\n");
|
||||
}
|
||||
|
||||
for declaration : state.ctx.root.children {
|
||||
if declaration.foreign_declaration {
|
||||
continue;
|
||||
}
|
||||
emit_declaration(state, declaration);
|
||||
}
|
||||
|
||||
state.ctx.codegen_result_text = builder_to_string(*state.builder);
|
||||
}
|
||||
|
||||
#scope_module
|
||||
#import "ncore";
|
||||
153
error.jai
Normal file
153
error.jai
Normal file
@@ -0,0 +1,153 @@
|
||||
Message_Kind :: enum {
|
||||
Log;
|
||||
Warning;
|
||||
Error;
|
||||
Internal_Error;
|
||||
}
|
||||
|
||||
Compiler_Message :: struct {
|
||||
message_kind : Message_Kind;
|
||||
|
||||
message : string;
|
||||
path : string;
|
||||
source_locations : [..]Source_Range;
|
||||
|
||||
report_source_location : bool = true;
|
||||
}
|
||||
|
||||
indent :: (builder : *String_Builder, indentation : int) {
|
||||
for 1..indentation append(builder, " ");
|
||||
}
|
||||
|
||||
newline :: (builder : *String_Builder) {
|
||||
append(builder, "\n");
|
||||
}
|
||||
|
||||
green :: (builder : *String_Builder) {
|
||||
append(builder, green());
|
||||
}
|
||||
|
||||
green :: () -> string {
|
||||
return "\x1b[92m";
|
||||
}
|
||||
|
||||
red :: (builder : *String_Builder) {
|
||||
append(builder, red());
|
||||
}
|
||||
|
||||
red :: () -> string {
|
||||
return "\x1b[91m";
|
||||
}
|
||||
|
||||
yellow :: (builder : *String_Builder) {
|
||||
append(builder, yellow());
|
||||
}
|
||||
|
||||
yellow :: () -> string {
|
||||
return "\x1b[93m";
|
||||
}
|
||||
|
||||
cyan :: (builder : *String_Builder) {
|
||||
append(builder, cyan());
|
||||
}
|
||||
|
||||
cyan :: () -> string {
|
||||
return "\x1b[96m";
|
||||
}
|
||||
|
||||
white :: (builder : *String_Builder) {
|
||||
append(builder, white());
|
||||
}
|
||||
|
||||
white :: () -> string {
|
||||
return "\x1b[97m";
|
||||
}
|
||||
|
||||
reset_color :: () -> string {
|
||||
return "\x1b[0m";
|
||||
}
|
||||
|
||||
reset_color :: (builder : *String_Builder) {
|
||||
append(builder, reset_color());
|
||||
}
|
||||
|
||||
add_message :: (messages : *[..]Compiler_Message, text : string, path : string, kind : Message_Kind) {
|
||||
message : Compiler_Message;
|
||||
message.message = text;
|
||||
message.path = path;
|
||||
message.report_source_location = false;
|
||||
message.message_kind = kind;
|
||||
array_add(messages, message);
|
||||
}
|
||||
|
||||
log_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
|
||||
add_message(messages, text, path, .Log);
|
||||
}
|
||||
|
||||
warning_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
|
||||
add_message(messages, text, path, .Warning);
|
||||
}
|
||||
|
||||
error_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
|
||||
add_message(messages, text, path, .Error);
|
||||
}
|
||||
|
||||
internal_error_message :: (messages : *[..]Compiler_Message, text : string, path : string) {
|
||||
add_message(messages, text, path, .Internal_Error);
|
||||
}
|
||||
|
||||
copy_messages :: (source : []Compiler_Message, dest : *[..]Compiler_Message) {
|
||||
for message : source {
|
||||
array_add(dest, message);
|
||||
}
|
||||
}
|
||||
|
||||
report_messages :: (ctx : *Compiler_Context, messages : []Compiler_Message) -> string {
|
||||
builder : String_Builder;
|
||||
init_string_builder(*builder);
|
||||
for message : messages {
|
||||
report_message(ctx, *builder, message);
|
||||
}
|
||||
return builder_to_string(*builder);
|
||||
}
|
||||
|
||||
report_message :: (ctx : *Compiler_Context, builder : *String_Builder, message : Compiler_Message) {
|
||||
report_message(ctx, builder, message.path, message.message, message.source_locations, message.message_kind, message.report_source_location);
|
||||
}
|
||||
|
||||
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");
|
||||
if path.count > 0 {
|
||||
print_to_builder(builder, "%:", path);
|
||||
} else {
|
||||
append(builder, "internal:");
|
||||
}
|
||||
|
||||
if source_locations.count > 0 {
|
||||
print_to_builder(builder, "%,%: ", source_locations[0].main_token.line, source_locations[0].main_token.column);
|
||||
}
|
||||
|
||||
if kind == .Log {
|
||||
append(builder, "\x1b[31mlog: ");
|
||||
} else if kind == .Error {
|
||||
append(builder, "\x1b[31merror: ");
|
||||
}
|
||||
|
||||
append(builder, "\x1b[37m");
|
||||
print_to_builder(builder, "%\n", message);
|
||||
append(builder, "\x1b[36m");
|
||||
|
||||
if report_source_location {
|
||||
for location : source_locations {
|
||||
append(builder, "\t");
|
||||
print_from_source_location(ctx, builder, location);
|
||||
append(builder, "\n\t");
|
||||
begin := location.begin;
|
||||
|
||||
print_token_pointer(builder, location.main_token);
|
||||
append(builder, "\n");
|
||||
}
|
||||
}
|
||||
append(builder, "\x1b[37m");
|
||||
}
|
||||
|
||||
96
first.jai
Normal file
96
first.jai
Normal file
@@ -0,0 +1,96 @@
|
||||
#import "Basic";
|
||||
#import "File";
|
||||
#import "Compiler";
|
||||
#import "Metaprogram_Plugins";
|
||||
|
||||
plugins: [..] *Metaprogram_Plugin;
|
||||
|
||||
build :: () {
|
||||
w := compiler_create_workspace("Ink Build");
|
||||
if !w {
|
||||
print("Workspace creation failed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
EXECUTABLE_NAME :: "ink";
|
||||
MAIN_FILE :: "ink.jai";
|
||||
|
||||
options := get_build_options(w);
|
||||
|
||||
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;
|
||||
array_add(*new_path, ..options.import_path);
|
||||
array_add(*new_path, "modules");
|
||||
array_add(*new_path, "../.");
|
||||
options.import_path = new_path;
|
||||
options.output_executable_name = EXECUTABLE_NAME;
|
||||
|
||||
wd := get_working_directory();
|
||||
|
||||
set_build_options(options, 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);
|
||||
|
||||
// Call message_loop(), which is a routine of ours below that will receive the messages.
|
||||
message_loop(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");
|
||||
|
||||
set_build_options_dc(.{do_output=false, write_added_strings=false});
|
||||
}
|
||||
|
||||
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();
|
||||
768
ink.jai
Normal file
768
ink.jai
Normal 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);
|
||||
}
|
||||
802
lexing.jai
Normal file
802
lexing.jai
Normal file
@@ -0,0 +1,802 @@
|
||||
Lexer :: struct {
|
||||
input : string;
|
||||
cursor : int;
|
||||
start : int;
|
||||
current_line : int;
|
||||
current_column : int;
|
||||
|
||||
ctx : *Compiler_Context;
|
||||
|
||||
path : string;
|
||||
}
|
||||
|
||||
Token_Kind :: enum {
|
||||
TOKEN_INVALID :: 0;
|
||||
TOKEN_FLOATLITERAL;
|
||||
TOKEN_INTLITERAL;
|
||||
|
||||
TOKEN_LOGICALOR;
|
||||
TOKEN_LOGICALAND;
|
||||
TOKEN_ISEQUAL;
|
||||
TOKEN_ISNOTEQUAL;
|
||||
TOKEN_PLUSEQUALS;
|
||||
TOKEN_MINUSEQUALS;
|
||||
TOKEN_TIMESEQUALS;
|
||||
TOKEN_DIVEQUALS;
|
||||
TOKEN_MODEQUALS;
|
||||
TOKEN_LESSEQUALS;
|
||||
TOKEN_LESS;
|
||||
TOKEN_GREATEREQUALS;
|
||||
TOKEN_GREATER;
|
||||
TOKEN_COLON;
|
||||
TOKEN_DOUBLECOLON;
|
||||
TOKEN_ASSIGN;
|
||||
TOKEN_ARROW;
|
||||
TOKEN_AT;
|
||||
|
||||
TOKEN_PLUS;
|
||||
TOKEN_STAR;
|
||||
TOKEN_SLASH;
|
||||
TOKEN_MOD;
|
||||
TOKEN_MINUS;
|
||||
|
||||
TOKEN_LEFTBRACE;
|
||||
TOKEN_RIGHTBRACE;
|
||||
TOKEN_LEFTBRACKET;
|
||||
TOKEN_RIGHTBRACKET;
|
||||
TOKEN_LEFTPAREN;
|
||||
TOKEN_RIGHTPAREN;
|
||||
TOKEN_SEMICOLON;
|
||||
TOKEN_COMMA;
|
||||
TOKEN_DOT;
|
||||
TOKEN_DOTDOT;
|
||||
|
||||
TOKEN_IDENTIFIER;
|
||||
|
||||
// Keywords
|
||||
TOKEN_BOOL;
|
||||
TOKEN_BUFFER;
|
||||
|
||||
TOKEN_CASE;
|
||||
TOKEN_CBUFFER;
|
||||
TOKEN_COLUMNMAJOR;
|
||||
TOKEN_CONST;
|
||||
TOKEN_CONSTANT_BUFFER;
|
||||
TOKEN_CONTINUE;
|
||||
|
||||
TOKEN_DEFAULT;
|
||||
TOKEN_DIRECTIVE;
|
||||
TOKEN_DISCARD;
|
||||
TOKEN_DO;
|
||||
TOKEN_DOUBLE;
|
||||
|
||||
TOKEN_ELSE;
|
||||
TOKEN_EXPORT;
|
||||
TOKEN_EXTERN;
|
||||
|
||||
TOKEN_FALSE;
|
||||
TOKEN_FOR;
|
||||
|
||||
TOKEN_HALF;
|
||||
TOKEN_HINT;
|
||||
|
||||
TOKEN_IF;
|
||||
TOKEN_IN;
|
||||
TOKEN_INOUT;
|
||||
TOKEN_INSTANCE;
|
||||
|
||||
TOKEN_MATRIX;
|
||||
TOKEN_META;
|
||||
|
||||
TOKEN_OPTIONAL;
|
||||
TOKEN_OUT;
|
||||
|
||||
TOKEN_PIXEL;
|
||||
TOKEN_PLEX;
|
||||
|
||||
TOKEN_RETURN;
|
||||
TOKEN_REGISTER;
|
||||
|
||||
TOKEN_STRING;
|
||||
TOKEN_STRUCT;
|
||||
TOKEN_SWITCH;
|
||||
|
||||
TOKEN_TRUE;
|
||||
|
||||
TOKEN_UNORM;
|
||||
TOKEN_UNSIGNED;
|
||||
TOKEN_UINT;
|
||||
|
||||
TOKEN_VECTOR;
|
||||
TOKEN_VERTEX;
|
||||
TOKEN_VOID;
|
||||
|
||||
TOKEN_WHILE;
|
||||
|
||||
TOKEN_EOF;
|
||||
TOKEN_ERROR;
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind : Token_Kind;
|
||||
union {
|
||||
ident_value : string;
|
||||
integer_value : int;
|
||||
float_value : float;
|
||||
string_value : string;
|
||||
}
|
||||
|
||||
source : *u8;
|
||||
|
||||
// This could all be derived on demand
|
||||
line : int;
|
||||
length : int;
|
||||
column : int;
|
||||
index : int;
|
||||
|
||||
error : string;
|
||||
|
||||
builtin : bool; // @Incomplete: This is kind of a bad idea, but let's just do it for now...
|
||||
}
|
||||
|
||||
Source_Range :: struct {
|
||||
begin : Token;
|
||||
end : Token;
|
||||
main_token : Token;
|
||||
}
|
||||
|
||||
is_at_end :: (using lexer : *Lexer) -> bool {
|
||||
return input.data[cursor] == #char "\0" || cursor == input.count;
|
||||
}
|
||||
|
||||
peek_char :: (using lexer : *Lexer) -> u8 {
|
||||
return input.data[cursor];
|
||||
}
|
||||
|
||||
peek_next_char :: (using lexer : *Lexer) -> u8 {
|
||||
if is_at_end(lexer) return #char "\0";
|
||||
return input.data[cursor + 1];
|
||||
}
|
||||
|
||||
match_character :: (lexer : *Lexer, expected : u8) -> bool {
|
||||
if is_at_end(lexer) return false;
|
||||
if lexer.input.data[lexer.cursor] != expected return false;
|
||||
|
||||
lexer.cursor += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
identifier :: (lexer : *Lexer) -> *Token {
|
||||
while is_alpha(peek_char(lexer)) || is_digit(peek_char(lexer)) || peek_char(lexer) == #char "_" {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
return make_identifier(lexer, identifier_kind(lexer));
|
||||
}
|
||||
|
||||
directive :: (lexer : *Lexer) -> *Token {
|
||||
advance(lexer);
|
||||
while is_alpha(peek_char(lexer)) || is_digit(peek_char(lexer)) || peek_char(lexer) == #char "_" {
|
||||
advance(lexer);
|
||||
}
|
||||
|
||||
return make_directive(lexer);
|
||||
}
|
||||
|
||||
number :: (lexer : *Lexer) -> *Token {
|
||||
while is_digit(peek_char(lexer)) advance(lexer);
|
||||
|
||||
is_float := false;
|
||||
|
||||
if peek_char(lexer) == #char "." && is_digit(peek_next_char(lexer)) {
|
||||
is_float = true;
|
||||
advance(lexer);
|
||||
f_suffix := false;
|
||||
while is_digit(peek_char(lexer)) {
|
||||
advance(lexer);
|
||||
}
|
||||
if peek_char(lexer) == #char "f" {
|
||||
advance(lexer);
|
||||
record_error(lexer, "We don't use 'f' suffixes for floating point values.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if is_float {
|
||||
return make_float(lexer);
|
||||
}
|
||||
return make_int(lexer);
|
||||
}
|
||||
|
||||
identifier_kind :: (using lexer : *Lexer) -> Token_Kind {
|
||||
length := cursor - lexer.start;
|
||||
|
||||
index := start;
|
||||
identifier : string;
|
||||
identifier.data = *input.data[start];
|
||||
identifier.count = length;
|
||||
|
||||
if identifier == "bool" return .TOKEN_BOOL;
|
||||
if identifier == "Buffer" return .TOKEN_BUFFER;
|
||||
if identifier == "case" return .TOKEN_CASE;
|
||||
if identifier == "columnmajor" return .TOKEN_COLUMNMAJOR;
|
||||
if identifier == "const" return .TOKEN_CONST;
|
||||
if identifier == "Constant_Buffer" return .TOKEN_CONSTANT_BUFFER;
|
||||
if identifier == "continue" return .TOKEN_CONTINUE;
|
||||
if identifier == "default" return .TOKEN_DEFAULT;
|
||||
if identifier == "directive" return .TOKEN_DIRECTIVE;
|
||||
if identifier == "discard" return .TOKEN_DIRECTIVE;
|
||||
if identifier == "discard" return .TOKEN_DISCARD;
|
||||
if identifier == "do" return .TOKEN_DO;
|
||||
if identifier == "double" return .TOKEN_DOUBLE;
|
||||
if identifier == "else" return .TOKEN_ELSE;
|
||||
if identifier == "export" return .TOKEN_EXPORT;
|
||||
if identifier == "extern" return .TOKEN_EXTERN;
|
||||
if identifier == "false" return .TOKEN_FALSE;
|
||||
if identifier == "for" return .TOKEN_FOR;
|
||||
if identifier == "half" return .TOKEN_HALF;
|
||||
if identifier == "hint" return .TOKEN_HINT;
|
||||
if identifier == "if" return .TOKEN_IF;
|
||||
if identifier == "in" return .TOKEN_IN;
|
||||
if identifier == "inout" return .TOKEN_INOUT;
|
||||
if identifier == "instance" return .TOKEN_INSTANCE;
|
||||
if identifier == "matrix" return .TOKEN_MATRIX;
|
||||
if identifier == "meta" return .TOKEN_META;
|
||||
if identifier == "optional" return .TOKEN_OPTIONAL;
|
||||
if identifier == "out" return .TOKEN_OUT;
|
||||
if identifier == "pixel" return .TOKEN_PIXEL;
|
||||
if identifier == "return" return .TOKEN_RETURN;
|
||||
if identifier == "register" return .TOKEN_REGISTER;
|
||||
if identifier == "struct" return .TOKEN_STRUCT;
|
||||
if identifier == "plex" return .TOKEN_STRUCT;
|
||||
if identifier == "switch" return .TOKEN_SWITCH;
|
||||
if identifier == "true" return .TOKEN_TRUE;
|
||||
if identifier == "unorm" return .TOKEN_UNORM;
|
||||
if identifier == "unsigned" return .TOKEN_UNSIGNED;
|
||||
if identifier == "uint" return .TOKEN_UINT;
|
||||
if identifier == "vector" return .TOKEN_VECTOR;
|
||||
if identifier == "vertex" return .TOKEN_VERTEX;
|
||||
if identifier == "void" return .TOKEN_VOID;
|
||||
if identifier == "while" return .TOKEN_WHILE;
|
||||
|
||||
return .TOKEN_IDENTIFIER;
|
||||
}
|
||||
|
||||
error_token :: (lexer : *Lexer, message : string) -> *Token {
|
||||
token : *Token = new_token(lexer, .TOKEN_ERROR);
|
||||
|
||||
lexer.ctx.had_error = true;
|
||||
token.error = copy_string(message);
|
||||
|
||||
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) {
|
||||
error : Compiler_Message;
|
||||
error.message_kind = .Error;
|
||||
error.message = message;
|
||||
error.path = lexer.path;
|
||||
|
||||
token := error_token(lexer, message);
|
||||
source_location : Source_Range;
|
||||
source_location.main_token = token;
|
||||
|
||||
token.length += token.column;
|
||||
token.source -= token.column;
|
||||
token.column = 0;
|
||||
|
||||
source_location.begin = token;
|
||||
length := source_location.begin.column;
|
||||
|
||||
source_location.end = token;
|
||||
|
||||
array_add(*error.source_locations, source_location);
|
||||
|
||||
lexer.ctx.had_error = true;
|
||||
array_add(*lexer.ctx.messages, error);
|
||||
}
|
||||
|
||||
make_int :: (lexer : *Lexer) -> *Token {
|
||||
token : *Token = new_token(lexer, .TOKEN_INTLITERAL);
|
||||
|
||||
str : string = .{ count = token.length,
|
||||
data = *lexer.input.data[lexer.start] };
|
||||
value, ok := string_to_int(str);
|
||||
|
||||
if ok {
|
||||
token.integer_value = value;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
make_float :: (lexer : *Lexer) -> *Token {
|
||||
token : *Token = new_token(lexer, .TOKEN_FLOATLITERAL);
|
||||
|
||||
str : string = .{ count = token.length,
|
||||
data = *lexer.input.data[lexer.start] };
|
||||
value, ok := string_to_float(str);
|
||||
if ok {
|
||||
token.float_value = value;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
new_token :: (lexer : *Lexer, kind : Token_Kind) -> *Token {
|
||||
length := lexer.cursor - lexer.start;
|
||||
token : Token;
|
||||
token.kind = kind;
|
||||
token.line = lexer.current_line;
|
||||
token.length = length;
|
||||
token.column = lexer.current_column;
|
||||
token.index = lexer.cursor - token.length;
|
||||
|
||||
if token.length > 0 {
|
||||
token.source = *lexer.input[token.index];
|
||||
} else {
|
||||
token.source = *lexer.input[token.index - 1];
|
||||
}
|
||||
lexer.current_column += length;
|
||||
|
||||
array_add(*lexer.ctx.tokens, token);
|
||||
return *lexer.ctx.tokens[lexer.ctx.tokens.count - 1];
|
||||
}
|
||||
|
||||
make_directive :: (lexer : *Lexer) -> *Token {
|
||||
lexer.start += 1;
|
||||
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 {
|
||||
token : *Token = new_token(lexer, kind);
|
||||
|
||||
name : string = .{ count = token.length,
|
||||
data = *lexer.input.data[lexer.start] };
|
||||
token.ident_value = name;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
make_token :: (lexer : *Lexer, token_kind : Token_Kind) -> *Token {
|
||||
return new_token(lexer, token_kind);
|
||||
}
|
||||
|
||||
skip_whitespace :: (lexer : *Lexer) {
|
||||
while true {
|
||||
if is_at_end(lexer) return;
|
||||
c := peek_char(lexer);
|
||||
|
||||
if c == {
|
||||
case #char " "; {
|
||||
lexer.current_column += 1;
|
||||
advance(lexer);
|
||||
continue;
|
||||
}
|
||||
case #char "\r"; #through;
|
||||
case #char "\t"; {
|
||||
advance(lexer);
|
||||
continue;
|
||||
}
|
||||
case #char "\n"; {
|
||||
advance(lexer);
|
||||
lexer.current_line += 1;
|
||||
lexer.current_column = 0;
|
||||
continue;
|
||||
}
|
||||
case #char "/"; {
|
||||
next := peek_next_char(lexer);
|
||||
if next == #char "/" {
|
||||
while peek_char(lexer) != #char "\n" && !is_at_end(lexer) {
|
||||
advance(lexer);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
advance :: (using lexer : *Lexer) -> u8 {
|
||||
c := input.data[cursor];
|
||||
cursor += 1;
|
||||
return c;
|
||||
}
|
||||
|
||||
scan_next_token :: (lexer : *Lexer) -> *Token {
|
||||
skip_whitespace(lexer);
|
||||
lexer.start = lexer.cursor;
|
||||
|
||||
if is_at_end(lexer) return make_token(lexer, .TOKEN_EOF);
|
||||
|
||||
c := advance(lexer);
|
||||
|
||||
if c == #char "#" return directive(lexer);
|
||||
if is_alpha(c) return identifier(lexer);
|
||||
if is_digit(c) return number(lexer);
|
||||
|
||||
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 "+"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_PLUSEQUALS);
|
||||
return make_token(lexer, .TOKEN_PLUS);
|
||||
}
|
||||
case #char "-"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_MINUSEQUALS);
|
||||
if match_character(lexer, #char ">") return make_token(lexer, .TOKEN_ARROW);
|
||||
return make_token(lexer, .TOKEN_MINUS);
|
||||
}
|
||||
case #char "*"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_TIMESEQUALS);
|
||||
return make_token(lexer, .TOKEN_STAR);
|
||||
}
|
||||
case #char "/"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_DIVEQUALS);
|
||||
return make_token(lexer, .TOKEN_SLASH);
|
||||
}
|
||||
case #char "%"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_MODEQUALS);
|
||||
return make_token(lexer, .TOKEN_MOD);
|
||||
}
|
||||
case #char ":"; {
|
||||
if match_character(lexer, #char ":") return make_token(lexer, .TOKEN_DOUBLECOLON);
|
||||
return make_token(lexer, .TOKEN_COLON);
|
||||
}
|
||||
case #char "@"; {
|
||||
return make_token(lexer, .TOKEN_AT);
|
||||
}
|
||||
case #char "|"; {
|
||||
if match_character(lexer, #char "|") return make_token(lexer, .TOKEN_LOGICALOR);
|
||||
}
|
||||
case #char "&"; {
|
||||
if match_character(lexer, #char "&") return make_token(lexer, .TOKEN_LOGICALAND);
|
||||
}
|
||||
case #char "!"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_ISNOTEQUAL);
|
||||
}
|
||||
case #char "="; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_ISEQUAL);
|
||||
return make_token(lexer, .TOKEN_ASSIGN);
|
||||
}
|
||||
case #char ">"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_GREATEREQUALS);
|
||||
return make_token(lexer, .TOKEN_GREATER);
|
||||
}
|
||||
case #char "<"; {
|
||||
if match_character(lexer, #char "=") return make_token(lexer, .TOKEN_LESSEQUALS);
|
||||
return make_token(lexer, .TOKEN_LESS);
|
||||
}
|
||||
case #char "{"; {
|
||||
return make_token(lexer, .TOKEN_LEFTBRACE);
|
||||
}
|
||||
case #char "}"; {
|
||||
return make_token(lexer, .TOKEN_RIGHTBRACE);
|
||||
}
|
||||
case #char "("; {
|
||||
return make_token(lexer, .TOKEN_LEFTPAREN);
|
||||
}
|
||||
case #char ")"; {
|
||||
return make_token(lexer, .TOKEN_RIGHTPAREN);
|
||||
}
|
||||
case #char "["; {
|
||||
return make_token(lexer, .TOKEN_LEFTBRACKET);
|
||||
}
|
||||
case #char "]"; {
|
||||
return make_token(lexer, .TOKEN_RIGHTBRACKET);
|
||||
}
|
||||
case #char ";"; return make_token(lexer, .TOKEN_SEMICOLON);
|
||||
case #char ","; return make_token(lexer, .TOKEN_COMMA);
|
||||
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 };
|
||||
record_error(lexer, tprint("Invalid token: %", s));
|
||||
return null;
|
||||
// return error_token(lexer, tprint("Invalid token: %", s));
|
||||
}
|
||||
|
||||
lex :: (ctx : *Compiler_Context, allocator := temp) {
|
||||
if ctx.had_error {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
ok := read_input_from_string(lexer, input);
|
||||
if !ok {
|
||||
record_error(lexer, "Unable to initialize from string\n");
|
||||
lexer.ctx.had_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
init_lexer_from_file :: (lexer : *Lexer, file_path : string) {
|
||||
ok := read_input_from_file(lexer, file_path);
|
||||
if !ok {
|
||||
record_error(lexer, tprint("Unable to read file: %\n", file_path));
|
||||
lexer.ctx.had_error = true;
|
||||
}
|
||||
}
|
||||
|
||||
read_input_from_string :: (lexer : *Lexer, input : string) -> bool {
|
||||
lexer.input = input;
|
||||
lexer.cursor = 0;
|
||||
lexer.start = 0;
|
||||
lexer.current_line = 1;
|
||||
lexer.current_column = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
read_input_from_file :: (lexer : *Lexer, file_path : string) -> bool {
|
||||
assert(file_path != "");
|
||||
|
||||
value, success := read_entire_file(file_path, true, true);
|
||||
if !success {
|
||||
free(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
lexer.path = copy_string(file_path);
|
||||
lexer.input = value;
|
||||
lexer.cursor = 0;
|
||||
lexer.start = 0;
|
||||
lexer.current_line = 1;
|
||||
lexer.current_column = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ===========================================================
|
||||
// Pretty printing
|
||||
pretty_print_token :: (token : *Token, builder : *String_Builder) {
|
||||
MAX :: 21;
|
||||
kind_name := enum_names(Token_Kind)[cast(int)token.kind];
|
||||
diff := MAX - kind_name.count;
|
||||
|
||||
print_to_builder(builder, "{kind = %; ", token.kind);
|
||||
for i : 0..diff - 1 {
|
||||
append(builder, " ");
|
||||
}
|
||||
|
||||
append_to_length :: (builder : *String_Builder, number : int) {
|
||||
if number < 10 {
|
||||
append(builder, " ");
|
||||
} else if number < 100 {
|
||||
append(builder, " ");
|
||||
} else if number < 1000 {
|
||||
append(builder, " ");
|
||||
} else if number < 10000 {
|
||||
append(builder, " ");
|
||||
}
|
||||
}
|
||||
|
||||
print_to_builder(builder, "; index = %", token.index);
|
||||
append_to_length(builder, token.index);
|
||||
|
||||
print_to_builder(builder, "; length = %", token.length);
|
||||
append_to_length(builder, token.length);
|
||||
|
||||
print_to_builder(builder, "line = %", token.line);
|
||||
append_to_length(builder, token.line);
|
||||
|
||||
print_to_builder(builder, "; column = %", token.column);
|
||||
append_to_length(builder, token.column);
|
||||
|
||||
append(builder, "; value ='");
|
||||
value_length : int;
|
||||
if token.kind == .TOKEN_IDENTIFIER {
|
||||
print_to_builder(builder, "%", token.ident_value);
|
||||
} else if token.kind == .TOKEN_INTLITERAL {
|
||||
print_to_builder(builder, "%", token.integer_value);
|
||||
} else if token.kind == .TOKEN_FLOATLITERAL {
|
||||
print_to_builder(builder, "%", token.float_value);
|
||||
} else if token.kind == .TOKEN_ERROR {
|
||||
print_to_builder(builder, "%", token.error);
|
||||
} else {
|
||||
source : string = .{ count = token.length,
|
||||
data = token.source };
|
||||
print_to_builder(builder, "%", source);
|
||||
}
|
||||
append(builder, "'; }\n");
|
||||
}
|
||||
|
||||
pretty_print_tokens :: (lexer : *Lexer, allocator : Allocator) -> string {
|
||||
builder : String_Builder;
|
||||
|
||||
init_string_builder(*builder,, allocator);
|
||||
|
||||
token : *Token = scan_next_token(lexer);
|
||||
while token && token.kind != .TOKEN_EOF {
|
||||
pretty_print_token(token, *builder);
|
||||
token = scan_next_token(lexer);
|
||||
}
|
||||
|
||||
return builder_to_string(*builder,, allocator);
|
||||
}
|
||||
|
||||
pretty_print_tokens :: (tokens : []Token, allocator : Allocator) -> string {
|
||||
builder : String_Builder;
|
||||
|
||||
init_string_builder(*builder,, allocator);
|
||||
|
||||
for token : tokens {
|
||||
pretty_print_token(*token, *builder);
|
||||
}
|
||||
return builder_to_string(*builder,, allocator);
|
||||
}
|
||||
|
||||
output_as_code_string :: (lexer : *Lexer, allocator : *Allocator) -> string {
|
||||
builder : String_Builder;
|
||||
|
||||
new_context := context;
|
||||
new_context.allocator = allocator;
|
||||
push_context new_context {
|
||||
init_string_builder(*builder); // @Incomplete: Consider passing builder as argument
|
||||
|
||||
token : *Token = scan_next_token(lexer);
|
||||
while token && token.kind != .TOKEN_EOF {
|
||||
token = scan_next_token(lexer);
|
||||
}
|
||||
|
||||
return builder_to_string(*builder);
|
||||
}
|
||||
}
|
||||
|
||||
print_token_pointer :: (builder : *String_Builder, token : Token) {
|
||||
for i : 0..token.column - 1 {
|
||||
append(builder, " ");
|
||||
}
|
||||
|
||||
for i : 0..token.length - 1 {
|
||||
append(builder, "^");
|
||||
}
|
||||
}
|
||||
|
||||
print_from_source_location :: (ctx : *Compiler_Context, builder : *String_Builder, source_location : Source_Range, indentation : int = 0) {
|
||||
current := source_location.begin;
|
||||
begin := source_location.begin;
|
||||
end := source_location.end;
|
||||
|
||||
if begin.builtin {
|
||||
for i : begin.index..end.index - 1 {
|
||||
tok := ctx.tokens[i];
|
||||
text : string;
|
||||
text.data = tok.source;
|
||||
text.count = tok.length;
|
||||
print_to_builder(builder, "%", text);
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
token_string = .{ count = count, data = begin.source };
|
||||
indent(builder, indentation);
|
||||
print_to_builder(builder, "%", token_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;
|
||||
init_string_builder(*builder,, sc.allocator);
|
||||
print_from_source_location(ctx, *builder, source_location,, sc.allocator);
|
||||
return builder_to_string(*builder,, allocator);
|
||||
}
|
||||
|
||||
|
||||
#import "Basic";
|
||||
#import "File";
|
||||
519
module.jai
Normal file
519
module.jai
Normal file
@@ -0,0 +1,519 @@
|
||||
#load "lexing.jai";
|
||||
#load "error.jai";
|
||||
#load "parsing.jai";
|
||||
#load "check.jai";
|
||||
#load "codegen.jai";
|
||||
|
||||
#import "File_Utilities";
|
||||
|
||||
/* TODO
|
||||
- [x] Remove builtin stringbuilding and replace it with ad-hoc string building when error reporting. In that case we are already building a string anyway, so we can just pass in the string builder
|
||||
- [ ] Support structured buffers (ro, rw, w)
|
||||
- [ ] Support mesh and amplification shaders
|
||||
- [ ] Support compute shaders
|
||||
- [x] Support #if at top level
|
||||
- [x] Support #if at block level
|
||||
- [x] Remove properties block and just use hinted constant buffers instead
|
||||
```
|
||||
props :: constant_buffer @properties {
|
||||
[...]
|
||||
}
|
||||
```
|
||||
- [ ] while loops
|
||||
- [ ] for-each loops
|
||||
- [ ] add parameters to hints (meta properties, resource binding indices if needed)
|
||||
- [ ] consider @entry(stage) syntax instead of the forced keyword
|
||||
- [ ] Add flags to compiler
|
||||
- [ ] Generate output flag(s)
|
||||
- [ ] Possibly final stage flag, so you can just call compile_file and it only does what you need.
|
||||
- Probably this flag is about which stage you need as the _last_ and not which stages to do, as that doesn't make sense.
|
||||
- [ ] Multiple output languages?
|
||||
*/
|
||||
|
||||
add_define :: (env : *Environment, key : string) {
|
||||
for define : env.defines {
|
||||
if define == key {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
array_add(*env.defines, key);
|
||||
}
|
||||
|
||||
remove_define :: (env : *Environment, key : string) {
|
||||
for define : env.defines {
|
||||
if define == key {
|
||||
env.defines[it_index] = env.defines[env.defines.count - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Environment :: struct {
|
||||
defines : [..]string;
|
||||
}
|
||||
|
||||
Field_Kind :: enum {
|
||||
Int :: 0;
|
||||
Half :: 1;
|
||||
Float :: 2;
|
||||
Double :: 3;
|
||||
Texture2D :: 8;
|
||||
Sampler :: 9;
|
||||
|
||||
Function;
|
||||
Struct;
|
||||
Array;
|
||||
}
|
||||
|
||||
Field_Type :: struct {
|
||||
kind : Field_Kind;
|
||||
|
||||
name : string; //@Note(niels): for structs
|
||||
|
||||
children : [..]Field;
|
||||
}
|
||||
|
||||
Hint_Kind :: enum {
|
||||
None;
|
||||
|
||||
Position;
|
||||
UV;
|
||||
Target;
|
||||
Output_Position;
|
||||
|
||||
Custom;
|
||||
}
|
||||
|
||||
Hint_Names :: #run -> [(cast(int)Hint_Kind.Target) + 1]string {
|
||||
names : [(cast(int)Hint_Kind.Target) + 1]string;
|
||||
names[Hint_Kind.Position] = "position";
|
||||
names[Hint_Kind.UV] = "uv";
|
||||
names[Hint_Kind.Target] = "target";
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
lookup_hint :: (name : string) -> Hint_Kind {
|
||||
if name == "position" {
|
||||
return Hint_Kind.Position;
|
||||
} else if name == "uv" {
|
||||
return Hint_Kind.UV;
|
||||
} else if starts_with(name, "target") {
|
||||
return Hint_Kind.Target;
|
||||
} else if name == "outposition" {
|
||||
return Hint_Kind.Output_Position;
|
||||
}
|
||||
return .None;
|
||||
}
|
||||
|
||||
Field_Hint :: struct {
|
||||
kind : Hint_Kind;
|
||||
|
||||
target_index : int;
|
||||
custom_hint_name : string;
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
name : string;
|
||||
|
||||
type : Field_Type;
|
||||
resource_index : u32;
|
||||
hints : [..]Field_Hint;
|
||||
}
|
||||
|
||||
Entry_Point :: struct {
|
||||
name : string;
|
||||
|
||||
function_input : [..]Field;
|
||||
return_value : Field;
|
||||
}
|
||||
|
||||
Buffer_Kind :: enum {
|
||||
Constant;
|
||||
Structured;
|
||||
}
|
||||
|
||||
Buffer :: struct {
|
||||
kind : Buffer_Kind;
|
||||
name : string;
|
||||
|
||||
fields : Static_Array(Field, 16);
|
||||
|
||||
hints : [..]Field_Hint;
|
||||
|
||||
buffer_index : u32;
|
||||
}
|
||||
|
||||
Input_File :: struct {
|
||||
source : string;
|
||||
path : string;
|
||||
}
|
||||
|
||||
Compiler_Context :: struct {
|
||||
file : Input_File;
|
||||
|
||||
environment : Environment;
|
||||
|
||||
tokens : [..]Token;;
|
||||
root : *AST_Node;
|
||||
nodes : [..]AST_Node;
|
||||
|
||||
codegen_result_text : string;
|
||||
|
||||
typed_buffers : Static_Array(Type_Variable_Handle, 32);
|
||||
// structured_buffers : Static_Array(Type_Variable_Handle, 16);
|
||||
|
||||
scope_stack : Scope_Stack;
|
||||
type_variables : [..]Type_Variable;
|
||||
|
||||
vertex_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
input : [..]Field;
|
||||
}
|
||||
|
||||
pixel_entry_point : struct {
|
||||
node : *AST_Node;
|
||||
name : string;
|
||||
return_value : Field;
|
||||
}
|
||||
|
||||
max_buffers :: 32;
|
||||
|
||||
buffers : Static_Array(Buffer, max_buffers);
|
||||
|
||||
had_error : bool;
|
||||
messages : [..]Compiler_Message;
|
||||
}
|
||||
|
||||
#add_context scratch_allocators : [2]Allocator;
|
||||
#add_context scratch_id : int = 0;
|
||||
|
||||
init_context_allocators :: () {
|
||||
if get_arena(context.scratch_allocators[0]) == null {
|
||||
context.scratch_allocators[0] = make_arena(Megabytes(128));
|
||||
context.scratch_allocators[1] = make_arena(Megabytes(128));
|
||||
}
|
||||
}
|
||||
|
||||
clear_context_allocators :: () {
|
||||
if get_arena(context.scratch_allocators[0]) != null {
|
||||
clear(context.scratch_allocators[0]);
|
||||
clear(context.scratch_allocators[1]);
|
||||
}
|
||||
}
|
||||
|
||||
get_scratch :: (conflict : Allocator = .{}) -> Scratch {
|
||||
arena := cast(*Arena)conflict.data;
|
||||
if arena == get_arena(context.scratch_allocators[0]) || context.scratch_id == 0 {
|
||||
context.scratch_id = 1;
|
||||
return scratch_begin(*context.scratch_allocators[1]);
|
||||
}
|
||||
context.scratch_id = 0;
|
||||
return scratch_begin(*context.scratch_allocators[0]);
|
||||
}
|
||||
|
||||
record_error :: (result : *Compiler_Context, format : string, args : .. Any) {
|
||||
error : Compiler_Message;
|
||||
error.message_kind = .Error;
|
||||
error.message = sprint(format, args);
|
||||
|
||||
array_add(*result.messages, error);
|
||||
}
|
||||
|
||||
make_file :: (result : *Compiler_Context, path : string) -> Input_File {
|
||||
if !file_exists(path) {
|
||||
record_error(result, "Unable to load file: %", path);
|
||||
return .{};
|
||||
}
|
||||
file_string, ok := read_entire_file(path);
|
||||
|
||||
if !ok {
|
||||
record_error(result, "Unable to load file: %", path);
|
||||
return .{};
|
||||
}
|
||||
|
||||
return make_file_from_string(file_string, path);
|
||||
}
|
||||
|
||||
make_file_from_string :: (source : string, path : string = "") -> Input_File {
|
||||
input_file : Input_File;
|
||||
|
||||
input_file.source = source;
|
||||
input_file.path = path;
|
||||
|
||||
return input_file;
|
||||
}
|
||||
|
||||
pretty_print_field :: (field : *Field) -> string {
|
||||
builder : String_Builder;
|
||||
init_string_builder(*builder,, temp);
|
||||
|
||||
pretty_print_field(*builder, field);
|
||||
|
||||
return builder_to_string(*builder);
|
||||
}
|
||||
|
||||
Min_Field_Name :: 10;
|
||||
|
||||
pretty_print_field :: (builder : *String_Builder, field : *Field) {
|
||||
if field.name.count > 0 {
|
||||
print_to_builder(builder, "% ", field.name);
|
||||
append(builder, ": ");
|
||||
} else {
|
||||
append(builder, "return - ");
|
||||
}
|
||||
|
||||
type := field.type;
|
||||
|
||||
if type.kind == {
|
||||
case .Int; {
|
||||
append(builder, "int");
|
||||
}
|
||||
case .Half; {
|
||||
append(builder, "half");
|
||||
}
|
||||
case .Float; {
|
||||
append(builder, "float");
|
||||
}
|
||||
case .Double; {
|
||||
append(builder, "double");
|
||||
}
|
||||
case .Texture2D; {
|
||||
append(builder, "texture2D");
|
||||
}
|
||||
case .Sampler; {
|
||||
append(builder, "sampler");
|
||||
}
|
||||
case .Struct; {
|
||||
print_to_builder(builder, "struct : % {", type.name);
|
||||
|
||||
newline_after := type.children.count / 4;
|
||||
|
||||
for *child : type.children {
|
||||
pretty_print_field(builder, child);
|
||||
if it_index < type.children.count - 1 {
|
||||
append(builder, ", ");
|
||||
}
|
||||
|
||||
if it_index % newline_after == 0 {
|
||||
append(builder, "\n");
|
||||
indent(builder, 4);
|
||||
}
|
||||
}
|
||||
|
||||
append(builder, "} ");
|
||||
}
|
||||
case .Array; {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for hint : field.hints {
|
||||
if hint.kind == {
|
||||
case .Position; {
|
||||
append(builder, "(@position)");
|
||||
}
|
||||
case .Target; {
|
||||
print_to_builder(builder, "(@target%)", hint.target_index);
|
||||
}
|
||||
case .Custom; {
|
||||
print_to_builder(builder, "(@%)", hint.custom_hint_name);
|
||||
}
|
||||
}
|
||||
|
||||
if it_index != field.hints.count - 1 {
|
||||
append(builder, ", ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type_variable_to_field :: (ctx : *Compiler_Context, variable : *Type_Variable) -> Field {
|
||||
field : Field;
|
||||
|
||||
field.name = variable.name;
|
||||
|
||||
type : Field_Type;
|
||||
|
||||
if variable.type == {
|
||||
case .Int; {
|
||||
type.kind = Field_Kind.Int;
|
||||
}
|
||||
case .Half; {
|
||||
type.kind = Field_Kind.Half;
|
||||
}
|
||||
case .Float; {
|
||||
type.kind = Field_Kind.Float;
|
||||
}
|
||||
case .Double; {
|
||||
type.kind = Field_Kind.Double;
|
||||
}
|
||||
case .Texture2D; {
|
||||
type.kind = Field_Kind.Texture2D;
|
||||
|
||||
field.resource_index = variable.resource_index;
|
||||
}
|
||||
case .Sampler; {
|
||||
type.kind = Field_Kind.Sampler;
|
||||
|
||||
field.resource_index = variable.resource_index;
|
||||
}
|
||||
case .Struct; {
|
||||
type.kind = Field_Kind.Struct;
|
||||
|
||||
find_result := find_symbol(ctx.scope_stack, variable.typename, xx 1);
|
||||
assert(find_result != null, "Internal compiler error\n");
|
||||
|
||||
type_var := from_handle(ctx.type_variables, find_result.type_variable);
|
||||
|
||||
for i : 0..type_var.children.count - 1 {
|
||||
child := type_var.children[i];
|
||||
child_field := type_variable_to_field(ctx, child);
|
||||
array_add(*type.children, child_field);
|
||||
}
|
||||
|
||||
type.name = variable.typename;
|
||||
}
|
||||
}
|
||||
|
||||
for hint : variable.source_node.hint_tokens {
|
||||
field_hint : Field_Hint;
|
||||
|
||||
if lookup_hint(hint.ident_value) == .Position {
|
||||
field_hint.kind = .Position;
|
||||
} else if lookup_hint(hint.ident_value) == .UV {
|
||||
field_hint.kind = .UV;
|
||||
} else if lookup_hint(hint.ident_value) == .Target {
|
||||
index_str : string;
|
||||
index_str.data = *hint.ident_value.data[7];
|
||||
index_str.count = 1;
|
||||
|
||||
result, ok, remainder := string_to_int(index_str);
|
||||
if ok {
|
||||
field_hint.target_index = result;
|
||||
}
|
||||
field_hint.kind = .Target;
|
||||
} else {
|
||||
field_hint.custom_hint_name = hint.ident_value;
|
||||
field_hint.kind = .Custom;
|
||||
}
|
||||
array_add(*field.hints, field_hint);
|
||||
}
|
||||
|
||||
field.type = type;
|
||||
|
||||
return field;
|
||||
}
|
||||
|
||||
type_variable_to_field :: (ctx : *Compiler_Context, variable : Type_Variable_Handle) -> Field {
|
||||
return type_variable_to_field(ctx, from_handle(ctx.type_variables, variable));
|
||||
}
|
||||
|
||||
generate_buffer :: (ctx : *Compiler_Context, type_handle : Type_Variable_Handle, buffers : *Static_Array) {
|
||||
variable := from_handle(ctx.type_variables, type_handle);
|
||||
|
||||
buffer := array_add(buffers);
|
||||
|
||||
if variable.type == {
|
||||
case .CBuffer; {
|
||||
buffer.kind = .Constant;
|
||||
}
|
||||
case .Buffer; {
|
||||
buffer.kind = .Structured;
|
||||
}
|
||||
}
|
||||
buffer.name = variable.name;
|
||||
|
||||
for i : 0..variable.children.count - 1 {
|
||||
child := variable.children[i];
|
||||
field : Field = type_variable_to_field(ctx, from_handle(ctx.type_variables, child));
|
||||
array_add(*buffer.fields, field);
|
||||
}
|
||||
|
||||
buffer.buffer_index = variable.resource_index;
|
||||
|
||||
for hint : variable.source_node.hint_tokens {
|
||||
field_hint : Field_Hint;
|
||||
field_hint.custom_hint_name = hint.ident_value;
|
||||
field_hint.kind = .Custom;
|
||||
array_add(*buffer.hints, field_hint);
|
||||
}
|
||||
}
|
||||
|
||||
generate_output_data :: (ctx : *Compiler_Context) {
|
||||
if ctx.had_error {
|
||||
return;
|
||||
}
|
||||
|
||||
if ctx.vertex_entry_point.node {
|
||||
ctx.vertex_entry_point.name = ctx.vertex_entry_point.node.name;
|
||||
|
||||
type_variable := from_handle(ctx.type_variables, ctx.vertex_entry_point.node.type_variable);
|
||||
assert(type_variable.type == .Function);
|
||||
|
||||
node := type_variable.source_node;
|
||||
if node.children.count > 0 {
|
||||
if node.children[0].kind == .FieldList {
|
||||
field_list := node.children[0];
|
||||
for child : field_list.children {
|
||||
tv := from_handle(ctx.type_variables, child.type_variable);
|
||||
field := type_variable_to_field(ctx, tv);
|
||||
array_add(*ctx.vertex_entry_point.input, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for buffer_variable : ctx.typed_buffers {
|
||||
generate_buffer(ctx, buffer_variable, *ctx.buffers);
|
||||
}
|
||||
|
||||
if ctx.pixel_entry_point.node {
|
||||
ctx.pixel_entry_point.name = ctx.pixel_entry_point.node.name;
|
||||
|
||||
type_variable := from_handle(ctx.type_variables, ctx.pixel_entry_point.node.type_variable);
|
||||
assert(type_variable.type == .Function);
|
||||
|
||||
if type_variable.return_type_variable > 0 {
|
||||
field := type_variable_to_field(ctx, type_variable.return_type_variable);
|
||||
for hint : type_variable.source_node.hint_tokens {
|
||||
field_hint : Field_Hint;
|
||||
|
||||
if lookup_hint(hint.ident_value) == .Position {
|
||||
field_hint.kind = .Position;
|
||||
} else if lookup_hint(hint.ident_value) == .Target {
|
||||
index_str : string;
|
||||
index_str.data = *hint.ident_value.data[7];
|
||||
index_str.count = 1;
|
||||
|
||||
result, ok, remainder := string_to_int(index_str);
|
||||
if ok {
|
||||
field_hint.target_index = result;
|
||||
}
|
||||
field_hint.kind = .Target;
|
||||
} else {
|
||||
// @Incomplete(nb): custom hints
|
||||
}
|
||||
array_add(*field.hints, field_hint);
|
||||
}
|
||||
ctx.pixel_entry_point.return_value = field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compile_file :: (ctx : *Compiler_Context, path : string, allocator : Allocator = temp) {
|
||||
new_context := context;
|
||||
new_context.allocator = allocator;
|
||||
push_context new_context {
|
||||
init_context_allocators();
|
||||
defer clear_context_allocators();
|
||||
|
||||
ctx.file = make_file(ctx, path);
|
||||
|
||||
lex(ctx, allocator);
|
||||
parse(ctx, allocator);
|
||||
check(ctx, allocator);
|
||||
codegen(ctx, allocator);
|
||||
generate_output_data(ctx);
|
||||
}
|
||||
}
|
||||
1493
parsing.jai
Normal file
1493
parsing.jai
Normal file
File diff suppressed because it is too large
Load Diff
BIN
shader_parsing_session.rdbg
Normal file
BIN
shader_parsing_session.rdbg
Normal file
Binary file not shown.
12
test/all.suite
Normal file
12
test/all.suite
Normal file
@@ -0,0 +1,12 @@
|
||||
test/assign_arithmetic_expression.inx lex parse
|
||||
test/empty_vertex_main.inx lex parse
|
||||
test/empty_vertex_main_with_position_parameter.inx lex parse
|
||||
test/meta_block.inx lex parse
|
||||
test/basic_property_and_return_value.inx lex parse
|
||||
test/function_call_return.inx lex parse
|
||||
test/struct_field_access_test.inx lex parse
|
||||
test/pass_and_access_struct_fields_in_functions.inx lex parse
|
||||
test/field_without_type_specifier.inx lex parse
|
||||
test/functions_with_same_name.inx lex parse
|
||||
test/function_with_int_return.inx lex parse
|
||||
test/type_as_variable_name.inx lex parse
|
||||
4
test/arithmetic_parens.ink
Normal file
4
test/arithmetic_parens.ink
Normal file
@@ -0,0 +1,4 @@
|
||||
vertex main :: () {
|
||||
v : float2;
|
||||
v.x = (2.0 + ((4.0 - 2.0) * 1.5)) * 3.0;
|
||||
}
|
||||
6
test/arrays.ink
Normal file
6
test/arrays.ink
Normal file
@@ -0,0 +1,6 @@
|
||||
vertex main :: () -> float4 @position {
|
||||
arr : [16].float4;
|
||||
arr[0] = float4(1, 1, 1, 1);
|
||||
pos := arr[1];
|
||||
return pos;
|
||||
}
|
||||
3
test/assign_arithmetic_expression.ink
Normal file
3
test/assign_arithmetic_expression.ink
Normal file
@@ -0,0 +1,3 @@
|
||||
vertex main :: () {
|
||||
x : float = 2.0 + 5.0;
|
||||
}
|
||||
5
test/assign_temporary.ink
Normal file
5
test/assign_temporary.ink
Normal file
@@ -0,0 +1,5 @@
|
||||
vertex main :: () {
|
||||
a : float2;
|
||||
b : float2;
|
||||
(a + b).x = 2.0;
|
||||
}
|
||||
10
test/bad_double_access.ink
Normal file
10
test/bad_double_access.ink
Normal 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;
|
||||
}
|
||||
11
test/basic_property_and_return_value.ink
Normal file
11
test/basic_property_and_return_value.ink
Normal file
@@ -0,0 +1,11 @@
|
||||
properties :: Constant_Buffer @properties {
|
||||
color : float4;
|
||||
}
|
||||
|
||||
vertex main :: (pos : float3 @position) -> float3 @position {
|
||||
return pos;
|
||||
}
|
||||
|
||||
pixel main :: () -> float4 @target0 {
|
||||
return properties.color;
|
||||
}
|
||||
8
test/binary_with_access.ink
Normal file
8
test/binary_with_access.ink
Normal file
@@ -0,0 +1,8 @@
|
||||
props :: properties {
|
||||
resolution : float2;
|
||||
}
|
||||
|
||||
vertex main :: (pos : float3 @position) -> float4 @position {
|
||||
p := float2(1.0 - 2.0 * props.resolution.x, 1.0 - 2.0 * props.resolution.y);
|
||||
return float4(p, 1.0, 1.0);
|
||||
}
|
||||
11
test/buffers.ink
Normal file
11
test/buffers.ink
Normal 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
34
test/builtin_types.ink
Normal file
@@ -0,0 +1,34 @@
|
||||
vertex main :: () {
|
||||
v2 : float2 = float2(2.0, 2.0);
|
||||
v2 = float2(2.0);
|
||||
v2 = float2(v2);
|
||||
|
||||
v3 : float3 = float3(2.0, 2.0, 2.0);
|
||||
v3 = float3(v2, 1.0);
|
||||
v3 = float3(1.0, v2);
|
||||
v3 = float3(1.0);
|
||||
v3 = float3(v3);
|
||||
|
||||
v4 : float4 = float4(2.0, 2.0, 2.0, 2.0);
|
||||
v4 = float4(v4);
|
||||
v4 = float4(v2, v2);
|
||||
v4 = float4(v2, 1.0, 1.0);
|
||||
v4 = float4(1.0, v2, 1.0);
|
||||
v4 = float4(1.0, 1.0, v2);
|
||||
v4 = float4(v3, 2.0);
|
||||
v4 = float4(2.0, v3);
|
||||
v4 = float4(2.0);
|
||||
|
||||
|
||||
v4 = float4(1.0, 1.0, v2);
|
||||
v4 = float4(2.0);
|
||||
|
||||
v2.x = 2.0;
|
||||
v2.y = 2.0;
|
||||
|
||||
p := v2.x + v3.z;
|
||||
q := v4.w + v2.x;
|
||||
|
||||
m : float4x4;
|
||||
|
||||
}
|
||||
6
test/check/arithmetic_parens.golden
Normal file
6
test/check/arithmetic_parens.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[v] : float2
|
||||
]
|
||||
]
|
||||
7
test/check/arrays.golden
Normal file
7
test/check/arrays.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : () -> float4
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float4
|
||||
[arr] : [16].float4
|
||||
]
|
||||
]
|
||||
6
test/check/assign_arithmetic_expression.golden
Normal file
6
test/check/assign_arithmetic_expression.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float
|
||||
]
|
||||
]
|
||||
6
test/check/bad_double_access.golden
Normal file
6
test/check/bad_double_access.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/bad_double_access.ink:7,4: [31merror: [37mAttempting to access a field on a primitive type 'float'.
|
||||
[96mp.v.x.
|
||||
^
|
||||
declaration:
|
||||
x: float
|
||||
[36m[37m
|
||||
12
test/check/basic_property_and_return_value.golden
Normal file
12
test/check/basic_property_and_return_value.golden
Normal file
@@ -0,0 +1,12 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : (pos : float3) -> float3
|
||||
[properties] : {color : float4}
|
||||
scope (properties) [
|
||||
[color] : float4
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
11
test/check/builtin_types.golden
Normal file
11
test/check/builtin_types.golden
Normal file
@@ -0,0 +1,11 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[v2] : float2
|
||||
[v4] : float4
|
||||
[v3] : float3
|
||||
[p] : float
|
||||
[m] : float4x4
|
||||
[q] : float
|
||||
]
|
||||
]
|
||||
8
test/check/complicated_computation.golden
Normal file
8
test/check/complicated_computation.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float
|
||||
[z] : float
|
||||
[y] : float
|
||||
]
|
||||
]
|
||||
15
test/check/constant_buffer.golden
Normal file
15
test/check/constant_buffer.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : (pos : float4) -> float4
|
||||
[camera] : {projection : float4x4, view : float4x4}
|
||||
scope (camera) [
|
||||
[projection] : float4x4
|
||||
[view] : float4x4
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float4
|
||||
[mv] : float4
|
||||
[mvp] : float4
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
15
test/check/custom_hint.golden
Normal file
15
test/check/custom_hint.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : (pos : float4) -> float4
|
||||
[vertex__vs_main] : (pos : float3) -> float4
|
||||
[p] : {time : float}
|
||||
scope (p) [
|
||||
[time] : float
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
scope (pixel__ps_main) [
|
||||
[t] : float
|
||||
[pos] : float4
|
||||
]
|
||||
]
|
||||
10
test/check/double_access.golden
Normal file
10
test/check/double_access.golden
Normal file
@@ -0,0 +1,10 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
[p] : {v : float2}
|
||||
scope (p) [
|
||||
[v] : float2
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float
|
||||
]
|
||||
]
|
||||
4
test/check/empty_struct.golden
Normal file
4
test/check/empty_struct.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
scope (global) [
|
||||
[Foo] : {}
|
||||
scope (Foo) []
|
||||
]
|
||||
4
test/check/empty_vertex_main.golden
Normal file
4
test/check/empty_vertex_main.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) []
|
||||
]
|
||||
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (pos : float3) -> float3
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
]
|
||||
7
test/check/field_assignment.golden
Normal file
7
test/check/field_assignment.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (pos : float4) -> float4
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float
|
||||
[pos] : float4
|
||||
]
|
||||
]
|
||||
4
test/check/field_without_type_specifier.golden
Normal file
4
test/check/field_without_type_specifier.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/field_without_type_specifier.shd:2,0: [31merror: [37mExpected type specifier after field name.
|
||||
[96mx := 5.0;
|
||||
^
|
||||
[36m[37m
|
||||
6
test/check/float_if_cond.golden
Normal file
6
test/check/float_if_cond.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/float_if_cond.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||
[96mif 1.0
|
||||
^^^
|
||||
1.0 has type float
|
||||
|
||||
[36m[37m
|
||||
4
test/check/float_suffix.golden
Normal file
4
test/check/float_suffix.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/float_suffix.shd:2,12: [31merror: [37mWe don't use 'f' suffixes for floating point values.
|
||||
[36m x : float = 2.0f
|
||||
^^^^
|
||||
[37m
|
||||
10
test/check/for_i_loop.golden
Normal file
10
test/check/for_i_loop.golden
Normal file
@@ -0,0 +1,10 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[x] : int
|
||||
scope (block) [
|
||||
[i] : int
|
||||
scope (block) []
|
||||
]
|
||||
]
|
||||
]
|
||||
4
test/check/for_index_outside.golden
Normal file
4
test/check/for_index_outside.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/for_index_outside.ink:6,0: [31merror: [37mUse of undeclared symbol 'i'
|
||||
[96m i += 1;
|
||||
^
|
||||
[36m[37m
|
||||
6
test/check/function_call.golden
Normal file
6
test/check/function_call.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[foo] : () -> int
|
||||
[vertex__vs_main] : ()
|
||||
scope (foo) []
|
||||
scope (vertex__vs_main) []
|
||||
]
|
||||
6
test/check/function_call_out_of_order_declaration.golden
Normal file
6
test/check/function_call_out_of_order_declaration.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[foo] : ()
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) []
|
||||
scope (foo) []
|
||||
]
|
||||
6
test/check/function_call_return.golden
Normal file
6
test/check/function_call_return.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) []
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
6
test/check/function_with_int_return.golden
Normal file
6
test/check/function_with_int_return.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (pos : float3) -> int
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
]
|
||||
8
test/check/functions_with_same_name.golden
Normal file
8
test/check/functions_with_same_name.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
[1;37mtest/functions_with_same_name.ink:2,0: [31merror: [37mRedeclaration of 'foo'
|
||||
[96m foo :: () {
|
||||
^^^
|
||||
|
||||
[97mtest/functions_with_same_name.ink:1,0: info: Here is the first declaration of 'foo'
|
||||
[96m foo :: () {
|
||||
^^^
|
||||
[36m[37m
|
||||
13
test/check/hinted_cbuffer.golden
Normal file
13
test/check/hinted_cbuffer.golden
Normal 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
|
||||
]
|
||||
]
|
||||
6
test/check/if_cond_assign.golden
Normal file
6
test/check/if_cond_assign.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/if_cond_assign.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||
[96mif 0 = 100
|
||||
^^^^^^
|
||||
if 0 = 100 { has type int
|
||||
|
||||
[36m[37m
|
||||
8
test/check/if_def_block.golden
Normal file
8
test/check/if_def_block.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : ()
|
||||
scope (pixel__ps_main) [ scope (block) [
|
||||
[alpha_color] : float4
|
||||
[f] : float
|
||||
]
|
||||
]
|
||||
]
|
||||
4
test/check/if_def_expression.golden
Normal file
4
test/check/if_def_expression.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
scope (global) [
|
||||
[vertex__vs_console_main] : ()
|
||||
scope (vertex__vs_console_main) []
|
||||
]
|
||||
8
test/check/ifdefs.golden
Normal file
8
test/check/ifdefs.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[vertex__vs_skinning_main] : ()
|
||||
[pixel__ps_main] : ()
|
||||
scope (vertex__vs_skinning_main) [
|
||||
[x] : float
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
15
test/check/inferred_types.golden
Normal file
15
test/check/inferred_types.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
scope (global) [
|
||||
[foo] : () -> float
|
||||
[vertex__vs_main] : (pos : float3) -> float4
|
||||
[bar] : () -> float
|
||||
scope (bar) []
|
||||
scope (foo) []
|
||||
scope (vertex__vs_main) [
|
||||
[v2] : float2
|
||||
[i] : int
|
||||
[v4] : float4
|
||||
[pos] : float3
|
||||
[v3] : float3
|
||||
[f] : float
|
||||
]
|
||||
]
|
||||
13
test/check/meta_block.golden
Normal file
13
test/check/meta_block.golden
Normal file
@@ -0,0 +1,13 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : (pos : float3, uv : float2) -> float3
|
||||
[properties] : properties
|
||||
scope (properties) [
|
||||
[color] : float4
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
[uv] : float2
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
11
test/check/multiple_functions.golden
Normal file
11
test/check/multiple_functions.golden
Normal file
@@ -0,0 +1,11 @@
|
||||
scope (global) [
|
||||
[foo] : () -> int
|
||||
[vertex__vs_main] : ()
|
||||
[bar] : () -> float
|
||||
scope (foo) []
|
||||
scope (bar) []
|
||||
scope (vertex__vs_main) [
|
||||
[x] : int
|
||||
[y] : float
|
||||
]
|
||||
]
|
||||
13
test/check/multiple_semicolons_everywhere.golden
Normal file
13
test/check/multiple_semicolons_everywhere.golden
Normal file
@@ -0,0 +1,13 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[foo] : () -> float4
|
||||
[vertex__vs_main] : (pos : float3) -> float3
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
scope (foo) []
|
||||
scope (pixel__ps_main) [
|
||||
[y] : float4
|
||||
[color] : float4
|
||||
]
|
||||
]
|
||||
9
test/check/nested_if.golden
Normal file
9
test/check/nested_if.golden
Normal 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) []
|
||||
]
|
||||
]
|
||||
]
|
||||
6
test/check/non_bool_cond.golden
Normal file
6
test/check/non_bool_cond.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/non_bool_cond.ink:0,0: [31merror: [37mType of expression in if condition has to be bool.
|
||||
[96mif 1.0
|
||||
^^^
|
||||
1.0 has type float
|
||||
|
||||
[36m[37m
|
||||
15
test/check/pass_and_access_struct_fields_in_functions.golden
Normal file
15
test/check/pass_and_access_struct_fields_in_functions.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
scope (global) [
|
||||
[foo] : (f : Foo) -> float
|
||||
[vertex__vs_main] : ()
|
||||
[Foo] : {some_data : float}
|
||||
scope (Foo) [
|
||||
[some_data] : float
|
||||
]
|
||||
scope (foo) [
|
||||
[f] : Foo
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[d] : float
|
||||
[f] : Foo
|
||||
]
|
||||
]
|
||||
8
test/check/passthrough.golden
Normal file
8
test/check/passthrough.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : (pos : float3) -> float3
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
9
test/check/precedence_test.golden
Normal file
9
test/check/precedence_test.golden
Normal file
@@ -0,0 +1,9 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (x : float, y : float, z : float, w : float)
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float
|
||||
[z] : float
|
||||
[y] : float
|
||||
[w] : float
|
||||
]
|
||||
]
|
||||
12
test/check/property_rename.golden
Normal file
12
test/check/property_rename.golden
Normal file
@@ -0,0 +1,12 @@
|
||||
scope (global) [
|
||||
[pixel__ps_main] : () -> float4
|
||||
[vertex__vs_main] : (pos : float4) -> float4
|
||||
[props] : {color : float4}
|
||||
scope (props) [
|
||||
[color] : float4
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float4
|
||||
]
|
||||
scope (pixel__ps_main) []
|
||||
]
|
||||
8
test/check/redeclared_variable.golden
Normal file
8
test/check/redeclared_variable.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
[1;37mtest/redeclared_variable.ink:3,0: [31merror: [37mRedeclaration of 'x'
|
||||
[96m x : float = 5.0
|
||||
^
|
||||
|
||||
[97mtest/redeclared_variable.ink:2,0: info: Here is the first declaration of 'x'
|
||||
[96m x : float = 1.0
|
||||
^
|
||||
[36m[37m
|
||||
8
test/check/rvalue_binary.golden
Normal file
8
test/check/rvalue_binary.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[b] : float2
|
||||
[x] : float
|
||||
[a] : float2
|
||||
]
|
||||
]
|
||||
9
test/check/simple_else_if.golden
Normal file
9
test/check/simple_else_if.golden
Normal 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) []
|
||||
]
|
||||
]
|
||||
7
test/check/simple_if.golden
Normal file
7
test/check/simple_if.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (pos : float3) -> float4
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
scope (block) []
|
||||
]
|
||||
]
|
||||
8
test/check/simple_if_else.golden
Normal file
8
test/check/simple_if_else.golden
Normal file
@@ -0,0 +1,8 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : (pos : float3) -> float4
|
||||
scope (vertex__vs_main) [
|
||||
[pos] : float3
|
||||
scope (block) []
|
||||
scope (block) []
|
||||
]
|
||||
]
|
||||
11
test/check/simple_struct_access.golden
Normal file
11
test/check/simple_struct_access.golden
Normal file
@@ -0,0 +1,11 @@
|
||||
scope (global) [
|
||||
[Data] : {color : float4}
|
||||
[vertex__vs_main] : ()
|
||||
scope (Data) [
|
||||
[color] : float4
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[x] : float4
|
||||
[d] : Data
|
||||
]
|
||||
]
|
||||
6
test/check/struct_access_primitive_type.golden
Normal file
6
test/check/struct_access_primitive_type.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/struct_access_primitive_type.ink:3,0: [31merror: [37mAttempting to access a field on a primitive type 'int'.
|
||||
[96mx.d = 4;
|
||||
^
|
||||
declaration:
|
||||
x : int = 5
|
||||
[36m[37m
|
||||
15
test/check/struct_within_struct.golden
Normal file
15
test/check/struct_within_struct.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
scope (global) [
|
||||
[Bar] : {t : Foo}
|
||||
[vertex__vs_main] : ()
|
||||
[Foo] : {color : float4}
|
||||
scope (Foo) [
|
||||
[color] : float4
|
||||
]
|
||||
scope (Bar) [
|
||||
[t] : Foo
|
||||
]
|
||||
scope (vertex__vs_main) [
|
||||
[b] : Bar
|
||||
[f] : Foo
|
||||
]
|
||||
]
|
||||
6
test/check/temp_access.golden
Normal file
6
test/check/temp_access.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
[1;37mtest/temp_access.ink:5,10: [31merror: [37mCannot assign to an lvalue.
|
||||
[96m (a + b).x = 2.0;
|
||||
^^^^^^^^^^^
|
||||
|
||||
|
||||
[36m[37m
|
||||
4
test/check/type_as_function_name.golden
Normal file
4
test/check/type_as_function_name.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/type_as_function_name.shd:1,0: [31merror: [37mInvalid function name 'int'
|
||||
[36m int :: () {
|
||||
^^^
|
||||
[37m
|
||||
4
test/check/type_as_variable_name.golden
Normal file
4
test/check/type_as_variable_name.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/type_as_variable_name.ink:2,0: [31merror: [37mInvalid variable name 'int'
|
||||
[36m int : float = 4.0
|
||||
^^^
|
||||
[37m
|
||||
10
test/check/unary.golden
Normal file
10
test/check/unary.golden
Normal file
@@ -0,0 +1,10 @@
|
||||
scope (global) [
|
||||
[pixel__ps_ps_main] : (position : float4) -> float4
|
||||
[vertex__vs_vs_main] : (position : float3) -> float4
|
||||
scope (vertex__vs_vs_main) [
|
||||
[position] : float3
|
||||
]
|
||||
scope (pixel__ps_ps_main) [
|
||||
[position] : float4
|
||||
]
|
||||
]
|
||||
7
test/check/undeclared_function.golden
Normal file
7
test/check/undeclared_function.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
[1;37mtest/undeclared_function.ink:2,0: [31merror: [37mAttempt to call undeclared function 'foo'.
|
||||
|
||||
[96m foo();
|
||||
^^^
|
||||
|
||||
|
||||
[36m[37m
|
||||
4
test/check/undeclared_symbol.golden
Normal file
4
test/check/undeclared_symbol.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
[1;37mtest/undeclared_symbol.ink:2,10: [31merror: [37mUse of undeclared symbol 'f'
|
||||
[96m b : int = f;
|
||||
^
|
||||
[36m[37m
|
||||
33
test/check/unknown_overload.golden
Normal file
33
test/check/unknown_overload.golden
Normal file
@@ -0,0 +1,33 @@
|
||||
[1;37mtest/unknown_overload.ink:6,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
||||
[96m found:
|
||||
foo(v, v);
|
||||
^^^
|
||||
|
||||
[97m While matching argument 1 in function call.
|
||||
[96m foo(v, v);
|
||||
^
|
||||
[97m Possible overloads:
|
||||
[96m foo :: (v1 : float3, v2 : float3) { (test/unknown_overload.ink:1)
|
||||
[96m foo :: (v1 : float2, v2 : float2, v3 : float2) { (test/unknown_overload.ink:2)
|
||||
|
||||
[36m[37m[1;37mtest/unknown_overload.ink:6,4: [31merror: [37mType mismatch. Expected float3 got float
|
||||
[96m found:
|
||||
foo(v, v);
|
||||
^
|
||||
expected:
|
||||
float3
|
||||
|
||||
got:
|
||||
v : float = 2.0
|
||||
|
||||
[36m[37m[1;37mtest/unknown_overload.ink:6,7: [31merror: [37mType mismatch. Expected float3 got float
|
||||
[96m found:
|
||||
foo(v, v);
|
||||
^
|
||||
expected:
|
||||
float3
|
||||
|
||||
got:
|
||||
v : float = 2.0
|
||||
|
||||
[36m[37m
|
||||
6
test/check/use_builtin_functions.golden
Normal file
6
test/check/use_builtin_functions.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
scope (global) [
|
||||
[vertex__vs_main] : ()
|
||||
scope (vertex__vs_main) [
|
||||
[f] : float4
|
||||
]
|
||||
]
|
||||
16
test/check/wrong_argument_count.golden
Normal file
16
test/check/wrong_argument_count.golden
Normal file
@@ -0,0 +1,16 @@
|
||||
[1;37mtest/wrong_argument_count.ink:5,19: [31merror: [37mUse of undeclared symbol 'w'
|
||||
[96m return x * y * z * w;
|
||||
^
|
||||
[36m[37m[1;37mtest/wrong_argument_count.ink:9,0: [31merror: [37mProcedure call did not match any of the possible overloads for 'foo'
|
||||
[96m found:
|
||||
foo(2.0, 3.0);
|
||||
^^^
|
||||
[97m Possible overloads:
|
||||
[96m foo :: (x : float, y : float, z : float) -> float { (test/wrong_argument_count.ink:1)
|
||||
[97m Not enough arguments: Wanted 3, got 2.
|
||||
|
||||
[96m foo :: (x : float, y : float, z : float, w : float) -> float { (test/wrong_argument_count.ink:4)
|
||||
[97m Not enough arguments: Wanted 4, got 2.
|
||||
|
||||
|
||||
[36m[37m
|
||||
30
test/check/wrong_multiply.golden
Normal file
30
test/check/wrong_multiply.golden
Normal file
@@ -0,0 +1,30 @@
|
||||
[1;37mtest/wrong_multiply.ink:4,18: [31merror: [37mProcedure call did not match any of the possible overloads for 'float4'
|
||||
[96m found:
|
||||
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||
^^^^^^
|
||||
|
||||
[97m While matching argument 2 in function call.
|
||||
[96m result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||
^
|
||||
[97m Possible overloads:
|
||||
[96m float4 :: (float, float, float, float)
|
||||
[96m float4 :: (float2, float2)
|
||||
[96m float4 :: (float2, float, float)
|
||||
[96m float4 :: (float, float2, float)
|
||||
[96m float4 :: (float, float, float2)
|
||||
[96m float4 :: (float, float3)
|
||||
[96m float4 :: (float3, float)
|
||||
[96m float4 :: (float4)
|
||||
[96m float4 :: (float)
|
||||
|
||||
[36m[37m[1;37mtest/wrong_multiply.ink:4,34: [31merror: [37mType mismatch. Expected float got float2
|
||||
[96m found:
|
||||
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||
^
|
||||
expected:
|
||||
float
|
||||
|
||||
got:
|
||||
result : float4 = float4(1.0, foo * res, 0.0, 1.0);
|
||||
|
||||
[36m[37m
|
||||
30
test/check/wrong_type_for_function.golden
Normal file
30
test/check/wrong_type_for_function.golden
Normal file
@@ -0,0 +1,30 @@
|
||||
[1;37mtest/wrong_type_for_function.ink:11,17: [31merror: [37mProcedure call did not match any of the possible overloads for 'float4'
|
||||
[96m found:
|
||||
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||
^^^^^^
|
||||
|
||||
[97m While matching argument 1 in function call.
|
||||
[96m color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||
^
|
||||
[97m Possible overloads:
|
||||
[96m float4 :: (float, float, float, float)
|
||||
[96m float4 :: (float2, float2)
|
||||
[96m float4 :: (float2, float, float)
|
||||
[96m float4 :: (float, float2, float)
|
||||
[96m float4 :: (float, float, float2)
|
||||
[96m float4 :: (float, float3)
|
||||
[96m float4 :: (float3, float)
|
||||
[96m float4 :: (float4)
|
||||
[96m float4 :: (float)
|
||||
|
||||
[36m[37m[1;37mtest/wrong_type_for_function.ink:11,24: [31merror: [37mType mismatch. Expected float got float2
|
||||
[96m found:
|
||||
color : float4 = float4(y, 1.0, 1.0, 1.0);
|
||||
^
|
||||
expected:
|
||||
float
|
||||
|
||||
got:
|
||||
y : float2 = foo()
|
||||
|
||||
[36m[37m
|
||||
47
test/check_all.suite
Normal file
47
test/check_all.suite
Normal 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
|
||||
6
test/codegen/arithmetic_parens.golden
Normal file
6
test/codegen/arithmetic_parens.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
void vs_main()
|
||||
{
|
||||
float2 v;
|
||||
v.x = (2.0f + ((4.0f - 2.0f) * 1.5f)) * 3.0f;
|
||||
}
|
||||
|
||||
6
test/codegen/arrays.golden
Normal file
6
test/codegen/arrays.golden
Normal file
@@ -0,0 +1,6 @@
|
||||
float4 vs_main() : SV_POSITION
|
||||
{
|
||||
float4 arr[16];
|
||||
return arr[0];
|
||||
}
|
||||
|
||||
5
test/codegen/assign_arithmetic_expression.golden
Normal file
5
test/codegen/assign_arithmetic_expression.golden
Normal file
@@ -0,0 +1,5 @@
|
||||
void vs_main()
|
||||
{
|
||||
float x = 2.0f + 5.0f;
|
||||
}
|
||||
|
||||
15
test/codegen/basic_property_and_return_value.golden
Normal file
15
test/codegen/basic_property_and_return_value.golden
Normal file
@@ -0,0 +1,15 @@
|
||||
cbuffer properties : register(b0)
|
||||
{
|
||||
float4 color;
|
||||
}
|
||||
|
||||
float3 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
float4 ps_main() : SV_TARGET
|
||||
{
|
||||
return properties.color;
|
||||
}
|
||||
|
||||
28
test/codegen/builtin_types.golden
Normal file
28
test/codegen/builtin_types.golden
Normal file
@@ -0,0 +1,28 @@
|
||||
void vs_main()
|
||||
{
|
||||
float2 v2 = float2(2.0f, 2.0f);
|
||||
v2 = float2(2.0f, 2.0f);
|
||||
v2 = float2(v2, v2);
|
||||
float3 v3 = float3(2.0f, 2.0f, 2.0f);
|
||||
v3 = float3(v2, 1.0f);
|
||||
v3 = float3(1.0f, v2);
|
||||
v3 = float3(1.0f, 1.0f, 1.0f);
|
||||
v3 = float3(v3, v3, v3);
|
||||
float4 v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||
v4 = float4(v4, v4, v4, v4);
|
||||
v4 = float4(v2, v2);
|
||||
v4 = float4(v2, 1.0f, 1.0f);
|
||||
v4 = float4(1.0f, v2, 1.0f);
|
||||
v4 = float4(1.0f, 1.0f, v2);
|
||||
v4 = float4(v3, 2.0f);
|
||||
v4 = float4(2.0f, v3);
|
||||
v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||
v4 = float4(1.0f, 1.0f, v2);
|
||||
v4 = float4(2.0f, 2.0f, 2.0f, 2.0f);
|
||||
v2.x = 2.0f;
|
||||
v2.y = 2.0f;
|
||||
float p = v2.x + v3.z;
|
||||
float q = v4.w + v2.x;
|
||||
float4x4 m;
|
||||
}
|
||||
|
||||
7
test/codegen/complicated_computation.golden
Normal file
7
test/codegen/complicated_computation.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
void vs_main()
|
||||
{
|
||||
float x = 5.0f;
|
||||
float y = 3000.0f;
|
||||
float z = (y * y) + x;
|
||||
}
|
||||
|
||||
18
test/codegen/constant_buffer.golden
Normal file
18
test/codegen/constant_buffer.golden
Normal file
@@ -0,0 +1,18 @@
|
||||
cbuffer camera : register(b0)
|
||||
{
|
||||
float4x4 projection;
|
||||
float4x4 view;
|
||||
}
|
||||
|
||||
float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
||||
{
|
||||
float4 mv = mul(camera.view, pos);
|
||||
float4 mvp = mul(camera.projection, mv);
|
||||
return mvp;
|
||||
}
|
||||
|
||||
float4 ps_main() : SV_TARGET
|
||||
{
|
||||
return float4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
}
|
||||
|
||||
17
test/codegen/custom_hint.golden
Normal file
17
test/codegen/custom_hint.golden
Normal file
@@ -0,0 +1,17 @@
|
||||
cbuffer __PROPERTIES : register(b0)
|
||||
{
|
||||
float __PROPERTIES__time;
|
||||
}
|
||||
|
||||
|
||||
float4 vs_main(float3 pos : POSITION) : SV_POSITION
|
||||
{
|
||||
return float4(pos.x, pos.y, pos.z, 1.0f);
|
||||
}
|
||||
|
||||
float4 ps_main(float4 pos : SV_POSITION) : SV_TARGET
|
||||
{
|
||||
float t = __PROPERTIES__time;
|
||||
return float4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
2
test/codegen/empty_struct.golden
Normal file
2
test/codegen/empty_struct.golden
Normal file
@@ -0,0 +1,2 @@
|
||||
struct Foo {};
|
||||
|
||||
4
test/codegen/empty_vertex_main.golden
Normal file
4
test/codegen/empty_vertex_main.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
void vs_main()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
float3 vs_main(float3 pos : POSITION)
|
||||
{
|
||||
return pos;
|
||||
}
|
||||
|
||||
7
test/codegen/field_assignment.golden
Normal file
7
test/codegen/field_assignment.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
float4 vs_main(float4 pos : POSITION) : SV_POSITION
|
||||
{
|
||||
float x = 5.0f;
|
||||
x = 7.0f;
|
||||
return pos;
|
||||
}
|
||||
|
||||
12
test/codegen/function_call.golden
Normal file
12
test/codegen/function_call.golden
Normal file
@@ -0,0 +1,12 @@
|
||||
int foo();
|
||||
|
||||
int foo()
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
|
||||
11
test/codegen/function_call_out_of_order_declaration.golden
Normal file
11
test/codegen/function_call_out_of_order_declaration.golden
Normal file
@@ -0,0 +1,11 @@
|
||||
void foo();
|
||||
|
||||
void vs_main()
|
||||
{
|
||||
foo();
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
}
|
||||
|
||||
9
test/codegen/function_call_return.golden
Normal file
9
test/codegen/function_call_return.golden
Normal file
@@ -0,0 +1,9 @@
|
||||
void vs_main()
|
||||
{
|
||||
}
|
||||
|
||||
float4 ps_main() : SV_TARGET
|
||||
{
|
||||
return float4(1, 1, 1, 1);
|
||||
}
|
||||
|
||||
13
test/codegen/hinted_cbuffer.golden
Normal file
13
test/codegen/hinted_cbuffer.golden
Normal 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;
|
||||
}
|
||||
|
||||
7
test/codegen/if_def_block.golden
Normal file
7
test/codegen/if_def_block.golden
Normal file
@@ -0,0 +1,7 @@
|
||||
void ps_main()
|
||||
{
|
||||
|
||||
float4 alpha_color = float4(1, 0, 0, 1);
|
||||
float f = 2.0f;
|
||||
}
|
||||
|
||||
4
test/codegen/if_def_expression.golden
Normal file
4
test/codegen/if_def_expression.golden
Normal file
@@ -0,0 +1,4 @@
|
||||
void vs_console_main()
|
||||
{
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user