dusk: create IR instead simple analysing
This commit is contained in:
parent
0a790a38f3
commit
51b0e8695f
8 changed files with 785 additions and 284 deletions
|
|
@ -1,182 +0,0 @@
|
|||
const std = @import("std");
|
||||
const Ast = @import("Ast.zig");
|
||||
const Token = @import("Token.zig");
|
||||
const ErrorMsg = @import("main.zig").ErrorMsg;
|
||||
const Analyse = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
tree: *const Ast,
|
||||
errors: std.ArrayListUnmanaged(ErrorMsg),
|
||||
|
||||
pub fn deinit(self: *Analyse) void {
|
||||
for (self.errors.items) |*err_msg| err_msg.deinit(self.allocator);
|
||||
self.errors.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn analyseRoot(self: *Analyse) !void {
|
||||
const global_items = self.tree.spanToList(0);
|
||||
|
||||
for (global_items, 0..) |node_i, i| {
|
||||
try self.checkRedeclaration(global_items[i + 1 ..], node_i);
|
||||
try self.globalDecl(global_items, node_i);
|
||||
}
|
||||
|
||||
if (self.errors.items.len > 0) {
|
||||
return error.Analysing;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn globalDecl(self: *Analyse, parent_scope: []const Ast.Index, node_i: Ast.Index) !void {
|
||||
switch (self.tree.nodeTag(node_i)) {
|
||||
.global_variable => {}, // TODO
|
||||
.struct_decl => try self.structDecl(parent_scope, node_i),
|
||||
else => std.debug.print("Global Decl TODO: {}\n", .{self.tree.nodeTag(node_i)}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn structDecl(self: *Analyse, parent_scope: []const Ast.Index, node: Ast.Index) !void {
|
||||
const member_list = self.tree.spanToList(self.tree.nodeLHS(node));
|
||||
for (member_list, 0..) |member_node, i| {
|
||||
try self.checkRedeclaration(member_list[i + 1 ..], member_node);
|
||||
const member_loc = self.tree.tokenLoc(self.tree.nodeToken(member_node));
|
||||
const member_type_node = self.tree.nodeRHS(member_node);
|
||||
const member_type_loc = self.tree.tokenLoc(self.tree.nodeToken(member_type_node));
|
||||
const member_type_name = member_type_loc.slice(self.tree.source);
|
||||
|
||||
var inner_type = member_type_node;
|
||||
while (true) {
|
||||
switch (self.tree.nodeTag(inner_type)) {
|
||||
.scalar_type,
|
||||
.vector_type,
|
||||
.matrix_type,
|
||||
.atomic_type,
|
||||
=> {},
|
||||
.array_type => {
|
||||
if (self.tree.nodeRHS(member_type_node) == Ast.null_index and
|
||||
i != member_list.len - 1)
|
||||
{
|
||||
try self.addError(
|
||||
member_loc,
|
||||
"struct member with runtime-sized array type, must be the last member of the structure",
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
}
|
||||
},
|
||||
.user_type => {
|
||||
const decl_node = try self.expectFindTypeAliasOrStructDeclNode(member_type_loc, parent_scope, member_type_name) orelse break;
|
||||
if (self.tree.nodeTag(decl_node) == .type_alias) {
|
||||
inner_type = self.tree.nodeLHS(decl_node);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
try self.addError(
|
||||
member_loc,
|
||||
"invalid struct member type '{s}'",
|
||||
.{member_type_name},
|
||||
null,
|
||||
);
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// UNUSED
|
||||
/// returns the actual type of a type alias
|
||||
pub fn fetchTypeAliasType(self: *Analyse, node: Ast.Index) void {
|
||||
std.debug.assert(self.tree.nodeTag(node) == .type_alias);
|
||||
if (self.tree.nodeTag(node) == .type_alias) {
|
||||
return self.fetchTypeAliasType(self.tree.nodeLHS(node));
|
||||
}
|
||||
return self.tree.nodeLHS(node);
|
||||
}
|
||||
|
||||
pub fn expectFindTypeAliasOrStructDeclNode(self: *Analyse, ref_loc: Token.Loc, scope_items: []const Ast.Index, name: []const u8) !?Ast.Index {
|
||||
if (try self.expectFindDeclNode(ref_loc, scope_items, name)) |decl_node| {
|
||||
switch (self.tree.nodeTag(decl_node)) {
|
||||
.struct_decl, .type_alias => return decl_node,
|
||||
else => {
|
||||
try self.addError(
|
||||
ref_loc,
|
||||
"'{s}' is neither an struct or type alias",
|
||||
.{name},
|
||||
null,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn expectFindDeclNode(self: *Analyse, ref_loc: Token.Loc, scope_items: []const Ast.Index, name: []const u8) !?Ast.Index {
|
||||
return self.findDeclNode(scope_items, name) orelse {
|
||||
try self.addError(
|
||||
ref_loc,
|
||||
"use of undeclared identifier '{s}'",
|
||||
.{name},
|
||||
null,
|
||||
);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn findDeclNode(self: *Analyse, scope_items: []const Ast.Index, name: []const u8) ?Ast.Index {
|
||||
for (scope_items) |node| {
|
||||
const node_token = self.declNameToken(node) orelse continue;
|
||||
if (std.mem.eql(u8, name, self.tree.tokenLoc(node_token).slice(self.tree.source))) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn checkRedeclaration(self: *Analyse, scope_items: []const Ast.Index, decl_node: Ast.Index) !void {
|
||||
const decl_token_loc = self.tree.tokenLoc(self.declNameToken(decl_node).?);
|
||||
const decl_name = decl_token_loc.slice(self.tree.source);
|
||||
for (scope_items) |redecl_node| {
|
||||
std.debug.assert(decl_node != redecl_node);
|
||||
const redecl_token_loc = self.tree.tokenLoc(self.declNameToken(redecl_node).?);
|
||||
const redecl_name = redecl_token_loc.slice(self.tree.source);
|
||||
if (std.mem.eql(u8, decl_name, redecl_name)) {
|
||||
try self.addError(
|
||||
redecl_token_loc,
|
||||
"redeclaration of '{s}'",
|
||||
.{decl_name},
|
||||
try ErrorMsg.Note.create(
|
||||
self.allocator,
|
||||
decl_token_loc,
|
||||
"other declaration here",
|
||||
.{},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declNameToken(self: *Analyse, node: Ast.Index) ?Ast.Index {
|
||||
return switch (self.tree.nodeTag(node)) {
|
||||
.global_variable => self.tree.extraData(Ast.Node.GlobalVarDecl, self.tree.nodeLHS(node)).name,
|
||||
.struct_decl,
|
||||
.fn_decl,
|
||||
.global_constant,
|
||||
.override,
|
||||
.type_alias,
|
||||
=> self.tree.nodeToken(node) + 1,
|
||||
.struct_member => self.tree.nodeToken(node),
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addError(
|
||||
self: *Analyse,
|
||||
loc: Token.Loc,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
note: ?ErrorMsg.Note,
|
||||
) !void {
|
||||
const err_msg = try ErrorMsg.create(self.allocator, loc, format, args, note);
|
||||
try self.errors.append(self.allocator, err_msg);
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
const std = @import("std");
|
||||
const Analyse = @import("Analyse.zig");
|
||||
const Parser = @import("Parser.zig");
|
||||
const Token = @import("Token.zig");
|
||||
const Tokenizer = @import("Tokenizer.zig");
|
||||
|
|
@ -30,22 +29,27 @@ pub const ParseResult = union(enum) {
|
|||
|
||||
/// parses a TranslationUnit (WGSL Program)
|
||||
pub fn parse(allocator: std.mem.Allocator, source: [:0]const u8) !ParseResult {
|
||||
const estimated_tokens = source.len / 8;
|
||||
var tokens = std.MultiArrayList(Token){};
|
||||
try tokens.ensureTotalCapacity(allocator, estimated_tokens);
|
||||
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
try tokens.append(allocator, tok);
|
||||
if (tok.tag == .eof) break;
|
||||
}
|
||||
|
||||
var p = Parser{
|
||||
.allocator = allocator,
|
||||
.source = source,
|
||||
.tok_i = 0,
|
||||
.tokens = tokens.toOwnedSlice(),
|
||||
.tokens = blk: {
|
||||
const estimated_tokens = source.len / 8;
|
||||
|
||||
var tokens = std.MultiArrayList(Token){};
|
||||
errdefer tokens.deinit(allocator);
|
||||
|
||||
try tokens.ensureTotalCapacity(allocator, estimated_tokens);
|
||||
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
try tokens.append(allocator, tok);
|
||||
if (tok.tag == .eof) break;
|
||||
}
|
||||
|
||||
break :blk tokens;
|
||||
},
|
||||
.nodes = .{},
|
||||
.extra = .{},
|
||||
.scratch = .{},
|
||||
|
|
@ -53,48 +57,25 @@ pub fn parse(allocator: std.mem.Allocator, source: [:0]const u8) !ParseResult {
|
|||
.extensions = Extension.Array.initFill(false),
|
||||
};
|
||||
defer p.deinit();
|
||||
errdefer p.tokens.deinit(allocator);
|
||||
|
||||
// TODO: make sure tokens:nodes ratio is right
|
||||
const estimated_node_count = (tokens.len + 2) / 2;
|
||||
const estimated_node_count = (p.tokens.len + 2) / 2;
|
||||
try p.nodes.ensureTotalCapacity(allocator, estimated_node_count);
|
||||
|
||||
p.parseRoot() catch |err| {
|
||||
if (err == error.Parsing) {
|
||||
p.tokens.deinit(allocator);
|
||||
return .{ .errors = try p.errors.toOwnedSlice(allocator) };
|
||||
}
|
||||
return err;
|
||||
_ = try p.translationUnit() orelse {
|
||||
return .{ .errors = try p.errors.toOwnedSlice(allocator) };
|
||||
};
|
||||
|
||||
return .{
|
||||
.tree = .{
|
||||
.source = source,
|
||||
.tokens = p.tokens,
|
||||
.tokens = p.tokens.toOwnedSlice(),
|
||||
.nodes = p.nodes.toOwnedSlice(),
|
||||
.extra = try p.extra.toOwnedSlice(allocator),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn analyse(tree: Ast, allocator: std.mem.Allocator) !?[]ErrorMsg {
|
||||
var analyser = Analyse{
|
||||
.allocator = allocator,
|
||||
.tree = &tree,
|
||||
.errors = .{},
|
||||
};
|
||||
defer analyser.deinit();
|
||||
|
||||
analyser.analyseRoot() catch |err| {
|
||||
if (err == error.Analysing) {
|
||||
return try analyser.errors.toOwnedSlice(allocator);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn spanToList(tree: Ast, span: Ast.Index) []const Ast.Index {
|
||||
std.debug.assert(tree.nodeTag(span) == .span);
|
||||
return tree.extra[tree.nodeLHS(span)..tree.nodeRHS(span)];
|
||||
|
|
@ -313,7 +294,12 @@ pub const Node = struct {
|
|||
/// TOK : k_i32, k_u32, k_f32, k_f16, k_bool
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
scalar_type,
|
||||
number_type,
|
||||
|
||||
/// TOK : k_bool
|
||||
/// LHS : --
|
||||
/// RHS : --
|
||||
bool_type,
|
||||
|
||||
/// TOK : k_sampler, k_comparison_sampler
|
||||
/// LHS : --
|
||||
|
|
@ -523,8 +509,8 @@ pub const Node = struct {
|
|||
/// vector prefix (e.g. vec2) and matrix prefix (e.g. mat2x2) LHS is null
|
||||
/// see callExpr in Parser.zig if you don't understand this
|
||||
///
|
||||
/// TOK : ident, k_array, 'scalar keywords', 'vector keywords', 'matrix keywords'
|
||||
/// LHS : (scalar_type, vector_type, matrix_type, array_type)?
|
||||
/// TOK : ident, k_array, k_bool, 'number type keywords', 'vector keywords', 'matrix keywords'
|
||||
/// LHS : (number_type, bool_type, vector_type, matrix_type, array_type)?
|
||||
/// RHS : arguments (Expr span)
|
||||
call,
|
||||
|
||||
|
|
|
|||
429
libs/dusk/src/AstGen.zig
Normal file
429
libs/dusk/src/AstGen.zig
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
const std = @import("std");
|
||||
const Ast = @import("Ast.zig");
|
||||
const Token = @import("Token.zig");
|
||||
const IR = @import("IR.zig");
|
||||
const ErrorMsg = @import("main.zig").ErrorMsg;
|
||||
const AstGen = @This();
|
||||
|
||||
arena: std.mem.Allocator,
|
||||
allocator: std.mem.Allocator,
|
||||
tree: *const Ast,
|
||||
errors: std.ArrayListUnmanaged(ErrorMsg),
|
||||
scope: Scope = .top,
|
||||
|
||||
pub const Scope = union(enum) {
|
||||
top,
|
||||
range: []const Ast.Index,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *AstGen) void {
|
||||
for (self.errors.items) |*err_msg| err_msg.deinit(self.allocator);
|
||||
self.errors.deinit(self.allocator);
|
||||
}
|
||||
|
||||
pub fn translationUnit(self: *AstGen) !?IR.TranslationUnit {
|
||||
const global_decls = self.tree.spanToList(0);
|
||||
|
||||
var list = std.ArrayList(IR.GlobalDecl).init(self.arena);
|
||||
defer list.deinit();
|
||||
|
||||
for (global_decls, 0..) |node, i| {
|
||||
self.scope = .top;
|
||||
try self.checkRedeclaration(global_decls[i + 1 ..], node);
|
||||
const global = try self.globalDecl(node) orelse continue;
|
||||
try list.append(global);
|
||||
}
|
||||
|
||||
if (self.errors.items.len > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return try list.toOwnedSlice();
|
||||
}
|
||||
|
||||
pub fn globalDecl(self: *AstGen, node: Ast.Index) !?IR.GlobalDecl {
|
||||
switch (self.tree.nodeTag(node)) {
|
||||
.global_variable => {}, // TODO
|
||||
.struct_decl => return .{ .@"struct" = try self.structDecl(node) orelse return null },
|
||||
else => std.debug.print("Global Decl TODO: {}\n", .{self.tree.nodeTag(node)}),
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn structDecl(self: *AstGen, node: Ast.Index) !?IR.StructDecl {
|
||||
var members_arr = std.ArrayList(IR.StructMember).init(self.arena);
|
||||
defer members_arr.deinit();
|
||||
|
||||
const member_list = self.tree.spanToList(self.tree.nodeLHS(node));
|
||||
for (member_list, 0..) |member_node, i| {
|
||||
try self.checkRedeclaration(member_list[i + 1 ..], member_node);
|
||||
const member_loc = self.tree.tokenLoc(self.tree.nodeToken(member_node));
|
||||
const member_type_node = self.tree.nodeRHS(member_node);
|
||||
const member_type_token = self.tree.nodeToken(member_type_node);
|
||||
const member_type_loc = self.tree.tokenLoc(member_type_token);
|
||||
const member_type_name = member_type_loc.slice(self.tree.source);
|
||||
|
||||
var inner_type = member_type_node;
|
||||
while (true) {
|
||||
switch (self.tree.nodeTag(inner_type)) {
|
||||
.bool_type => {
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .bool,
|
||||
});
|
||||
},
|
||||
.number_type => {
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .number = self.numberType(inner_type) },
|
||||
});
|
||||
},
|
||||
.vector_type => {
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .vector = try self.vectorType(inner_type) orelse break },
|
||||
});
|
||||
},
|
||||
.matrix_type => {
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .matrix = try self.matrixType(inner_type) orelse break },
|
||||
});
|
||||
},
|
||||
.atomic_type => {
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .atomic = try self.atomicType(inner_type) orelse break },
|
||||
});
|
||||
},
|
||||
.array_type => {
|
||||
const array_type = try self.arrayType(inner_type) orelse return null;
|
||||
|
||||
try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .array = array_type },
|
||||
});
|
||||
|
||||
if (array_type.size == null and i + 1 != member_list.len) {
|
||||
try self.addError(
|
||||
member_loc,
|
||||
"struct member with runtime-sized array type, must be the last member of the structure",
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.user_type => {
|
||||
const decl_node = try self.expectFindDeclNode(member_type_loc, member_type_name) orelse break;
|
||||
switch (self.tree.nodeTag(decl_node)) {
|
||||
.type_alias => {
|
||||
inner_type = self.tree.nodeLHS(decl_node);
|
||||
continue;
|
||||
},
|
||||
.struct_decl => try members_arr.append(.{
|
||||
.name = member_loc.slice(self.tree.source),
|
||||
.type = .{ .@"struct" = try self.structType(member_type_node) orelse return null },
|
||||
}),
|
||||
else => {
|
||||
try self.addError(
|
||||
member_type_loc,
|
||||
"'{s}' is neither an struct or type alias",
|
||||
.{member_type_name},
|
||||
null,
|
||||
);
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
try self.addError(
|
||||
member_loc,
|
||||
"invalid struct member type '{s}'",
|
||||
.{member_type_name},
|
||||
null,
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const struct_name = self.declNameToken(node) orelse unreachable;
|
||||
return .{
|
||||
.name = self.tree.tokenLoc(struct_name).slice(self.tree.source),
|
||||
.members = try members_arr.toOwnedSlice(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn numberType(self: *AstGen, node: Ast.Index) IR.NumberType {
|
||||
const token = self.tree.nodeToken(node);
|
||||
const token_tag = self.tree.tokenTag(token);
|
||||
return switch (token_tag) {
|
||||
.k_i32 => .i32,
|
||||
.k_u32 => .u32,
|
||||
.k_f32 => .f32,
|
||||
.k_f16 => .f16,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn vectorType(self: *AstGen, node: Ast.Index) !?IR.VectorType {
|
||||
const token = self.tree.nodeToken(node);
|
||||
const token_tag = self.tree.tokenTag(token);
|
||||
const size: IR.VectorType.Size = switch (token_tag) {
|
||||
.k_vec2 => .vec2,
|
||||
.k_vec3 => .vec3,
|
||||
.k_vec4 => .vec4,
|
||||
else => unreachable,
|
||||
};
|
||||
const type_node = self.tree.nodeLHS(node);
|
||||
const component_type: IR.VectorType.Type = switch (self.tree.nodeTag(type_node)) {
|
||||
.bool_type => .bool,
|
||||
.number_type => .{ .number = self.numberType(type_node) },
|
||||
else => {
|
||||
try self.addError(
|
||||
self.tree.tokenLoc(self.tree.nodeToken(type_node)),
|
||||
"invalid vector component type",
|
||||
.{},
|
||||
try ErrorMsg.Note.create(
|
||||
self.allocator,
|
||||
null,
|
||||
"must be 'i32', 'u32', 'f32', 'f16' or 'bool'",
|
||||
.{},
|
||||
),
|
||||
);
|
||||
return null;
|
||||
},
|
||||
};
|
||||
return .{ .size = size, .component_type = component_type };
|
||||
}
|
||||
|
||||
pub fn matrixType(self: *AstGen, node: Ast.Index) !?IR.MatrixType {
|
||||
const token = self.tree.nodeToken(node);
|
||||
const token_tag = self.tree.tokenTag(token);
|
||||
const size: IR.MatrixType.Size = switch (token_tag) {
|
||||
.k_mat2x2 => .mat2x2,
|
||||
.k_mat2x3 => .mat2x3,
|
||||
.k_mat2x4 => .mat2x4,
|
||||
.k_mat3x2 => .mat3x2,
|
||||
.k_mat3x3 => .mat3x3,
|
||||
.k_mat3x4 => .mat3x4,
|
||||
.k_mat4x2 => .mat4x2,
|
||||
.k_mat4x3 => .mat4x3,
|
||||
.k_mat4x4 => .mat4x4,
|
||||
else => unreachable,
|
||||
};
|
||||
const type_node = self.tree.nodeLHS(node);
|
||||
const type_token = self.tree.nodeToken(type_node);
|
||||
const type_token_tag = self.tree.tokenTag(type_token);
|
||||
const component_type: IR.MatrixType.Type = switch (type_token_tag) {
|
||||
.k_f32 => .f32,
|
||||
.k_f16 => .f16,
|
||||
else => {
|
||||
try self.addError(
|
||||
self.tree.tokenLoc(self.tree.nodeToken(type_node)),
|
||||
"invalid matrix element type",
|
||||
.{},
|
||||
try ErrorMsg.Note.create(
|
||||
self.allocator,
|
||||
null,
|
||||
"must be 'f32' or 'f16'",
|
||||
.{},
|
||||
),
|
||||
);
|
||||
return null;
|
||||
},
|
||||
};
|
||||
return .{ .size = size, .component_type = component_type };
|
||||
}
|
||||
|
||||
pub fn atomicType(self: *AstGen, node: Ast.Index) !?IR.AtomicType {
|
||||
const type_node = self.tree.nodeLHS(node);
|
||||
const type_token = self.tree.nodeToken(type_node);
|
||||
const type_token_tag = self.tree.tokenTag(type_token);
|
||||
const component_type: IR.AtomicType.Type = switch (type_token_tag) {
|
||||
.k_i32 => .i32,
|
||||
.k_u32 => .u32,
|
||||
else => {
|
||||
try self.addError(
|
||||
self.tree.tokenLoc(self.tree.nodeToken(type_node)),
|
||||
"invalid atomic component type",
|
||||
.{},
|
||||
try ErrorMsg.Note.create(
|
||||
self.allocator,
|
||||
null,
|
||||
"must be 'i32' or 'u32'",
|
||||
.{},
|
||||
),
|
||||
);
|
||||
return null;
|
||||
},
|
||||
};
|
||||
return .{ .component_type = component_type };
|
||||
}
|
||||
|
||||
pub fn arrayType(self: *AstGen, node: Ast.Index) !?IR.ArrayType {
|
||||
var component_type_node = self.tree.nodeLHS(node);
|
||||
var component_type: IR.ArrayType.Type = undefined;
|
||||
while (true) {
|
||||
switch (self.tree.nodeTag(component_type_node)) {
|
||||
.bool_type => component_type = .bool,
|
||||
.number_type => component_type = .{ .number = self.numberType(component_type_node) },
|
||||
.vector_type => component_type = .{ .vector = try self.vectorType(component_type_node) orelse return null },
|
||||
.matrix_type => component_type = .{ .matrix = try self.matrixType(component_type_node) orelse return null },
|
||||
.atomic_type => component_type = .{ .atomic = try self.atomicType(component_type_node) orelse return null },
|
||||
.array_type => {
|
||||
var array_type = try self.arena.create(IR.ArrayType.Type);
|
||||
|
||||
if (try self.arrayType(component_type_node)) |ty| {
|
||||
if (ty.size != null) {
|
||||
array_type.* = ty.component_type;
|
||||
component_type = .{ .array = array_type };
|
||||
break;
|
||||
} else {
|
||||
try self.addError(
|
||||
self.tree.tokenLoc(self.tree.nodeToken(component_type_node)),
|
||||
"array componet type can not be a runtime-sized array",
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
.user_type => {
|
||||
const component_type_loc = self.tree.tokenLoc(self.tree.nodeToken(component_type_node));
|
||||
const component_type_name = component_type_loc.slice(self.tree.source);
|
||||
const decl_node = try self.expectFindDeclNode(component_type_loc, component_type_name) orelse break;
|
||||
switch (self.tree.nodeTag(decl_node)) {
|
||||
.type_alias => {
|
||||
component_type_node = self.tree.nodeLHS(decl_node);
|
||||
continue;
|
||||
},
|
||||
.struct_decl => component_type = .{ .@"struct" = try self.structType(component_type_node) orelse return null },
|
||||
else => {
|
||||
try self.addError(
|
||||
component_type_loc,
|
||||
"'{s}' is neither an struct or type alias",
|
||||
.{component_type_name},
|
||||
null,
|
||||
);
|
||||
return null;
|
||||
},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
try self.addError(
|
||||
self.tree.tokenLoc(self.tree.nodeToken(component_type_node)),
|
||||
"invalid array component type",
|
||||
.{},
|
||||
null,
|
||||
);
|
||||
return null;
|
||||
},
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const size_node = self.tree.nodeRHS(node);
|
||||
if (size_node == Ast.null_index) {
|
||||
return .{ .component_type = component_type };
|
||||
}
|
||||
|
||||
return .{ .component_type = component_type, .size = undefined }; // TODO
|
||||
}
|
||||
|
||||
pub fn structType(self: *AstGen, node: Ast.Index) !?[]const u8 {
|
||||
const ref_loc = self.tree.tokenLoc(self.tree.nodeToken(node));
|
||||
return ref_loc.slice(self.tree.source);
|
||||
}
|
||||
|
||||
/// UNUSED
|
||||
/// returns the actual type of a type alias
|
||||
pub fn fetchTypeAliasType(self: *AstGen, node: Ast.Index) void {
|
||||
std.debug.assert(self.tree.nodeTag(node) == .type_alias);
|
||||
if (self.tree.nodeTag(node) == .type_alias) {
|
||||
return self.fetchTypeAliasType(self.tree.nodeLHS(node));
|
||||
}
|
||||
return self.tree.nodeLHS(node);
|
||||
}
|
||||
|
||||
pub fn expectFindDeclNode(self: *AstGen, ref_loc: Token.Loc, name: []const u8) !?Ast.Index {
|
||||
return self.findDeclNode(name) orelse {
|
||||
try self.addError(
|
||||
ref_loc,
|
||||
"use of undeclared identifier '{s}'",
|
||||
.{name},
|
||||
null,
|
||||
);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn findDeclNode(self: *AstGen, name: []const u8) ?Ast.Index {
|
||||
for (self.scopeToRange()) |node| {
|
||||
const node_token = self.declNameToken(node) orelse continue;
|
||||
if (std.mem.eql(u8, name, self.tree.tokenLoc(node_token).slice(self.tree.source))) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn checkRedeclaration(self: *AstGen, range: []const Ast.Index, decl_node: Ast.Index) !void {
|
||||
const decl_token_loc = self.tree.tokenLoc(self.declNameToken(decl_node).?);
|
||||
const decl_name = decl_token_loc.slice(self.tree.source);
|
||||
for (range) |redecl_node| {
|
||||
std.debug.assert(decl_node != redecl_node);
|
||||
const redecl_token_loc = self.tree.tokenLoc(self.declNameToken(redecl_node).?);
|
||||
const redecl_name = redecl_token_loc.slice(self.tree.source);
|
||||
if (std.mem.eql(u8, decl_name, redecl_name)) {
|
||||
try self.addError(
|
||||
redecl_token_loc,
|
||||
"redeclaration of '{s}'",
|
||||
.{decl_name},
|
||||
try ErrorMsg.Note.create(
|
||||
self.allocator,
|
||||
decl_token_loc,
|
||||
"other declaration here",
|
||||
.{},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn declNameToken(self: *AstGen, node: Ast.Index) ?Ast.Index {
|
||||
return switch (self.tree.nodeTag(node)) {
|
||||
.global_variable => self.tree.extraData(Ast.Node.GlobalVarDecl, self.tree.nodeLHS(node)).name,
|
||||
.struct_decl,
|
||||
.fn_decl,
|
||||
.global_constant,
|
||||
.override,
|
||||
.type_alias,
|
||||
=> self.tree.nodeToken(node) + 1,
|
||||
.struct_member => self.tree.nodeToken(node),
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn scopeToRange(self: AstGen) []const Ast.Index {
|
||||
return switch (self.scope) {
|
||||
.top => self.tree.spanToList(0),
|
||||
.range => |r| r,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn addError(
|
||||
self: *AstGen,
|
||||
loc: Token.Loc,
|
||||
comptime format: []const u8,
|
||||
args: anytype,
|
||||
note: ?ErrorMsg.Note,
|
||||
) !void {
|
||||
const err_msg = try ErrorMsg.create(self.allocator, loc, format, args, note);
|
||||
try self.errors.append(self.allocator, err_msg);
|
||||
}
|
||||
209
libs/dusk/src/IR.zig
Normal file
209
libs/dusk/src/IR.zig
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
const std = @import("std");
|
||||
const AstGen = @import("AstGen.zig");
|
||||
const Ast = @import("Ast.zig");
|
||||
const ErrorMsg = @import("main.zig").ErrorMsg;
|
||||
const IR = @This();
|
||||
|
||||
arena: std.heap.ArenaAllocator,
|
||||
root: TranslationUnit,
|
||||
|
||||
pub fn deinit(self: IR) void {
|
||||
self.arena.deinit();
|
||||
}
|
||||
|
||||
pub const AstGenResult = union(enum) {
|
||||
ir: IR,
|
||||
errors: []ErrorMsg,
|
||||
};
|
||||
|
||||
pub fn generate(allocator: std.mem.Allocator, tree: *const Ast) !AstGenResult {
|
||||
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||
errdefer arena.deinit();
|
||||
|
||||
var astgen = AstGen{
|
||||
.arena = arena.allocator(),
|
||||
.allocator = allocator,
|
||||
.tree = tree,
|
||||
.errors = .{},
|
||||
};
|
||||
defer astgen.deinit();
|
||||
|
||||
const root = try astgen.translationUnit() orelse {
|
||||
arena.deinit();
|
||||
return .{
|
||||
.errors = try astgen.errors.toOwnedSlice(allocator),
|
||||
};
|
||||
};
|
||||
|
||||
return .{ .ir = .{ .arena = arena, .root = root } };
|
||||
}
|
||||
|
||||
pub const TranslationUnit = []const GlobalDecl;
|
||||
|
||||
pub const GlobalDecl = union(enum) {
|
||||
variable: GlobalVariable,
|
||||
@"struct": StructDecl,
|
||||
};
|
||||
|
||||
pub const GlobalVariable = struct {
|
||||
name: []const u8,
|
||||
type: union(enum) {},
|
||||
expr: Expression,
|
||||
};
|
||||
|
||||
pub const StructDecl = struct {
|
||||
name: []const u8,
|
||||
members: []const StructMember,
|
||||
};
|
||||
|
||||
pub const StructMember = struct {
|
||||
name: []const u8,
|
||||
type: Type,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
bool,
|
||||
@"struct": []const u8,
|
||||
number: NumberType,
|
||||
vector: VectorType,
|
||||
matrix: MatrixType,
|
||||
atomic: AtomicType,
|
||||
array: ArrayType,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Expression = union(enum) {
|
||||
literal: Literal,
|
||||
ident: []const u8,
|
||||
unary: struct {
|
||||
op: UnaryOperator,
|
||||
expr: *const Expression,
|
||||
},
|
||||
binary: struct {
|
||||
op: BinaryOperator,
|
||||
lhs: *const Expression,
|
||||
rhs: *const Expression,
|
||||
},
|
||||
index: struct {
|
||||
base: *const Expression,
|
||||
index: *const Expression,
|
||||
},
|
||||
member: struct {
|
||||
base: *const Expression,
|
||||
field: []const u8,
|
||||
},
|
||||
bitcast: struct {
|
||||
expr: *const Expression,
|
||||
to: union(enum) {
|
||||
number: NumberType,
|
||||
vector: VectorType,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub const Literal = union(enum) {
|
||||
number: NumberLiteral,
|
||||
bool: bool,
|
||||
};
|
||||
|
||||
pub const NumberLiteral = union(enum) {
|
||||
int: i64,
|
||||
float: f64,
|
||||
};
|
||||
|
||||
pub const UnaryOperator = enum {
|
||||
not,
|
||||
negate,
|
||||
addr_of,
|
||||
deref,
|
||||
};
|
||||
|
||||
pub const BinaryOperator = enum {
|
||||
mul,
|
||||
div,
|
||||
mod,
|
||||
add,
|
||||
sub,
|
||||
shift_left,
|
||||
shift_right,
|
||||
binary_and,
|
||||
binary_or,
|
||||
binary_xor,
|
||||
circuit_and,
|
||||
circuit_or,
|
||||
equal,
|
||||
not_equal,
|
||||
less,
|
||||
less_equal,
|
||||
greater,
|
||||
greater_equal,
|
||||
};
|
||||
|
||||
pub const NumberType = enum {
|
||||
i32,
|
||||
u32,
|
||||
f32,
|
||||
f16,
|
||||
};
|
||||
|
||||
pub const VectorType = struct {
|
||||
size: Size,
|
||||
component_type: Type,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
bool,
|
||||
number: NumberType,
|
||||
};
|
||||
|
||||
pub const Size = enum {
|
||||
vec2,
|
||||
vec3,
|
||||
vec4,
|
||||
};
|
||||
};
|
||||
|
||||
pub const MatrixType = struct {
|
||||
size: Size,
|
||||
component_type: Type,
|
||||
|
||||
pub const Type = enum {
|
||||
f32,
|
||||
f16,
|
||||
abstract_float,
|
||||
};
|
||||
|
||||
pub const Size = enum {
|
||||
mat2x2,
|
||||
mat2x3,
|
||||
mat2x4,
|
||||
mat3x2,
|
||||
mat3x3,
|
||||
mat3x4,
|
||||
mat4x2,
|
||||
mat4x3,
|
||||
mat4x4,
|
||||
};
|
||||
};
|
||||
|
||||
pub const AtomicType = struct {
|
||||
component_type: Type,
|
||||
|
||||
pub const Type = enum {
|
||||
u32,
|
||||
i32,
|
||||
};
|
||||
};
|
||||
|
||||
pub const ArrayType = struct {
|
||||
component_type: Type,
|
||||
size: ?NumberLiteral = null,
|
||||
|
||||
pub const Type = union(enum) {
|
||||
bool,
|
||||
number: NumberType,
|
||||
@"struct": []const u8,
|
||||
vector: VectorType,
|
||||
matrix: MatrixType,
|
||||
atomic: AtomicType,
|
||||
array: *Type,
|
||||
};
|
||||
};
|
||||
|
|
@ -12,7 +12,7 @@ const Parser = @This();
|
|||
allocator: std.mem.Allocator,
|
||||
source: [:0]const u8,
|
||||
tok_i: Ast.Index,
|
||||
tokens: Ast.TokenList.Slice,
|
||||
tokens: std.MultiArrayList(Token),
|
||||
nodes: std.MultiArrayList(Ast.Node),
|
||||
extra: std.ArrayListUnmanaged(Ast.Index),
|
||||
scratch: std.ArrayListUnmanaged(Ast.Index),
|
||||
|
|
@ -20,6 +20,7 @@ errors: std.ArrayListUnmanaged(ErrorMsg),
|
|||
extensions: Extension.Array,
|
||||
|
||||
pub fn deinit(p: *Parser) void {
|
||||
p.tokens.deinit(p.allocator);
|
||||
p.nodes.deinit(p.allocator);
|
||||
p.extra.deinit(p.allocator);
|
||||
p.scratch.deinit(p.allocator);
|
||||
|
|
@ -27,10 +28,10 @@ pub fn deinit(p: *Parser) void {
|
|||
p.errors.deinit(p.allocator);
|
||||
}
|
||||
|
||||
pub fn parseRoot(p: *Parser) !void {
|
||||
pub fn translationUnit(p: *Parser) !?Ast.Index {
|
||||
const root = try p.addNode(.{ .tag = .span, .main_token = undefined });
|
||||
|
||||
while (try p.globalDirective()) |ext| {
|
||||
while (try p.globalDirectiveRecoverable()) |ext| {
|
||||
p.extensions.set(ext, true);
|
||||
}
|
||||
|
||||
|
|
@ -40,12 +41,24 @@ pub fn parseRoot(p: *Parser) !void {
|
|||
}
|
||||
|
||||
if (p.errors.items.len > 0) {
|
||||
return error.Parsing;
|
||||
return null;
|
||||
}
|
||||
|
||||
try p.extra.appendSlice(p.allocator, p.scratch.items);
|
||||
p.nodes.items(.lhs)[root] = @intCast(Ast.Index, p.extra.items.len - p.scratch.items.len);
|
||||
p.nodes.items(.rhs)[root] = @intCast(Ast.Index, p.extra.items.len);
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
pub fn globalDirectiveRecoverable(p: *Parser) !?Extension {
|
||||
return p.globalDirective() catch |err| switch (err) {
|
||||
error.Parsing => {
|
||||
p.findNextGlobalDirective();
|
||||
return null;
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn globalDirective(p: *Parser) !?Extension {
|
||||
|
|
@ -61,7 +74,7 @@ pub fn globalDirective(p: *Parser) !?Extension {
|
|||
pub fn expectGlobalDeclRecoverable(p: *Parser) !?Ast.Index {
|
||||
return p.expectGlobalDecl() catch |err| switch (err) {
|
||||
error.Parsing => {
|
||||
p.findNextGlobal();
|
||||
p.findNextGlobalDecl();
|
||||
return null;
|
||||
},
|
||||
else => return err,
|
||||
|
|
@ -1056,13 +1069,29 @@ pub fn typeSpecifier(p: *Parser) !?Ast.Index {
|
|||
}
|
||||
|
||||
pub fn typeSpecifierWithoutIdent(p: *Parser) !?Ast.Index {
|
||||
if (p.isVectorPrefix() or p.isMatrixPrefix()) {
|
||||
if (p.isVectorPrefix()) {
|
||||
const main_token = p.advanceToken();
|
||||
|
||||
_ = try p.expectToken(.less_than);
|
||||
const elem_type = try p.expectTypeSpecifier();
|
||||
_ = try p.expectToken(.greater_than);
|
||||
|
||||
return try p.addNode(.{
|
||||
.tag = if (p.isVectorPrefix()) .vector_type else .matrix_type,
|
||||
.tag = .vector_type,
|
||||
.main_token = main_token,
|
||||
.lhs = elem_type,
|
||||
});
|
||||
}
|
||||
|
||||
if (p.isMatrixPrefix()) {
|
||||
const main_token = p.advanceToken();
|
||||
|
||||
_ = try p.expectToken(.less_than);
|
||||
const elem_type = try p.expectTypeSpecifier();
|
||||
_ = try p.expectToken(.greater_than);
|
||||
|
||||
return try p.addNode(.{
|
||||
.tag = .matrix_type,
|
||||
.main_token = main_token,
|
||||
.lhs = elem_type,
|
||||
});
|
||||
|
|
@ -1074,10 +1103,13 @@ pub fn typeSpecifierWithoutIdent(p: *Parser) !?Ast.Index {
|
|||
.k_u32,
|
||||
.k_f32,
|
||||
.k_f16,
|
||||
.k_bool,
|
||||
=> {
|
||||
_ = p.advanceToken();
|
||||
return try p.addNode(.{ .tag = .scalar_type, .main_token = main_token });
|
||||
return try p.addNode(.{ .tag = .number_type, .main_token = main_token });
|
||||
},
|
||||
.k_bool => {
|
||||
_ = p.advanceToken();
|
||||
return try p.addNode(.{ .tag = .bool_type, .main_token = main_token });
|
||||
},
|
||||
.k_sampler, .k_comparison_sampler => {
|
||||
_ = p.advanceToken();
|
||||
|
|
@ -1328,7 +1360,8 @@ pub fn callExpr(p: *Parser) !?Ast.Index {
|
|||
const type_node = try p.typeSpecifierWithoutIdent() orelse return null;
|
||||
const tag = p.nodes.items(.tag)[type_node];
|
||||
switch (tag) {
|
||||
.scalar_type,
|
||||
.bool_type,
|
||||
.number_type,
|
||||
.vector_type,
|
||||
.matrix_type,
|
||||
.array_type,
|
||||
|
|
@ -1730,7 +1763,20 @@ pub fn componentOrSwizzleSpecifier(p: *Parser, prefix: Ast.Index) !Ast.Index {
|
|||
}
|
||||
}
|
||||
|
||||
fn findNextGlobal(p: *Parser) void {
|
||||
fn findNextGlobalDirective(p: *Parser) void {
|
||||
while (true) {
|
||||
switch (p.peekToken(.tag, 0)) {
|
||||
.k_enable, .k_require, .eof => return,
|
||||
.semicolon => {
|
||||
_ = p.advanceToken();
|
||||
return;
|
||||
},
|
||||
else => _ = p.advanceToken(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn findNextGlobalDecl(p: *Parser) void {
|
||||
var level: Ast.Index = 0;
|
||||
while (true) {
|
||||
switch (p.peekToken(.tag, 0)) {
|
||||
|
|
|
|||
|
|
@ -214,6 +214,8 @@ pub const Tag = enum {
|
|||
k_override,
|
||||
/// 'ptr'
|
||||
k_ptr,
|
||||
/// 'require'
|
||||
k_require,
|
||||
/// 'return'
|
||||
k_return,
|
||||
/// 'sampler'
|
||||
|
|
@ -363,6 +365,7 @@ pub const Tag = enum {
|
|||
.k_mat4x4 => "mat4x4",
|
||||
.k_override => "override",
|
||||
.k_ptr => "ptr",
|
||||
.k_require => "require",
|
||||
.k_return => "return",
|
||||
.k_sampler => "sampler",
|
||||
.k_comparison_sampler => "sampler_comparison",
|
||||
|
|
@ -433,6 +436,7 @@ pub const keywords = std.ComptimeStringMap(Tag, .{
|
|||
.{ "mat4x4", .k_mat4x4 },
|
||||
.{ "override", .k_override },
|
||||
.{ "ptr", .k_ptr },
|
||||
.{ "require", .k_require },
|
||||
.{ "return", .k_return },
|
||||
.{ "sampler", .k_sampler },
|
||||
.{ "sampler_comparison", .k_comparison_sampler },
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const Ast = @import("Ast.zig");
|
||||
pub const Analyse = @import("Analyse.zig");
|
||||
pub const IR = @import("IR.zig");
|
||||
pub const Parser = @import("Parser.zig");
|
||||
pub const Token = @import("Token.zig");
|
||||
pub const Tokenizer = @import("Tokenizer.zig");
|
||||
|
|
|
|||
|
|
@ -86,17 +86,19 @@ fn printCode(writer: anytype, term: std.debug.TTY.Config, source: []const u8, lo
|
|||
try writer.writeByte('\n');
|
||||
}
|
||||
|
||||
fn expectTree(source: [:0]const u8) !dusk.Ast {
|
||||
fn expectIR(source: [:0]const u8) !dusk.IR {
|
||||
var res = try dusk.Ast.parse(allocator, source);
|
||||
switch (res) {
|
||||
.tree => |*tree| {
|
||||
errdefer tree.deinit(allocator);
|
||||
if (try tree.analyse(allocator)) |errors| {
|
||||
try printErrors(errors, source, null);
|
||||
allocator.free(errors);
|
||||
return error.Analysing;
|
||||
defer tree.deinit(allocator);
|
||||
switch (try dusk.IR.generate(allocator, tree)) {
|
||||
.ir => |ir| return ir,
|
||||
.errors => |err_msgs| {
|
||||
try printErrors(err_msgs, source, null);
|
||||
allocator.free(err_msgs);
|
||||
return error.ExpectedIR;
|
||||
},
|
||||
}
|
||||
return tree.*;
|
||||
},
|
||||
.errors => |err_msgs| {
|
||||
try printErrors(err_msgs, source, null);
|
||||
|
|
@ -107,20 +109,27 @@ fn expectTree(source: [:0]const u8) !dusk.Ast {
|
|||
}
|
||||
|
||||
fn expectError(source: [:0]const u8, err: dusk.ErrorMsg) !void {
|
||||
var res = try dusk.Ast.parse(allocator, source);
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{ .stack_trace_frames = 12 }){};
|
||||
const all = gpa.allocator();
|
||||
defer _ = gpa.deinit();
|
||||
var res = try dusk.Ast.parse(all, source);
|
||||
const err_list = switch (res) {
|
||||
.tree => |*tree| blk: {
|
||||
defer tree.deinit(allocator);
|
||||
if (try tree.analyse(allocator)) |errors| {
|
||||
break :blk errors;
|
||||
defer tree.deinit(all);
|
||||
switch (try dusk.IR.generate(all, tree)) {
|
||||
.ir => |*ir| {
|
||||
ir.deinit();
|
||||
return error.ExpectedError;
|
||||
},
|
||||
.errors => |err_msgs| break :blk err_msgs,
|
||||
}
|
||||
return error.ExpectedError;
|
||||
},
|
||||
.errors => |err_msgs| err_msgs,
|
||||
};
|
||||
defer {
|
||||
for (err_list) |*err_msg| err_msg.deinit(allocator);
|
||||
allocator.free(err_list);
|
||||
for (err_list) |*err_msg| err_msg.deinit(all);
|
||||
all.free(err_list);
|
||||
}
|
||||
{
|
||||
errdefer {
|
||||
|
|
@ -162,65 +171,65 @@ fn expectError(source: [:0]const u8, err: dusk.ErrorMsg) !void {
|
|||
|
||||
test "empty" {
|
||||
const source = "";
|
||||
var tree = try expectTree(source);
|
||||
defer tree.deinit(allocator);
|
||||
var ir = try expectIR(source);
|
||||
defer ir.deinit();
|
||||
}
|
||||
|
||||
test "boids" {
|
||||
const source = @embedFile("boids.wgsl");
|
||||
var tree = try expectTree(source);
|
||||
defer tree.deinit(allocator);
|
||||
var ir = try expectIR(source);
|
||||
defer ir.deinit();
|
||||
}
|
||||
|
||||
test "gkurve" {
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
const source = @embedFile("gkurve.wgsl");
|
||||
var tree = try expectTree(source);
|
||||
defer tree.deinit(allocator);
|
||||
var ir = try expectIR(source);
|
||||
defer ir.deinit();
|
||||
}
|
||||
|
||||
test "variable & expressions" {
|
||||
const source = "var expr = 1 + 5 + 2 * 3 > 6 >> 7;";
|
||||
// const source = "var expr = 1 + 5 + 2 * 3 > 6 >> 7;";
|
||||
|
||||
var tree = try expectTree(source);
|
||||
defer tree.deinit(allocator);
|
||||
// var ir = try expectIR(source);
|
||||
// defer ir.deinit();
|
||||
|
||||
const root_node = 0;
|
||||
try expect(tree.nodeLHS(root_node) + 1 == tree.nodeRHS(root_node));
|
||||
// const root_node = 0;
|
||||
// try expect(ir.nodeLHS(root_node) + 1 == ir.nodeRHS(root_node));
|
||||
|
||||
const variable = tree.spanToList(root_node)[0];
|
||||
const variable_name = tree.tokenLoc(tree.extraData(dusk.Ast.Node.GlobalVarDecl, tree.nodeLHS(variable)).name);
|
||||
try expect(std.mem.eql(u8, "expr", variable_name.slice(source)));
|
||||
try expect(tree.nodeTag(variable) == .global_variable);
|
||||
try expect(tree.tokenTag(tree.nodeToken(variable)) == .k_var);
|
||||
// const variable = ir.spanToList(root_node)[0];
|
||||
// const variable_name = ir.tokenLoc(ir.extraData(dusk.Ast.Node.GlobalVarDecl, ir.nodeLHS(variable)).name);
|
||||
// try expect(std.mem.eql(u8, "expr", variable_name.slice(source)));
|
||||
// try expect(ir.nodeTag(variable) == .global_variable);
|
||||
// try expect(ir.tokenTag(ir.nodeToken(variable)) == .k_var);
|
||||
|
||||
const expr = tree.nodeRHS(variable);
|
||||
try expect(tree.nodeTag(expr) == .greater);
|
||||
// const expr = ir.nodeRHS(variable);
|
||||
// try expect(ir.nodeTag(expr) == .greater);
|
||||
|
||||
const @"1 + 5 + 2 * 3" = tree.nodeLHS(expr);
|
||||
try expect(tree.nodeTag(@"1 + 5 + 2 * 3") == .add);
|
||||
// const @"1 + 5 + 2 * 3" = ir.nodeLHS(expr);
|
||||
// try expect(ir.nodeTag(@"1 + 5 + 2 * 3") == .add);
|
||||
|
||||
const @"1 + 5" = tree.nodeLHS(@"1 + 5 + 2 * 3");
|
||||
try expect(tree.nodeTag(@"1 + 5") == .add);
|
||||
// const @"1 + 5" = ir.nodeLHS(@"1 + 5 + 2 * 3");
|
||||
// try expect(ir.nodeTag(@"1 + 5") == .add);
|
||||
|
||||
const @"1" = tree.nodeLHS(@"1 + 5");
|
||||
try expect(tree.nodeTag(@"1") == .number_literal);
|
||||
// const @"1" = ir.nodeLHS(@"1 + 5");
|
||||
// try expect(ir.nodeTag(@"1") == .number_literal);
|
||||
|
||||
const @"5" = tree.nodeRHS(@"1 + 5");
|
||||
try expect(tree.nodeTag(@"5") == .number_literal);
|
||||
// const @"5" = ir.nodeRHS(@"1 + 5");
|
||||
// try expect(ir.nodeTag(@"5") == .number_literal);
|
||||
|
||||
const @"2 * 3" = tree.nodeRHS(@"1 + 5 + 2 * 3");
|
||||
try expect(tree.nodeTag(@"2 * 3") == .mul);
|
||||
// const @"2 * 3" = ir.nodeRHS(@"1 + 5 + 2 * 3");
|
||||
// try expect(ir.nodeTag(@"2 * 3") == .mul);
|
||||
|
||||
const @"6 >> 7" = tree.nodeRHS(expr);
|
||||
try expect(tree.nodeTag(@"6 >> 7") == .shift_right);
|
||||
// const @"6 >> 7" = ir.nodeRHS(expr);
|
||||
// try expect(ir.nodeTag(@"6 >> 7") == .shift_right);
|
||||
|
||||
const @"6" = tree.nodeLHS(@"6 >> 7");
|
||||
try expect(tree.nodeTag(@"6") == .number_literal);
|
||||
// const @"6" = ir.nodeLHS(@"6 >> 7");
|
||||
// try expect(ir.nodeTag(@"6") == .number_literal);
|
||||
|
||||
const @"7" = tree.nodeRHS(@"6 >> 7");
|
||||
try expect(tree.nodeTag(@"7") == .number_literal);
|
||||
// const @"7" = ir.nodeRHS(@"6 >> 7");
|
||||
// try expect(ir.nodeTag(@"7") == .number_literal);
|
||||
}
|
||||
|
||||
test "analyser errors" {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue