Compare commits
No commits in common. "842dbd56cf45e382a7de2e9737a20af010e15f91" and "f100aa0d4649a1386c5cba798349b0fc2711baa5" have entirely different histories.
842dbd56cf
...
f100aa0d46
|
@ -1 +1,3 @@
|
||||||
|
tools/sokol-shdc filter=lfs diff=lfs merge=lfs -text
|
||||||
|
tools/sokol-shdc.exe filter=lfs diff=lfs merge=lfs -text
|
||||||
*.bmp filter=lfs diff=lfs merge=lfs -text
|
*.bmp filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated assets
|
# Generated assets
|
||||||
.zig-cache
|
/.zig-cache
|
||||||
/zig-out
|
/zig-out
|
||||||
*.spv
|
*.glsl.zig
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[submodule "ext/spirv-cross"]
|
|
||||||
path = ext/spirv-cross
|
|
||||||
url = https://github.com/KhronosGroup/SPIRV-Cross/
|
|
|
@ -7,7 +7,7 @@
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"target": "${workspaceRoot}/zig-out/bin/main",
|
"target": "${workspaceRoot}/zig-out/bin/main",
|
||||||
"cwd": "${workspaceRoot}/debug/",
|
"cwd": "${workspaceRoot}/debug/",
|
||||||
"valuesFormatting": "prettyPrinters",
|
"valuesFormatting": "parseText",
|
||||||
"preLaunchTask": "Build All"
|
"preLaunchTask": "Build All"
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,17 +10,5 @@
|
||||||
"editor.detectIndentation": false,
|
"editor.detectIndentation": false,
|
||||||
"editor.insertSpaces": false,
|
"editor.insertSpaces": false,
|
||||||
"editor.rulers": [120],
|
"editor.rulers": [120],
|
||||||
},
|
}
|
||||||
|
|
||||||
"files.exclude": {
|
|
||||||
"**/.git": true,
|
|
||||||
"**/.svn": true,
|
|
||||||
"**/.hg": true,
|
|
||||||
"**/CVS": true,
|
|
||||||
"**/.DS_Store": true,
|
|
||||||
"**/Thumbs.db": true,
|
|
||||||
"**/.zig-cache": true,
|
|
||||||
"zig-out": true,
|
|
||||||
".drone.yml": true,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
29
b64_dump.py
29
b64_dump.py
|
@ -1,29 +0,0 @@
|
||||||
#!/bin/python
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import base64
|
|
||||||
import struct
|
|
||||||
|
|
||||||
def format_base64_to_u32(base64_string):
|
|
||||||
# Decode the Base64 string
|
|
||||||
decoded_bytes = base64.b64decode(base64_string)
|
|
||||||
|
|
||||||
# Interpret the bytes as a sequence of u32 values
|
|
||||||
u32_values = struct.unpack('I' * (len(decoded_bytes) // 4), decoded_bytes)
|
|
||||||
|
|
||||||
# Format the u32 values as C-style hex values
|
|
||||||
formatted_u32_values = ', '.join(f'0x{value:08x}' for value in u32_values)
|
|
||||||
|
|
||||||
# Split the formatted string into lines of 8 values each
|
|
||||||
lines = [', '.join(formatted_u32_values.split(', ')[i:i+8]) for i in range(0, len(u32_values), 8)]
|
|
||||||
|
|
||||||
# Print the formatted values
|
|
||||||
print(',\n'.join(lines))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if len(sys.argv) != 2:
|
|
||||||
print(f"Usage: python {sys.argv[0]} <base64_string>")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
base64_string = sys.argv[1]
|
|
||||||
format_base64_to_u32(base64_string)
|
|
166
build.zig
166
build.zig
|
@ -6,49 +6,34 @@ pub fn build(b: *std.Build) !void {
|
||||||
const target = b.standardTargetOptions(.{});
|
const target = b.standardTargetOptions(.{});
|
||||||
const optimize = b.standardOptimizeOption(.{});
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const sokol_dependency = b.dependency("sokol", .{
|
|
||||||
.target = target,
|
|
||||||
.optimize = optimize,
|
|
||||||
});
|
|
||||||
|
|
||||||
const coral_module = b.createModule(.{
|
const coral_module = b.createModule(.{
|
||||||
.root_source_file = b.path("src/coral/coral.zig"),
|
.root_source_file = b.path("src/coral/coral.zig"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const flow_module = b.createModule(.{
|
const ona_module = add: {
|
||||||
.root_source_file = b.path("src/flow/flow.zig"),
|
const sokol_dependency = b.dependency("sokol", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
.imports = &.{
|
const module = b.addModule("ona", .{
|
||||||
.{
|
.root_source_file = b.path("src/ona/ona.zig"),
|
||||||
.name = "coral",
|
|
||||||
.module = coral_module,
|
.imports = &.{
|
||||||
|
.{
|
||||||
|
.name = "sokol",
|
||||||
|
.module = sokol_dependency.module("sokol"),
|
||||||
|
},
|
||||||
|
|
||||||
|
.{
|
||||||
|
.name = "coral",
|
||||||
|
.module = coral_module,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
const ona_module = b.createModule(.{
|
break: add module;
|
||||||
.root_source_file = b.path("src/ona/ona.zig"),
|
};
|
||||||
|
|
||||||
.imports = &.{
|
|
||||||
.{
|
|
||||||
.name = "sokol",
|
|
||||||
.module = sokol_dependency.module("sokol"),
|
|
||||||
},
|
|
||||||
|
|
||||||
.{
|
|
||||||
.name = "coral",
|
|
||||||
.module = coral_module,
|
|
||||||
},
|
|
||||||
|
|
||||||
.{
|
|
||||||
.name = "flow",
|
|
||||||
.module = flow_module,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ona_module.addIncludePath(b.path("ext/"));
|
|
||||||
ona_module.linkLibrary(build_spirvcross(b, target, optimize));
|
|
||||||
|
|
||||||
b.step("test", "Run unit tests").dependOn(create: {
|
b.step("test", "Run unit tests").dependOn(create: {
|
||||||
const tests = b.addTest(.{
|
const tests = b.addTest(.{
|
||||||
|
@ -61,59 +46,92 @@ pub fn build(b: *std.Build) !void {
|
||||||
});
|
});
|
||||||
|
|
||||||
b.installArtifact(add: {
|
b.installArtifact(add: {
|
||||||
const exe = b.addExecutable(.{
|
const compile_step = b.addExecutable(.{
|
||||||
.name = "main",
|
.name = "main",
|
||||||
.root_source_file = b.path("src/main.zig"),
|
.root_source_file = b.path("src/main.zig"),
|
||||||
.target = target,
|
.target = target,
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
|
|
||||||
exe.root_module.addImport("ona", ona_module);
|
compile_step.root_module.addImport("ona", ona_module);
|
||||||
exe.root_module.addImport("coral", coral_module);
|
compile_step.root_module.addImport("coral", coral_module);
|
||||||
exe.linkLibC();
|
compile_step.linkLibC();
|
||||||
exe.linkSystemLibrary("SDL2");
|
compile_step.linkSystemLibrary("SDL2");
|
||||||
|
|
||||||
break: add exe;
|
try depend_on_shaders(b, target, "src/ona/gfx/shaders/", &compile_step.step);
|
||||||
|
|
||||||
|
break: add compile_step;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_spirvcross(
|
fn depend_on_shaders(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
mode: std.builtin.OptimizeMode,
|
shader_dir_path: []const u8,
|
||||||
) *std.Build.Step.Compile {
|
step: *std.Build.Step,
|
||||||
const dir = "ext/spirv-cross/";
|
) !void {
|
||||||
|
var dir = try std.fs.cwd().openDir(shader_dir_path, .{ .iterate = true });
|
||||||
|
|
||||||
const sources = [_][]const u8{
|
defer dir.close();
|
||||||
"spirv_cross.cpp",
|
|
||||||
"spirv_parser.cpp",
|
var walker = try dir.walk(b.allocator);
|
||||||
"spirv_cross_parsed_ir.cpp",
|
|
||||||
"spirv_cfg.cpp",
|
defer walker.deinit();
|
||||||
"spirv_glsl.cpp",
|
|
||||||
"spirv_msl.cpp",
|
const shdc_path = switch (builtin.os.tag) {
|
||||||
"spirv_hlsl.cpp",
|
.windows => "./tools/sokol-shdc.exe",
|
||||||
"spirv_reflect.cpp",
|
.linux => "./tools/sokol-shdc",
|
||||||
"spirv_cross_util.cpp",
|
else => @compileError("cannot compile sokol shaders on this platform"),
|
||||||
"spirv_cross_c.cpp",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const lib = b.addStaticLibrary(.{
|
const path_buffer_max = 255;
|
||||||
.name = "spirvcross",
|
var input_path_buffer = [_]u8{undefined} ** path_buffer_max;
|
||||||
.target = target,
|
var output_path_buffer = [_]u8{undefined} ** path_buffer_max;
|
||||||
.optimize = mode,
|
|
||||||
});
|
|
||||||
|
|
||||||
switch (lib.rootModuleTarget().abi) {
|
const glsl = if (target.result.isDarwin()) "glsl410" else "glsl430";
|
||||||
.msvc => lib.linkLibC(),
|
const slang = glsl ++ ":metal_macos:hlsl5:glsl300es:wgsl";
|
||||||
else => lib.linkLibCpp(),
|
|
||||||
|
while (try walker.next()) |entry| {
|
||||||
|
if (entry.kind != .file or !std.mem.endsWith(u8, entry.path, ".glsl")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const input_path = try std.fmt.bufPrint(&input_path_buffer, "{s}{s}", .{shader_dir_path, entry.path});
|
||||||
|
const output_path = try std.fmt.bufPrint(&output_path_buffer, "{s}.zig", .{input_path});
|
||||||
|
const output = std.fs.path.basename(output_path);
|
||||||
|
|
||||||
|
dir.access(output, .{.mode = .read_only}) catch {
|
||||||
|
const cmd = b.addSystemCommand(&.{
|
||||||
|
shdc_path,
|
||||||
|
"-i",
|
||||||
|
input_path,
|
||||||
|
"-o",
|
||||||
|
output_path,
|
||||||
|
"-l",
|
||||||
|
slang,
|
||||||
|
"-f",
|
||||||
|
"sokol_zig",
|
||||||
|
});
|
||||||
|
|
||||||
|
step.dependOn(&cmd.step);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ((try dir.statFile(entry.path)).mtime > (try dir.statFile(output)).mtime) {
|
||||||
|
const cmd = b.addSystemCommand(&.{
|
||||||
|
shdc_path,
|
||||||
|
"-i",
|
||||||
|
input_path,
|
||||||
|
"-o",
|
||||||
|
output_path,
|
||||||
|
"-l",
|
||||||
|
slang,
|
||||||
|
"-f",
|
||||||
|
"sokol_zig",
|
||||||
|
});
|
||||||
|
|
||||||
|
step.dependOn(&cmd.step);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (sources) |src| {
|
|
||||||
lib.addCSourceFile(.{
|
|
||||||
.file = b.path(dir ++ src),
|
|
||||||
.flags = &.{"-fstrict-aliasing", "-DSPIRV_CROSS_C_API_GLSL", "-DSPIRV_CROSS_C_API_HLSL", "-DSPIRV_CROSS_C_API_MSL"},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return lib;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
.{
|
.{
|
||||||
.name = "Ona",
|
.name = "Ona",
|
||||||
.version = "0.0.1",
|
.version = "0.0.1",
|
||||||
.paths = .{
|
.paths = .{
|
||||||
"src",
|
"src",
|
||||||
"build.zig",
|
"build.zig",
|
||||||
"build.zig.zon",
|
"build.zig.zon",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"README.md",
|
"README.md",
|
||||||
},
|
},
|
||||||
.dependencies = .{
|
.dependencies = .{
|
||||||
.sokol = .{
|
.sokol = .{
|
||||||
.url = "git+https://github.com/floooh/sokol-zig.git#7c25767e51aa06dd5fb0684e4a8f2200d182ef27",
|
.url = "git+https://github.com/floooh/sokol-zig.git#7c25767e51aa06dd5fb0684e4a8f2200d182ef27",
|
||||||
.hash = "1220fa7f47fbaf2f1ed8c17fab2d23b6a85bcbbc4aa0b3802c90a3e8bf6fca1f8569",
|
.hash = "1220fa7f47fbaf2f1ed8c17fab2d23b6a85bcbbc4aa0b3802c90a3e8bf6fca1f8569",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 6fd1f75636b1c424b809ad8a84804654cf5ae48b
|
|
|
@ -1,21 +1,30 @@
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const heap = @import("./heap.zig");
|
||||||
|
|
||||||
const states = @import("./states.zig");
|
const map = @import("./map.zig");
|
||||||
|
|
||||||
|
const resource = @import("./resource.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const stack = @import("./stack.zig");
|
||||||
|
|
||||||
const system = @import("./system.zig");
|
const system = @import("./system.zig");
|
||||||
|
|
||||||
thread_pool: ?*std.Thread.Pool = null,
|
thread_pool: ?*std.Thread.Pool = null,
|
||||||
thread_restricted_resources: [std.enums.values(states.ThreadRestriction).len]states.Table,
|
thread_restricted_resources: [std.enums.values(ThreadRestriction).len]resource.Table,
|
||||||
event_systems: coral.stack.Sequential(system.Schedule),
|
event_systems: stack.Sequential(system.Schedule),
|
||||||
|
|
||||||
pub const Event = enum (usize) { _ };
|
pub const Event = enum (usize) { _ };
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
pub const ThreadRestriction = enum {
|
||||||
|
none,
|
||||||
|
main,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn create_event(self: *Self, label: []const u8) std.mem.Allocator.Error!Event {
|
pub fn create_event(self: *Self, label: []const u8) std.mem.Allocator.Error!Event {
|
||||||
var systems = try system.Schedule.init(label);
|
var systems = try system.Schedule.init(label);
|
||||||
|
|
||||||
|
@ -39,7 +48,7 @@ pub fn deinit(self: *Self) void {
|
||||||
|
|
||||||
if (self.thread_pool) |thread_pool| {
|
if (self.thread_pool) |thread_pool| {
|
||||||
thread_pool.deinit();
|
thread_pool.deinit();
|
||||||
coral.heap.allocator.destroy(thread_pool);
|
heap.allocator.destroy(thread_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.event_systems.deinit();
|
self.event_systems.deinit();
|
||||||
|
@ -47,25 +56,25 @@ pub fn deinit(self: *Self) void {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_resource(self: Self, comptime State: type) ?*State {
|
pub fn get_resource(self: Self, thread_restriction: ThreadRestriction, comptime Resource: type) ?*Resource {
|
||||||
return @ptrCast(@alignCast(self.thread_restricted_resources[@intFromEnum(states.thread_restriction(State))].get(State)));
|
return @ptrCast(@alignCast(self.thread_restricted_resources[@intFromEnum(thread_restriction)].get(Resource)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_get_resource(self: *Self, value: anytype) std.mem.Allocator.Error!*@TypeOf(value) {
|
pub fn set_get_resource(self: *Self, thread_restriction: ThreadRestriction, value: anytype) std.mem.Allocator.Error!*@TypeOf(value) {
|
||||||
return self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set_get(value);
|
return self.thread_restricted_resources[@intFromEnum(thread_restriction)].set_get(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(thread_count: u32) std.Thread.SpawnError!Self {
|
pub fn init(thread_count: u32) std.Thread.SpawnError!Self {
|
||||||
var world = Self{
|
var world = Self{
|
||||||
.thread_restricted_resources = .{states.Table.init(), states.Table.init()},
|
.thread_restricted_resources = .{resource.Table.init(), resource.Table.init()},
|
||||||
.event_systems = .{.allocator = coral.heap.allocator},
|
.event_systems = .{.allocator = heap.allocator},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (thread_count != 0 and !builtin.single_threaded) {
|
if (thread_count != 0 and !builtin.single_threaded) {
|
||||||
const thread_pool = try coral.heap.allocator.create(std.Thread.Pool);
|
const thread_pool = try heap.allocator.create(std.Thread.Pool);
|
||||||
|
|
||||||
try thread_pool.init(.{
|
try thread_pool.init(.{
|
||||||
.allocator = coral.heap.allocator,
|
.allocator = heap.allocator,
|
||||||
.n_jobs = thread_count,
|
.n_jobs = thread_count,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -83,6 +92,6 @@ pub fn run_event(self: *Self, event: Event) anyerror!void {
|
||||||
try self.event_systems.values[@intFromEnum(event)].run(self);
|
try self.event_systems.values[@intFromEnum(event)].run(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_resource(self: *Self, value: anytype) std.mem.Allocator.Error!void {
|
pub fn set_resource(self: *Self, thread_restriction: ThreadRestriction, value: anytype) std.mem.Allocator.Error!void {
|
||||||
try self.thread_restricted_resources[@intFromEnum(states.thread_restriction(@TypeOf(value)))].set(value);
|
try self.thread_restricted_resources[@intFromEnum(thread_restriction)].set(value);
|
||||||
}
|
}
|
|
@ -115,7 +115,7 @@ pub const DecimalFormat = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn format(self: DecimalFormat, writer: io.Writer, value: anytype) io.PrintError!void {
|
pub fn print(self: DecimalFormat, writer: io.Writer, value: anytype) io.PrintError!void {
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
return io.print(writer, switch (self.positive_prefix) {
|
return io.print(writer, switch (self.positive_prefix) {
|
||||||
.none => "0",
|
.none => "0",
|
||||||
|
@ -178,13 +178,13 @@ pub const HexadecimalFormat = struct {
|
||||||
positive_prefix: enum {none, plus, space} = .none,
|
positive_prefix: enum {none, plus, space} = .none,
|
||||||
casing: enum {lower, upper} = .lower,
|
casing: enum {lower, upper} = .lower,
|
||||||
|
|
||||||
pub const default = HexadecimalFormat{
|
const default = HexadecimalFormat{
|
||||||
.delimiter = "",
|
.delimiter = "",
|
||||||
.positive_prefix = .none,
|
.positive_prefix = .none,
|
||||||
.casing = .lower,
|
.casing = .lower,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn format(self: HexadecimalFormat, writer: io.Writer, value: anytype) io.Error!void {
|
pub fn print(self: HexadecimalFormat, writer: io.Writer, value: anytype) ?usize {
|
||||||
// TODO: Implement.
|
// TODO: Implement.
|
||||||
_ = self;
|
_ = self;
|
||||||
_ = writer;
|
_ = writer;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
pub const ascii = @import("./ascii.zig");
|
pub const ascii = @import("./ascii.zig");
|
||||||
|
|
||||||
|
pub const dag = @import("./dag.zig");
|
||||||
|
|
||||||
pub const files = @import("./files.zig");
|
pub const files = @import("./files.zig");
|
||||||
|
|
||||||
pub const hashes = @import("./hashes.zig");
|
pub const hashes = @import("./hashes.zig");
|
||||||
|
@ -14,8 +16,296 @@ pub const scalars = @import("./scalars.zig");
|
||||||
|
|
||||||
pub const slices = @import("./slices.zig");
|
pub const slices = @import("./slices.zig");
|
||||||
|
|
||||||
|
pub const slots = @import("./slots.zig");
|
||||||
|
|
||||||
pub const stack = @import("./stack.zig");
|
pub const stack = @import("./stack.zig");
|
||||||
|
|
||||||
|
pub const system = @import("./system.zig");
|
||||||
|
|
||||||
pub const utf8 = @import("./utf8.zig");
|
pub const utf8 = @import("./utf8.zig");
|
||||||
|
|
||||||
pub const vectors = @import("./vectors.zig");
|
pub const vectors = @import("./vectors.zig");
|
||||||
|
|
||||||
|
pub const World = @import("./World.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const ResourceOptions = struct {
|
||||||
|
thread_restriction: World.ThreadRestriction,
|
||||||
|
read_only: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn Read(comptime Value: type) type {
|
||||||
|
return Resource(Value, .{
|
||||||
|
.thread_restriction = .none,
|
||||||
|
.read_only = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ReadBlocking(comptime Value: type) type {
|
||||||
|
return Resource(Value, .{
|
||||||
|
.thread_restriction = .main,
|
||||||
|
.read_only = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Resource(comptime Value: type, comptime options: ResourceOptions) type {
|
||||||
|
const value_info = @typeInfo(Value);
|
||||||
|
|
||||||
|
const Qualified = switch (value_info) {
|
||||||
|
.Optional => @Type(.{
|
||||||
|
.Optional = .{
|
||||||
|
.child = .{
|
||||||
|
.Pointer = .{
|
||||||
|
.is_allowzero = false,
|
||||||
|
.sentinel = null,
|
||||||
|
.address_space = .generic,
|
||||||
|
.is_volatile = false,
|
||||||
|
.alignment = @alignOf(Value),
|
||||||
|
.size = .One,
|
||||||
|
.child = Value,
|
||||||
|
.is_const = options.read_only,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
else => @Type(.{
|
||||||
|
.Pointer = .{
|
||||||
|
.is_allowzero = false,
|
||||||
|
.sentinel = null,
|
||||||
|
.address_space = .generic,
|
||||||
|
.is_volatile = false,
|
||||||
|
.alignment = @alignOf(Value),
|
||||||
|
.size = .One,
|
||||||
|
.child = Value,
|
||||||
|
.is_const = options.read_only,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
res: Qualified,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub const State = struct {
|
||||||
|
res: Qualified,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn bind(context: system.BindContext) std.mem.Allocator.Error!State {
|
||||||
|
const thread_restriction_name = switch (thread_restriction) {
|
||||||
|
.main => "main thread-restricted ",
|
||||||
|
.none => ""
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = switch (options.read_only) {
|
||||||
|
true => (try context.register_read_only_resource_access(thread_restriction, Value)),
|
||||||
|
false => (try context.register_read_write_resource_access(thread_restriction, Value)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.res = switch (value_info) {
|
||||||
|
.Optional => res,
|
||||||
|
|
||||||
|
else => res orelse {
|
||||||
|
@panic(std.fmt.comptimePrint("attempt to use {s}{s} {s} that has not yet been set", .{
|
||||||
|
thread_restriction_name,
|
||||||
|
if (options.read_only) "read-only" else "read-write",
|
||||||
|
@typeName(Value),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(state: *State) Self {
|
||||||
|
return .{
|
||||||
|
.res = state.res,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const thread_restriction = options.thread_restriction;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Write(comptime Value: type) type {
|
||||||
|
return Resource(Value, .{
|
||||||
|
.thread_restriction = .none,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn WriteBlocking(comptime Value: type) type {
|
||||||
|
return Resource(Value, .{
|
||||||
|
.thread_restriction = .main,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parameter_type(comptime Value: type) *const system.Info.Parameter {
|
||||||
|
const has_state = @hasDecl(Value, "State");
|
||||||
|
|
||||||
|
if (@sizeOf(Value) == 0) {
|
||||||
|
@compileError("System parameters must have a non-zero size");
|
||||||
|
}
|
||||||
|
|
||||||
|
const parameters = struct {
|
||||||
|
fn bind(allocator: std.mem.Allocator, context: system.BindContext) std.mem.Allocator.Error!?*anyopaque {
|
||||||
|
if (has_state) {
|
||||||
|
const value_name = @typeName(Value);
|
||||||
|
|
||||||
|
if (!@hasDecl(Value, "bind")) {
|
||||||
|
@compileError(
|
||||||
|
"a `bind` declaration on " ++
|
||||||
|
value_name ++
|
||||||
|
" is requied for parameter types with a `State` declaration");
|
||||||
|
}
|
||||||
|
|
||||||
|
const bind_type = @typeInfo(@TypeOf(Value.bind));
|
||||||
|
|
||||||
|
if (bind_type != .Fn) {
|
||||||
|
@compileError("`bind` declaration on " ++ value_name ++ " must be a fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bind_type.Fn.params.len != 1 or bind_type.Fn.params[0].type.? != system.BindContext) {
|
||||||
|
@compileError(
|
||||||
|
"`bind` fn on " ++
|
||||||
|
value_name ++
|
||||||
|
" must accept " ++
|
||||||
|
@typeName(system.BindContext) ++
|
||||||
|
" as it's one and only argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
const state = try allocator.create(Value.State);
|
||||||
|
|
||||||
|
state.* = switch (bind_type.Fn.return_type.?) {
|
||||||
|
Value.State => Value.bind(context),
|
||||||
|
std.mem.Allocator.Error!Value.State => try Value.bind(context),
|
||||||
|
else => @compileError(
|
||||||
|
"`bind` fn on " ++
|
||||||
|
@typeName(Value) ++
|
||||||
|
" must return " ++
|
||||||
|
@typeName(Value.State) ++
|
||||||
|
" or " ++
|
||||||
|
@typeName(std.mem.Allocator.Error!Value.State)),
|
||||||
|
};
|
||||||
|
|
||||||
|
return @ptrCast(state);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(argument: *anyopaque, state: ?*anyopaque) void {
|
||||||
|
const value_name = @typeName(Value);
|
||||||
|
|
||||||
|
if (!@hasDecl(Value, "init")) {
|
||||||
|
@compileError("an `init` declaration on " ++ value_name ++ " is requied for parameter types");
|
||||||
|
}
|
||||||
|
|
||||||
|
const init_type = @typeInfo(@TypeOf(Value.init));
|
||||||
|
|
||||||
|
if (init_type != .Fn) {
|
||||||
|
@compileError("`init` declaration on " ++ value_name ++ " must be a fn");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_type.Fn.return_type.? != Value) {
|
||||||
|
@compileError("`init` fn on " ++ value_name ++ " must return a " ++ value_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const concrete_argument = @as(*Value, @ptrCast(@alignCast(argument)));
|
||||||
|
|
||||||
|
if (has_state) {
|
||||||
|
if (init_type.Fn.params.len != 1 or init_type.Fn.params[0].type.? != *Value.State) {
|
||||||
|
@compileError("`init` fn on stateful " ++ value_name ++ " must accept a " ++ @typeName(*Value.State));
|
||||||
|
}
|
||||||
|
|
||||||
|
concrete_argument.* = Value.init(@ptrCast(@alignCast(state.?)));
|
||||||
|
} else {
|
||||||
|
if (init_type.Fn.params.len != 0) {
|
||||||
|
@compileError("`init` fn on statelss " ++ value_name ++ " cannot use parameters");
|
||||||
|
}
|
||||||
|
|
||||||
|
concrete_argument.* = Value.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unbind(allocator: std.mem.Allocator, state: ?*anyopaque) void {
|
||||||
|
if (@hasDecl(Value, "unbind")) {
|
||||||
|
if (has_state) {
|
||||||
|
const typed_state = @as(*Value.State, @ptrCast(@alignCast(state.?)));
|
||||||
|
|
||||||
|
Value.unbind(typed_state);
|
||||||
|
allocator.destroy(typed_state);
|
||||||
|
} else {
|
||||||
|
Value.unbind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return comptime &.{
|
||||||
|
.thread_restriction = if (@hasDecl(Value, "thread_restriction")) Value.thread_restriction else .none,
|
||||||
|
.init = parameters.init,
|
||||||
|
.bind = parameters.bind,
|
||||||
|
.unbind = parameters.unbind,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_fn(comptime call: anytype) *const system.Info {
|
||||||
|
const Call = @TypeOf(call);
|
||||||
|
|
||||||
|
const system_info = comptime generate: {
|
||||||
|
switch (@typeInfo(Call)) {
|
||||||
|
.Fn => |call_fn| {
|
||||||
|
if (call_fn.params.len > system.max_parameters) {
|
||||||
|
@compileError("number of parameters to `call` cannot be more than 16");
|
||||||
|
}
|
||||||
|
|
||||||
|
const systems = struct {
|
||||||
|
fn run(parameters: []const *const system.Info.Parameter, states: *const [system.max_parameters]?*anyopaque) anyerror!void {
|
||||||
|
var call_args = @as(std.meta.ArgsTuple(Call), undefined);
|
||||||
|
|
||||||
|
inline for (parameters, &call_args, states[0 .. parameters.len]) |parameter, *call_arg, state| {
|
||||||
|
parameter.init(call_arg, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (@typeInfo(call_fn.return_type.?)) {
|
||||||
|
.Void => @call(.auto, call, call_args),
|
||||||
|
.ErrorUnion => try @call(.auto, call, call_args),
|
||||||
|
else => @compileError("number of parameters to `call` must return void or !void"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var parameters = @as([system.max_parameters]*const system.Info.Parameter, undefined);
|
||||||
|
var thread_restriction = World.ThreadRestriction.none;
|
||||||
|
|
||||||
|
for (0 .. call_fn.params.len) |index| {
|
||||||
|
const CallParam = call_fn.params[index].type.?;
|
||||||
|
const parameter = parameter_type(CallParam);
|
||||||
|
|
||||||
|
if (parameter.thread_restriction != .none) {
|
||||||
|
if (thread_restriction != .none and thread_restriction != parameter.thread_restriction) {
|
||||||
|
@compileError("a system may not have conflicting thread restrictions");
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_restriction = parameter.thread_restriction;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[index] = parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
break: generate &.{
|
||||||
|
.parameters = parameters,
|
||||||
|
.parameter_count = call_fn.params.len,
|
||||||
|
.execute = systems.run,
|
||||||
|
.thread_restriction = thread_restriction,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @compileError("parameter `call` must be a function"),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return system_info;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
const coral = @import("coral");
|
const stack = @import("./stack.zig");
|
||||||
|
|
||||||
|
const slices = @import("./slices.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
@ -7,9 +9,9 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
node_count: usize = 0,
|
node_count: usize = 0,
|
||||||
table: NodeTables,
|
table: NodeTables,
|
||||||
|
|
||||||
const NodeTables = coral.stack.Parallel(struct {
|
const NodeTables = stack.Parallel(struct {
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
edges: coral.stack.Sequential(Node),
|
edges: stack.Sequential(Node),
|
||||||
is_occupied: bool = true,
|
is_occupied: bool = true,
|
||||||
is_visited: bool = false,
|
is_visited: bool = false,
|
||||||
});
|
});
|
||||||
|
@ -80,7 +82,7 @@ pub fn Graph(comptime Payload: type) type {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (coral.slices.index_of(edges.values, 0, edge_node) == null) {
|
if (slices.index_of(edges.values, 0, edge_node) == null) {
|
||||||
try edges.push_grow(edge_node);
|
try edges.push_grow(edge_node);
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,23 +108,13 @@ pub fn Generator(comptime Output: type, comptime input_types: []const type) type
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NullWritable = struct {
|
pub const PrintError = Error || error {
|
||||||
written: usize = 0,
|
IncompleteWrite,
|
||||||
|
|
||||||
pub fn write(self: *NullWritable, buffer: []const Byte) Error!usize {
|
|
||||||
self.written += buffer.len;
|
|
||||||
|
|
||||||
return buffer.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn writer(self: *NullWritable) Writer {
|
|
||||||
return Writer.bind(NullWritable, self, write);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Reader = Generator(Error!usize, &.{[]Byte});
|
pub const Reader = Generator(Error!usize, &.{[]coral.Byte});
|
||||||
|
|
||||||
pub const Writer = Generator(Error!usize, &.{[]const Byte});
|
pub const Writer = Generator(Error!usize, &.{[]const coral.Byte});
|
||||||
|
|
||||||
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
pub fn alloc_read(input: coral.io.Reader, allocator: std.mem.Allocator) []coral.Byte {
|
||||||
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
const buffer = coral.Stack(coral.Byte){.allocator = allocator};
|
||||||
|
@ -148,6 +138,12 @@ pub fn bytes_of(value: anytype) []const Byte {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(writer: Writer, utf8: []const u8) PrintError!void {
|
||||||
|
if (try writer.yield(.{utf8}) != utf8.len) {
|
||||||
|
return error.IncompleteWrite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn skip_n(input: Reader, distance: u64) Error!void {
|
pub fn skip_n(input: Reader, distance: u64) Error!void {
|
||||||
var buffer = @as([512]coral.Byte, undefined);
|
var buffer = @as([512]coral.Byte, undefined);
|
||||||
var remaining = distance;
|
var remaining = distance;
|
||||||
|
@ -163,7 +159,7 @@ pub fn skip_n(input: Reader, distance: u64) Error!void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slice_sentineled(comptime Sentinel: type, comptime sen: Sentinel, ptr: [*:sen]const Sentinel) [:sen]const Sentinel {
|
pub fn slice_sentineled(comptime sen: anytype, ptr: [*:sen]const @TypeOf(sen)) [:sen]const @TypeOf(sen) {
|
||||||
var len = @as(usize, 0);
|
var len = @as(usize, 0);
|
||||||
|
|
||||||
while (ptr[len] != sen) {
|
while (ptr[len] != sen) {
|
||||||
|
@ -210,9 +206,3 @@ pub fn stream_n(input: Reader, output: Writer, limit: usize) Error!usize {
|
||||||
remaining -= read;
|
remaining -= read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_all(writer: Writer, utf8: []const u8) Error!void {
|
|
||||||
if (try writer.yield(.{utf8}) != utf8.len) {
|
|
||||||
return error.UnavailableResource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
const coral = @import("coral");
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const heap = @import("./heap.zig");
|
||||||
|
|
||||||
|
const map = @import("./map.zig");
|
||||||
|
|
||||||
pub const Table = struct {
|
pub const Table = struct {
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
table: coral.map.Hashed(TypeID, Entry, coral.map.enum_traits(TypeID)),
|
table: map.Hashed(TypeID, Entry, map.enum_traits(TypeID)),
|
||||||
|
|
||||||
const Entry = struct {
|
const Entry = struct {
|
||||||
ptr: *anyopaque,
|
ptr: *anyopaque,
|
||||||
|
@ -27,8 +29,8 @@ pub const Table = struct {
|
||||||
|
|
||||||
pub fn init() Table {
|
pub fn init() Table {
|
||||||
return .{
|
return .{
|
||||||
.arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
.arena = std.heap.ArenaAllocator.init(heap.allocator),
|
||||||
.table = .{.allocator = coral.heap.allocator},
|
.table = .{.allocator = heap.allocator},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +61,6 @@ pub const Table = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ThreadRestriction = enum {
|
|
||||||
none,
|
|
||||||
main,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TypeID = enum (usize) { _ };
|
pub const TypeID = enum (usize) { _ };
|
||||||
|
|
||||||
pub fn type_id(comptime T: type) TypeID {
|
pub fn type_id(comptime T: type) TypeID {
|
||||||
|
@ -77,11 +74,3 @@ pub fn type_id(comptime T: type) TypeID {
|
||||||
|
|
||||||
return @enumFromInt(@intFromPtr(&TypeHandle.byte));
|
return @enumFromInt(@intFromPtr(&TypeHandle.byte));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_restriction(comptime State: type) ThreadRestriction {
|
|
||||||
if (@hasDecl(State, "thread_restriction")) {
|
|
||||||
return State.thread_restriction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .none;
|
|
||||||
}
|
|
|
@ -0,0 +1,998 @@
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const file = @import("../file.zig");
|
||||||
|
|
||||||
|
const script = @import("../script.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const tokens = @import("./tokens.zig");
|
||||||
|
|
||||||
|
const tree = @import("./tree.zig");
|
||||||
|
|
||||||
|
name: *script.Object,
|
||||||
|
arity: u8,
|
||||||
|
opcodes: coral.Stack(Opcode),
|
||||||
|
lines: coral.Stack(tokens.Line),
|
||||||
|
cursor: usize,
|
||||||
|
constants: coral.Stack(*script.Object),
|
||||||
|
bindings: []?*script.Object,
|
||||||
|
externals: *script.Object,
|
||||||
|
|
||||||
|
const Compiler = struct {
|
||||||
|
chunk: *Self,
|
||||||
|
env: *script.Runtime,
|
||||||
|
|
||||||
|
fn compile_argument(self: *const Compiler, environment: *const tree.Environment, initial_argument: ?*const tree.Expr) script.Error!u8 {
|
||||||
|
// TODO: Exceeding 255 arguments will make the VM crash.
|
||||||
|
var maybe_argument = initial_argument;
|
||||||
|
var argument_count = @as(u8, 0);
|
||||||
|
|
||||||
|
while (maybe_argument) |argument| {
|
||||||
|
try self.compile_expression(environment, argument, null);
|
||||||
|
|
||||||
|
maybe_argument = argument.next;
|
||||||
|
argument_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return argument_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_expression(self: *const Compiler, environment: *const tree.Environment, expression: *const tree.Expr, name: ?[]const u8) script.Error!void {
|
||||||
|
const number_format = coral.utf8.DecimalFormat{
|
||||||
|
.delimiter = "_",
|
||||||
|
.positive_prefix = .none,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (expression.kind) {
|
||||||
|
.nil_literal => try self.chunk.write(expression.line, .push_nil),
|
||||||
|
.true_literal => try self.chunk.write(expression.line, .push_true),
|
||||||
|
.false_literal => try self.chunk.write(expression.line, .push_false),
|
||||||
|
|
||||||
|
.number_literal => |literal| {
|
||||||
|
for (literal) |codepoint| {
|
||||||
|
if (codepoint == '.') {
|
||||||
|
return self.chunk.write(expression.line, .{
|
||||||
|
.push_const = try self.declare_float(number_format.parse(literal, script.Float) orelse unreachable),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, .{
|
||||||
|
.push_const = try self.declare_fixed(number_format.parse(literal, script.Fixed) orelse unreachable),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.string_literal => |literal| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_string(literal)});
|
||||||
|
},
|
||||||
|
|
||||||
|
.vector2 => |vector2| {
|
||||||
|
try self.compile_expression(environment, vector2.x, null);
|
||||||
|
try self.compile_expression(environment, vector2.y, null);
|
||||||
|
try self.chunk.write(expression.line, .push_vector2);
|
||||||
|
},
|
||||||
|
|
||||||
|
.vector3 => |vector3| {
|
||||||
|
try self.compile_expression(environment, vector3.x, null);
|
||||||
|
try self.compile_expression(environment, vector3.y, null);
|
||||||
|
try self.compile_expression(environment, vector3.z, null);
|
||||||
|
try self.chunk.write(expression.line, .push_vector3);
|
||||||
|
},
|
||||||
|
|
||||||
|
.string_template => {
|
||||||
|
var current_expression = expression.next orelse {
|
||||||
|
return self.chunk.write(expression.line, .{.push_const = try self.declare_string("")});
|
||||||
|
};
|
||||||
|
|
||||||
|
var component_count = @as(u8, 0);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
try self.compile_expression(environment, current_expression, null);
|
||||||
|
|
||||||
|
component_count += 1;
|
||||||
|
|
||||||
|
current_expression = current_expression.next orelse {
|
||||||
|
return self.chunk.write(expression.line, .{.push_concat = component_count});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_literal => |literal| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(literal)});
|
||||||
|
},
|
||||||
|
|
||||||
|
.table => |table| {
|
||||||
|
var entries = table.nodes();
|
||||||
|
var num_entries = @as(u16, 0);
|
||||||
|
|
||||||
|
while (entries.next()) |entry| {
|
||||||
|
try self.compile_expression(environment, entry.key, null);
|
||||||
|
try self.compile_expression(environment, entry.value, null);
|
||||||
|
|
||||||
|
num_entries = coral.scalars.add(num_entries, 1) orelse {
|
||||||
|
return self.env.raise(error.OutOfMemory, "too many initializer values", .{});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, .{.push_table = num_entries});
|
||||||
|
},
|
||||||
|
|
||||||
|
.lambda_construct => |lambda_construct| {
|
||||||
|
var chunk = try Self.init(self.env, name orelse "<lambda>", lambda_construct.environment, &.{});
|
||||||
|
|
||||||
|
errdefer chunk.deinit(self.env);
|
||||||
|
|
||||||
|
if (lambda_construct.environment.capture_count == 0) {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
|
||||||
|
} else {
|
||||||
|
const lambda_captures = lambda_construct.environment.get_captures();
|
||||||
|
var index = lambda_captures.len;
|
||||||
|
|
||||||
|
while (index != 0) {
|
||||||
|
index -= 1;
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, switch (lambda_captures[index]) {
|
||||||
|
.declaration_index => |declaration_index| .{.push_local = declaration_index},
|
||||||
|
.capture_index => |capture_index| .{.push_binding = capture_index},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_chunk(chunk)});
|
||||||
|
try self.chunk.write(expression.line, .{.bind = lambda_construct.environment.capture_count});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.binary_op => |binary_op| {
|
||||||
|
try self.compile_expression(environment, binary_op.lhs_operand, null);
|
||||||
|
try self.compile_expression(environment, binary_op.rhs_operand, null);
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, switch (binary_op.operation) {
|
||||||
|
.addition => .add,
|
||||||
|
.subtraction => .sub,
|
||||||
|
.multiplication => .mul,
|
||||||
|
.divsion => .div,
|
||||||
|
.greater_equals_comparison => .cge,
|
||||||
|
.greater_than_comparison => .cgt,
|
||||||
|
.equals_comparison => .eql,
|
||||||
|
.less_than_comparison => .clt,
|
||||||
|
.less_equals_comparison => .cle,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.unary_op => |unary_op| {
|
||||||
|
try self.compile_expression(environment, unary_op.operand, null);
|
||||||
|
|
||||||
|
try self.chunk.write(expression.line, switch (unary_op.operation) {
|
||||||
|
.boolean_negation => .not,
|
||||||
|
.numeric_negation => .neg,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.invoke => |invoke| {
|
||||||
|
const argument_count = try self.compile_argument(environment, invoke.argument);
|
||||||
|
|
||||||
|
try self.compile_expression(environment, invoke.object, null);
|
||||||
|
try self.chunk.write(expression.line, .{.call = argument_count});
|
||||||
|
},
|
||||||
|
|
||||||
|
.group => |group| try self.compile_expression(environment, group, null),
|
||||||
|
|
||||||
|
.declaration_get => |declaration_get| {
|
||||||
|
if (get_local_index(environment, declaration_get.declaration)) |index| {
|
||||||
|
if (is_declaration_boxed(declaration_get.declaration)) {
|
||||||
|
try self.chunk.write(expression.line, .{.push_local = index});
|
||||||
|
try self.chunk.write(expression.line, .get_box);
|
||||||
|
} else {
|
||||||
|
try self.chunk.write(expression.line, .{.push_local = index});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try get_binding_index(environment, declaration_get.declaration)) |index| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_binding = index});
|
||||||
|
|
||||||
|
if (is_declaration_boxed(declaration_get.declaration)) {
|
||||||
|
try self.chunk.write(expression.line, .get_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.env.raise(error.IllegalState, "local out of scope", .{});
|
||||||
|
},
|
||||||
|
|
||||||
|
.declaration_set => |declaration_set| {
|
||||||
|
if (get_local_index(environment, declaration_set.declaration)) |index| {
|
||||||
|
if (is_declaration_boxed(declaration_set.declaration)) {
|
||||||
|
try self.chunk.write(expression.line, .{.push_local = index});
|
||||||
|
try self.compile_expression(environment, declaration_set.assign, null);
|
||||||
|
try self.chunk.write(expression.line, .set_box);
|
||||||
|
} else {
|
||||||
|
try self.compile_expression(environment, declaration_set.assign, null);
|
||||||
|
try self.chunk.write(expression.line, .{.set_local = index});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (try get_binding_index(environment, declaration_set.declaration)) |index| {
|
||||||
|
try self.compile_expression(environment, declaration_set.assign, null);
|
||||||
|
try self.chunk.write(expression.line, .{.push_binding = index});
|
||||||
|
|
||||||
|
if (is_declaration_boxed(declaration_set.declaration)) {
|
||||||
|
try self.chunk.write(expression.line, .set_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.env.raise(error.IllegalState, "local out of scope", .{});
|
||||||
|
},
|
||||||
|
|
||||||
|
.field_get => |field_get| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_get.identifier)});
|
||||||
|
try self.compile_expression(environment, field_get.object, null);
|
||||||
|
try self.chunk.write(expression.line, .get_dynamic);
|
||||||
|
},
|
||||||
|
|
||||||
|
.field_set => |field_set| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(field_set.identifier)});
|
||||||
|
try self.compile_expression(environment, field_set.assign, null);
|
||||||
|
try self.compile_expression(environment, field_set.object, null);
|
||||||
|
try self.chunk.write(expression.line, .set_dynamic);
|
||||||
|
},
|
||||||
|
|
||||||
|
.subscript_get => |subscript_get| {
|
||||||
|
try self.compile_expression(environment, subscript_get.index, null);
|
||||||
|
try self.compile_expression(environment, subscript_get.object, null);
|
||||||
|
try self.chunk.write(expression.line, .get_dynamic);
|
||||||
|
},
|
||||||
|
|
||||||
|
.subscript_set => |subscript_set| {
|
||||||
|
try self.compile_expression(environment, subscript_set.index, null);
|
||||||
|
try self.compile_expression(environment, subscript_set.assign, null);
|
||||||
|
try self.compile_expression(environment, subscript_set.object, null);
|
||||||
|
try self.chunk.write(expression.line, .set_dynamic);
|
||||||
|
},
|
||||||
|
|
||||||
|
.external_get => |external_get| {
|
||||||
|
try self.chunk.write(expression.line, .{.push_const = try self.declare_symbol(external_get.name)});
|
||||||
|
try self.chunk.write(expression.line, .get_external);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_environment(self: *const Compiler, environment: *const tree.Environment) script.Error!void {
|
||||||
|
if (environment.statement) |statement| {
|
||||||
|
const last_statement = try self.compile_statement(environment, statement);
|
||||||
|
|
||||||
|
if (last_statement.kind != .@"return") {
|
||||||
|
try self.chunk.write(last_statement.line, .push_nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_statement(self: *const Compiler, environment: *const tree.Environment, initial_statement: *const tree.Stmt) script.Error!*const tree.Stmt {
|
||||||
|
var current_statement = initial_statement;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (current_statement.kind) {
|
||||||
|
.@"return" => |@"return"| {
|
||||||
|
if (@"return".returned_expression) |expression| {
|
||||||
|
try self.compile_expression(environment, expression, null);
|
||||||
|
} else {
|
||||||
|
try self.chunk.write(current_statement.line, .push_nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Omit ret calls at ends of chunk.
|
||||||
|
try self.chunk.write(current_statement.line, .ret);
|
||||||
|
},
|
||||||
|
|
||||||
|
.@"while" => |@"while"| {
|
||||||
|
try self.compile_expression(environment, @"while".loop_expression, null);
|
||||||
|
try self.chunk.write(current_statement.line, .{.jf = 0});
|
||||||
|
|
||||||
|
const origin_index = @as(u16, @intCast(self.chunk.opcodes.values.len - 1));
|
||||||
|
|
||||||
|
_ = try self.compile_statement(environment, @"while".loop);
|
||||||
|
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
|
||||||
|
|
||||||
|
try self.compile_expression(environment, @"while".loop_expression, null);
|
||||||
|
try self.chunk.write(current_statement.line, .{.jt = origin_index});
|
||||||
|
},
|
||||||
|
|
||||||
|
.@"if" => |@"if"| {
|
||||||
|
try self.compile_expression(environment, @"if".then_expression, null);
|
||||||
|
try self.chunk.write(current_statement.line, .{.jf = 0});
|
||||||
|
|
||||||
|
const origin_index = @as(u16, @intCast(self.chunk.opcodes.values.len - 1));
|
||||||
|
|
||||||
|
_ = try self.compile_statement(environment, @"if".@"then");
|
||||||
|
self.chunk.opcodes.values[origin_index].jf = @intCast(self.chunk.opcodes.values.len - 1);
|
||||||
|
|
||||||
|
if (@"if".@"else") |@"else"| {
|
||||||
|
_ = try self.compile_statement(environment, @"else");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.declare => |declare| {
|
||||||
|
try self.compile_expression(environment, declare.initial_expression, declare.declaration.identifier);
|
||||||
|
|
||||||
|
if (is_declaration_boxed(declare.declaration)) {
|
||||||
|
try self.chunk.write(current_statement.line, .push_boxed);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.top_expression => |top_expression| {
|
||||||
|
try self.compile_expression(environment, top_expression, null);
|
||||||
|
|
||||||
|
if (top_expression.kind == .invoke) {
|
||||||
|
try self.chunk.write(current_statement.line, .pop);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
current_statement = current_statement.next orelse return current_statement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const constants_max = @as(usize, std.math.maxInt(u16));
|
||||||
|
|
||||||
|
fn declare_chunk(self: *const Compiler, chunk: Self) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == std.math.maxInt(u16)) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = @as(usize, std.math.maxInt(u16)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_dynamic(chunk)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_fixed(self: *const Compiler, fixed: script.Fixed) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_fixed(fixed)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_float(self: *const Compiler, float: script.Float) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_float(float)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_string(self: *const Compiler, string: []const u8) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_string(string)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_vector2(self: *const Compiler, vector: script.Vector2) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_vector2(vector)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_vector3(self: *const Compiler, vector: script.Vector3) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_vector3(vector)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_symbol(self: *const Compiler, symbol: []const u8) script.Error!u16 {
|
||||||
|
if (self.chunk.constants.values.len == constants_max) {
|
||||||
|
return self.env.raise(error.BadSyntax, "chunks cannot contain more than {max} constants", .{
|
||||||
|
.max = constants_max,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const constant = (try self.env.new_symbol(symbol)).pop().?;
|
||||||
|
|
||||||
|
errdefer self.env.release(constant);
|
||||||
|
|
||||||
|
try self.chunk.constants.push(constant);
|
||||||
|
|
||||||
|
return @intCast(self.chunk.constants.values.len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_binding_index(environment: *const tree.Environment, declaration: *const tree.Declaration) script.Error!?u8 {
|
||||||
|
var binding_index = @as(u8, 0);
|
||||||
|
|
||||||
|
while (binding_index < environment.capture_count) : (binding_index += 1) {
|
||||||
|
var capture = &environment.captures[binding_index];
|
||||||
|
var target_environment = environment.enclosing orelse return null;
|
||||||
|
|
||||||
|
while (capture.* == .capture_index) {
|
||||||
|
capture = &target_environment.captures[capture.capture_index];
|
||||||
|
target_environment = target_environment.enclosing orelse return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
std.debug.assert(capture.* == .declaration_index);
|
||||||
|
|
||||||
|
if (&target_environment.declarations[capture.declaration_index] == declaration) {
|
||||||
|
return binding_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_local_index(environment: *const tree.Environment, declaration: *const tree.Declaration) ?u8 {
|
||||||
|
var remaining = environment.declaration_count;
|
||||||
|
|
||||||
|
while (remaining != 0) {
|
||||||
|
remaining -= 1;
|
||||||
|
|
||||||
|
if (&environment.declarations[remaining] == declaration) {
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_declaration_boxed(declaration: *const tree.Declaration) bool {
|
||||||
|
return declaration.is.captured and !declaration.is.readonly;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Opcode = union (enum) {
|
||||||
|
ret,
|
||||||
|
pop,
|
||||||
|
push_nil,
|
||||||
|
push_true,
|
||||||
|
push_false,
|
||||||
|
push_const: u16,
|
||||||
|
push_local: u8,
|
||||||
|
push_top,
|
||||||
|
push_vector2,
|
||||||
|
push_vector3,
|
||||||
|
push_table: u16,
|
||||||
|
push_binding: u8,
|
||||||
|
push_concat: u8,
|
||||||
|
push_boxed,
|
||||||
|
set_local: u8,
|
||||||
|
get_dynamic,
|
||||||
|
set_dynamic,
|
||||||
|
get_external,
|
||||||
|
get_box,
|
||||||
|
set_box,
|
||||||
|
call: u8,
|
||||||
|
bind: u8,
|
||||||
|
|
||||||
|
not,
|
||||||
|
neg,
|
||||||
|
|
||||||
|
add,
|
||||||
|
sub,
|
||||||
|
mul,
|
||||||
|
div,
|
||||||
|
|
||||||
|
eql,
|
||||||
|
cgt,
|
||||||
|
clt,
|
||||||
|
cge,
|
||||||
|
cle,
|
||||||
|
|
||||||
|
jt: u16,
|
||||||
|
jf: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const External = struct {[]const u8, *script.Object};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, env: *script.Runtime) void {
|
||||||
|
while (self.constants.pop()) |constant| {
|
||||||
|
env.release(constant.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.constants.deinit();
|
||||||
|
self.opcodes.deinit();
|
||||||
|
self.lines.deinit();
|
||||||
|
env.release(self.name);
|
||||||
|
env.release(self.externals);
|
||||||
|
|
||||||
|
if (self.bindings.len != 0) {
|
||||||
|
for (self.bindings) |binding| {
|
||||||
|
if (binding) |value| {
|
||||||
|
env.release(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
env.allocator.free(self.bindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.bindings = &.{};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dump(chunk: Self, env: *script.Runtime) script.Error!*script.Object {
|
||||||
|
var opcode_cursor = @as(u32, 0);
|
||||||
|
var buffer = coral.list.ByteStack.init(env.allocator);
|
||||||
|
|
||||||
|
defer buffer.deinit();
|
||||||
|
|
||||||
|
const writer = coral.list.stack_as_writer(&buffer);
|
||||||
|
|
||||||
|
_ = coral.utf8.print_string(writer, "\n");
|
||||||
|
|
||||||
|
while (opcode_cursor < chunk.opcodes.values.len) : (opcode_cursor += 1) {
|
||||||
|
_ = coral.utf8.print_formatted(writer, "[{instruction}]: ", .{.instruction = opcode_cursor});
|
||||||
|
|
||||||
|
_ = switch (chunk.opcodes.values[opcode_cursor]) {
|
||||||
|
.ret => coral.utf8.print_string(writer, "ret\n"),
|
||||||
|
.pop => coral.utf8.print_string(writer, "pop\n"),
|
||||||
|
.push_nil => coral.utf8.print_string(writer, "push nil\n"),
|
||||||
|
.push_true => coral.utf8.print_string(writer, "push true\n"),
|
||||||
|
.push_false => coral.utf8.print_string(writer, "push false\n"),
|
||||||
|
|
||||||
|
.push_const => |push_const| print: {
|
||||||
|
const string_ref = (try (try env.push(try chunk.get_constant(env, push_const))).to_string()).pop().?;
|
||||||
|
|
||||||
|
defer env.release(string_ref);
|
||||||
|
|
||||||
|
const string = string_ref.is_string();
|
||||||
|
|
||||||
|
break: print coral.utf8.print_formatted(writer, "push const ({value})\n", .{.value = string.?});
|
||||||
|
},
|
||||||
|
|
||||||
|
.push_local => |push_local| coral.utf8.print_formatted(writer, "push local ({local})\n", .{
|
||||||
|
.local = push_local,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.push_top => coral.utf8.print_string(writer, "push top\n"),
|
||||||
|
|
||||||
|
.push_table => |push_table| coral.utf8.print_formatted(writer, "push table ({count})\n", .{
|
||||||
|
.count = push_table,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.push_boxed => coral.utf8.print_string(writer, "push boxed\n"),
|
||||||
|
|
||||||
|
.push_binding => |push_binding| coral.utf8.print_formatted(writer, "push binding ({binding})\n", .{
|
||||||
|
.binding = push_binding,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.push_concat => |push_concat| coral.utf8.print_formatted(writer, "push concat ({count})\n", .{
|
||||||
|
.count = push_concat,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.push_builtin => |push_builtin| coral.utf8.print_formatted(writer, "push builtin ({builtin})\n", .{
|
||||||
|
.builtin = switch (push_builtin) {
|
||||||
|
.import => "import",
|
||||||
|
.print => "print",
|
||||||
|
.vec2 => "vec2",
|
||||||
|
.vec3 => "vec3",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
.bind => |bind| coral.utf8.print_formatted(writer, "bind ({count})\n", .{
|
||||||
|
.count = bind,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.set_local => |local_set| coral.utf8.print_formatted(writer, "set local ({local})\n", .{
|
||||||
|
.local = local_set,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.get_box => coral.utf8.print_string(writer, "get box\n"),
|
||||||
|
.set_box => coral.utf8.print_string(writer, "set box\n"),
|
||||||
|
.get_dynamic => coral.utf8.print_string(writer, "get dynamic\n"),
|
||||||
|
.set_dynamic => coral.utf8.print_string(writer, "set dynamic\n"),
|
||||||
|
.call => |call| coral.utf8.print_formatted(writer, "call ({count})\n", .{.count = call}),
|
||||||
|
.not => coral.utf8.print_string(writer, "not\n"),
|
||||||
|
.neg => coral.utf8.print_string(writer, "neg\n"),
|
||||||
|
.add => coral.utf8.print_string(writer, "add\n"),
|
||||||
|
.sub => coral.utf8.print_string(writer, "sub\n"),
|
||||||
|
.mul => coral.utf8.print_string(writer, "mul\n"),
|
||||||
|
.div => coral.utf8.print_string(writer, "div\n"),
|
||||||
|
.eql => coral.utf8.print_string(writer, "eql\n"),
|
||||||
|
.cgt => coral.utf8.print_string(writer, "cgt\n"),
|
||||||
|
.clt => coral.utf8.print_string(writer, "clt\n"),
|
||||||
|
.cge => coral.utf8.print_string(writer, "cge\n"),
|
||||||
|
.cle => coral.utf8.print_string(writer, "cle\n"),
|
||||||
|
.jf => |jf| coral.utf8.print_formatted(writer, "jf ({instruction})\n", .{.instruction = jf}),
|
||||||
|
.jt => |jt| coral.utf8.print_formatted(writer, "jt ({instruction})\n", .{.instruction = jt}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (try env.new_string(buffer.values)).pop().?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(self: *Self, env: *script.Runtime) script.Error!?*script.Object {
|
||||||
|
self.cursor = 0;
|
||||||
|
|
||||||
|
while (self.cursor < self.opcodes.values.len) : (self.cursor += 1) {
|
||||||
|
switch (self.opcodes.values[self.cursor]) {
|
||||||
|
.ret => break,
|
||||||
|
.pop => env.discard(),
|
||||||
|
.push_nil => _ = try env.push(null),
|
||||||
|
.push_true => _ = try env.new_boolean(true),
|
||||||
|
.push_false => _ = try env.new_boolean(false),
|
||||||
|
.push_const => |push_const| _ = try env.push(try self.get_constant(env, push_const)),
|
||||||
|
.push_local => |push_local| _ = try env.local_get(push_local),
|
||||||
|
.push_top => _ = try env.local_top(),
|
||||||
|
|
||||||
|
.push_vector2 => {
|
||||||
|
const y = try env.expect_float(try env.expect_object(env.pop()));
|
||||||
|
const x = try env.expect_float(try env.expect_object(env.pop()));
|
||||||
|
|
||||||
|
_ = try env.new_vector2(.{@floatCast(x), @floatCast(y)});
|
||||||
|
},
|
||||||
|
|
||||||
|
.push_vector3 => {
|
||||||
|
const z = try env.expect_float(try env.expect_object(env.pop()));
|
||||||
|
const y = try env.expect_float(try env.expect_object(env.pop()));
|
||||||
|
const x = try env.expect_float(try env.expect_object(env.pop()));
|
||||||
|
|
||||||
|
_ = try env.new_vector3(.{@floatCast(x), @floatCast(y), @floatCast(z)});
|
||||||
|
},
|
||||||
|
|
||||||
|
.push_table => |push_table| {
|
||||||
|
const table = (try env.new_table()).pop().?;
|
||||||
|
|
||||||
|
defer env.release(table);
|
||||||
|
|
||||||
|
var popped = @as(usize, 0);
|
||||||
|
|
||||||
|
while (popped < push_table) : (popped += 1) {
|
||||||
|
if (env.pop()) |object| {
|
||||||
|
defer env.release(object);
|
||||||
|
|
||||||
|
try env.index_set(table, object);
|
||||||
|
} else {
|
||||||
|
env.release(try env.expect_object(env.pop()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try env.push(table);
|
||||||
|
},
|
||||||
|
|
||||||
|
.push_boxed => {
|
||||||
|
const value = env.pop();
|
||||||
|
|
||||||
|
defer {
|
||||||
|
if (value) |object| {
|
||||||
|
env.release(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try env.new_boxed(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.push_binding => |push_binding| _ = try env.push(try self.get_binding(env, push_binding)),
|
||||||
|
.push_concat => |push_concat| _ = try env.concat(push_concat),
|
||||||
|
|
||||||
|
.bind => |bind| {
|
||||||
|
const callable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(callable);
|
||||||
|
|
||||||
|
const chunk = try env.expect_dynamic(callable, Self);
|
||||||
|
|
||||||
|
if (chunk.bindings.len != 0) {
|
||||||
|
return env.raise(error.IllegalState, "cannot bind values to an already-bound chunk", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.bindings = try env.allocator.alloc(?*script.Object, bind);
|
||||||
|
|
||||||
|
errdefer env.allocator.free(chunk.bindings);
|
||||||
|
|
||||||
|
for (0 .. bind) |index| {
|
||||||
|
const value = env.pop();
|
||||||
|
|
||||||
|
errdefer {
|
||||||
|
if (value) |object| {
|
||||||
|
env.release(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.bindings[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try env.push(callable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.set_local => |local_set| _ = try env.local_set(local_set, env.pop()),
|
||||||
|
.get_box => _ = try env.boxed_get(),
|
||||||
|
|
||||||
|
.set_box => {
|
||||||
|
const value = env.pop();
|
||||||
|
|
||||||
|
defer {
|
||||||
|
if (value) |object| {
|
||||||
|
env.release(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try env.boxed_set(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.get_dynamic => {
|
||||||
|
const indexable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(indexable);
|
||||||
|
|
||||||
|
_ = try env.index_get(indexable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.set_dynamic => {
|
||||||
|
const indexable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(indexable);
|
||||||
|
|
||||||
|
const value = env.pop();
|
||||||
|
|
||||||
|
defer {
|
||||||
|
if (value) |object| {
|
||||||
|
env.release(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try env.index_set(indexable, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
.get_external => _ = try env.index_get(self.externals),
|
||||||
|
.call => |call| _ = try env.call(call),
|
||||||
|
|
||||||
|
.not => {
|
||||||
|
const object = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(object);
|
||||||
|
|
||||||
|
_ = try env.new_boolean(object.is_false());
|
||||||
|
},
|
||||||
|
|
||||||
|
.neg => _ = try env.neg(),
|
||||||
|
|
||||||
|
.add => {
|
||||||
|
const addable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(addable);
|
||||||
|
|
||||||
|
_ = switch (try env.expect_numeric(addable)) {
|
||||||
|
.fixed => |fixed| try env.fixed_add(fixed),
|
||||||
|
.float => |float| try env.float_add(float),
|
||||||
|
.vector2 => |vector2| try env.vector2_add(vector2),
|
||||||
|
.vector3 => |vector3| try env.vector3_add(vector3),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.sub => {
|
||||||
|
const subtractable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(subtractable);
|
||||||
|
|
||||||
|
_ = switch (try env.expect_numeric(subtractable)) {
|
||||||
|
.fixed => |fixed| try env.fixed_subtract(fixed),
|
||||||
|
.float => |float| try env.float_subtract(float),
|
||||||
|
.vector2 => |vector2| try env.vector2_subtract(vector2),
|
||||||
|
.vector3 => |vector3| try env.vector3_subtract(vector3),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.mul => {
|
||||||
|
const multiplicable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(multiplicable);
|
||||||
|
|
||||||
|
_ = switch (try env.expect_numeric(multiplicable)) {
|
||||||
|
.fixed => |fixed| try env.fixed_multiply(fixed),
|
||||||
|
.float => |float| try env.float_multiply(float),
|
||||||
|
.vector2 => |vector2| try env.vector2_multiply(vector2),
|
||||||
|
.vector3 => |vector3| try env.vector3_multiply(vector3),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.div => {
|
||||||
|
const divisible = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(divisible);
|
||||||
|
|
||||||
|
_ = switch (try env.expect_numeric(divisible)) {
|
||||||
|
.fixed => |fixed| try env.fixed_divide(fixed),
|
||||||
|
.float => |float| try env.float_divide(float),
|
||||||
|
.vector2 => |vector2| try env.vector2_divide(vector2),
|
||||||
|
.vector3 => |vector3| try env.vector3_divide(vector3),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.eql => {
|
||||||
|
if (env.pop()) |equatable| {
|
||||||
|
defer env.release(equatable);
|
||||||
|
|
||||||
|
_ = try env.equals_object(equatable);
|
||||||
|
} else {
|
||||||
|
_ = try env.equals_nil();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.cgt => {
|
||||||
|
const comparable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(comparable);
|
||||||
|
|
||||||
|
_ = try env.compare_greater(comparable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.clt => {
|
||||||
|
const comparable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(comparable);
|
||||||
|
|
||||||
|
_ = try env.compare_less(comparable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.cge => {
|
||||||
|
const comparable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(comparable);
|
||||||
|
|
||||||
|
_ = try env.compare_greater_equals(comparable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.cle => {
|
||||||
|
const comparable = try env.expect_object(env.pop());
|
||||||
|
|
||||||
|
defer env.release(comparable);
|
||||||
|
|
||||||
|
_ = try env.compare_less_equals(comparable);
|
||||||
|
},
|
||||||
|
|
||||||
|
.jf => |jf| {
|
||||||
|
if (env.pop()) |condition| {
|
||||||
|
defer env.release(condition);
|
||||||
|
|
||||||
|
if (condition.is_false()) {
|
||||||
|
self.cursor = jf;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.cursor = jf;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.jt => |jt| {
|
||||||
|
if (env.pop()) |condition| {
|
||||||
|
defer env.release(condition);
|
||||||
|
|
||||||
|
if (condition.is_true()) {
|
||||||
|
self.cursor = jt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return env.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_binding(self: *Self, env: *script.Runtime, index: usize) script.Error!?*script.Object {
|
||||||
|
if (index >= self.bindings.len) {
|
||||||
|
return env.raise(error.IllegalState, "invalid binding", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.bindings[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_constant(self: *const Self, env: *script.Runtime, index: usize) script.Error!*script.Object {
|
||||||
|
if (index >= self.constants.values.len) {
|
||||||
|
return env.raise(error.IllegalState, "invalid constant", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.constants.values[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(env: *script.Runtime, name: []const u8, environment: *const tree.Environment, externals: []const External) script.Error!Self {
|
||||||
|
const name_symbol = (try env.new_symbol(name)).pop().?;
|
||||||
|
|
||||||
|
errdefer env.release(name_symbol);
|
||||||
|
|
||||||
|
const externals_table = (try env.new_table()).pop().?;
|
||||||
|
|
||||||
|
errdefer env.release(externals_table);
|
||||||
|
|
||||||
|
for (0 .. externals.len) |i| {
|
||||||
|
const external_name, const external_object = externals[i];
|
||||||
|
|
||||||
|
try (try env.new_symbol(external_name)).index_set(externals_table, external_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunk = Self{
|
||||||
|
.externals = externals_table,
|
||||||
|
.name = name_symbol,
|
||||||
|
.opcodes = .{.allocator = env.allocator},
|
||||||
|
.constants = .{.allocator = env.allocator},
|
||||||
|
.lines = .{.allocator = env.allocator},
|
||||||
|
.bindings = &.{},
|
||||||
|
.arity = environment.argument_count,
|
||||||
|
.cursor = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var compiler = Compiler{
|
||||||
|
.chunk = &chunk,
|
||||||
|
.env = env,
|
||||||
|
};
|
||||||
|
|
||||||
|
try compiler.compile_environment(environment);
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const typeinfo = script.Typeinfo{
|
||||||
|
.name = "lambda",
|
||||||
|
.destruct = typeinfo_destruct,
|
||||||
|
.call = typeinfo_call,
|
||||||
|
.to_string = typeinfo_to_string,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn typeinfo_call(context: script.Typeinfo.CallContext) script.Error!?*script.Object {
|
||||||
|
return @as(*Self, @ptrCast(@alignCast(context.userdata))).execute(context.env);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_destruct(context: script.Typeinfo.DestructContext) void {
|
||||||
|
@as(*Self, @ptrCast(@alignCast(context.userdata))).deinit(context.env);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_to_string(context: script.Typeinfo.ToStringContext) script.Error!*script.Object {
|
||||||
|
return (try (try context.env.push(@as(*Self, @ptrCast(@alignCast(context.userdata))).name)).to_string()).pop().?;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(self: *Self, line: tokens.Line, opcode: Opcode) std.mem.Allocator.Error!void {
|
||||||
|
try self.opcodes.push(opcode);
|
||||||
|
try self.lines.push(line);
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const script = @import("../script.zig");
|
||||||
|
|
||||||
|
associative: coral.map.Table(*script.Object, *script.Object, struct {
|
||||||
|
pub const hash = script.Object.hash;
|
||||||
|
|
||||||
|
pub const equals = script.Object.equals;
|
||||||
|
}),
|
||||||
|
|
||||||
|
contiguous: coral.Stack(?*script.Object),
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn deinit(self: *Self, env: *script.Runtime) void {
|
||||||
|
{
|
||||||
|
var entries = self.associative.entries();
|
||||||
|
|
||||||
|
while (entries.next()) |entry| {
|
||||||
|
env.release(entry.key);
|
||||||
|
env.release(entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.associative.deinit();
|
||||||
|
|
||||||
|
while (self.contiguous.pop()) |value| {
|
||||||
|
if (value.*) |ref| {
|
||||||
|
env.release(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.contiguous.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(env: *script.Runtime) Self {
|
||||||
|
return .{
|
||||||
|
.associative = .{
|
||||||
|
.allocator = env.allocator,
|
||||||
|
.traits = .{},
|
||||||
|
},
|
||||||
|
|
||||||
|
.contiguous = .{.allocator = env.allocator},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const typeinfo = script.Typeinfo{
|
||||||
|
.name = "table",
|
||||||
|
.destruct = typeinfo_destruct,
|
||||||
|
.get = typeinfo_get,
|
||||||
|
.set = typeinfo_set,
|
||||||
|
.count = typeinfo_count,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn typeinfo_count(context: script.Typeinfo.CountContext) script.Error!script.Fixed {
|
||||||
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata)));
|
||||||
|
|
||||||
|
return @intCast(table.associative.len + table.contiguous.values.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_destruct(context: script.Typeinfo.DestructContext) void {
|
||||||
|
@as(*Self, @ptrCast(@alignCast(context.userdata))).deinit(context.env);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_get(context: script.Typeinfo.GetContext) script.Error!?*script.Object {
|
||||||
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata)));
|
||||||
|
const index = (try context.push_index()).pop().?;
|
||||||
|
|
||||||
|
defer context.env.release(index);
|
||||||
|
|
||||||
|
if (index.is_fixed()) |fixed| {
|
||||||
|
if (fixed < 0) {
|
||||||
|
// TODO: Negative indexing.
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixed < table.contiguous.values.len) {
|
||||||
|
return table.contiguous.values[@intCast(fixed)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table.associative.lookup(index)) |value| {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn typeinfo_set(context: script.Typeinfo.SetContext) script.Error!void {
|
||||||
|
const table = @as(*Self, @ptrCast(@alignCast(context.userdata)));
|
||||||
|
const index = (try context.push_index()).pop().?;
|
||||||
|
|
||||||
|
errdefer context.env.release(index);
|
||||||
|
|
||||||
|
if (index.is_fixed()) |fixed| {
|
||||||
|
if (fixed < 0) {
|
||||||
|
// TODO: Negative indexing.
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fixed < table.contiguous.values.len) {
|
||||||
|
const maybe_replacing = &table.contiguous.values[@intCast(fixed)];
|
||||||
|
|
||||||
|
if (maybe_replacing.*) |replacing| {
|
||||||
|
context.env.release(replacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((try context.push_value()).pop()) |value| {
|
||||||
|
errdefer context.env.release(value);
|
||||||
|
|
||||||
|
maybe_replacing.* = value;
|
||||||
|
} else {
|
||||||
|
maybe_replacing.* = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = (try context.push_value()).pop() orelse {
|
||||||
|
if (table.associative.remove(index)) |removed| {
|
||||||
|
context.env.release(removed.key);
|
||||||
|
context.env.release(removed.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
errdefer context.env.release(value);
|
||||||
|
|
||||||
|
if (try table.associative.replace(index, value)) |replaced| {
|
||||||
|
context.env.release(replaced.key);
|
||||||
|
context.env.release(replaced.value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,535 @@
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Line = struct {
|
||||||
|
number: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Token = union(enum) {
|
||||||
|
end,
|
||||||
|
unknown: u8,
|
||||||
|
newline,
|
||||||
|
identifier: []const u8,
|
||||||
|
builtin: []const u8,
|
||||||
|
|
||||||
|
symbol_plus,
|
||||||
|
symbol_minus,
|
||||||
|
symbol_asterisk,
|
||||||
|
symbol_forward_slash,
|
||||||
|
symbol_paren_left,
|
||||||
|
symbol_paren_right,
|
||||||
|
symbol_bang,
|
||||||
|
symbol_comma,
|
||||||
|
symbol_at,
|
||||||
|
symbol_brace_left,
|
||||||
|
symbol_brace_right,
|
||||||
|
symbol_bracket_left,
|
||||||
|
symbol_bracket_right,
|
||||||
|
symbol_period,
|
||||||
|
symbol_colon,
|
||||||
|
symbol_less_than,
|
||||||
|
symbol_less_equals,
|
||||||
|
symbol_greater_than,
|
||||||
|
symbol_greater_equals,
|
||||||
|
symbol_equals,
|
||||||
|
symbol_double_equals,
|
||||||
|
|
||||||
|
number: []const u8,
|
||||||
|
string: []const u8,
|
||||||
|
template_string: []const u8,
|
||||||
|
|
||||||
|
keyword_nil,
|
||||||
|
keyword_false,
|
||||||
|
keyword_true,
|
||||||
|
keyword_return,
|
||||||
|
keyword_self,
|
||||||
|
keyword_const,
|
||||||
|
keyword_if,
|
||||||
|
keyword_do,
|
||||||
|
keyword_end,
|
||||||
|
keyword_while,
|
||||||
|
keyword_else,
|
||||||
|
keyword_elif,
|
||||||
|
keyword_var,
|
||||||
|
keyword_vec2,
|
||||||
|
keyword_vec3,
|
||||||
|
keyword_let,
|
||||||
|
keyword_lambda,
|
||||||
|
|
||||||
|
pub fn text(self: Token) []const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.end => "end",
|
||||||
|
.unknown => |unknown| @as([*]const u8, @ptrCast(&unknown))[0 .. 1],
|
||||||
|
.newline => "newline",
|
||||||
|
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
.builtin => |identifier| identifier,
|
||||||
|
|
||||||
|
.symbol_plus => "+",
|
||||||
|
.symbol_minus => "-",
|
||||||
|
.symbol_asterisk => "*",
|
||||||
|
.symbol_forward_slash => "/",
|
||||||
|
.symbol_paren_left => "(",
|
||||||
|
.symbol_paren_right => ")",
|
||||||
|
.symbol_bang => "!",
|
||||||
|
.symbol_comma => ",",
|
||||||
|
.symbol_at => "@",
|
||||||
|
.symbol_brace_left => "{",
|
||||||
|
.symbol_brace_right => "}",
|
||||||
|
.symbol_bracket_left => "[",
|
||||||
|
.symbol_bracket_right => "]",
|
||||||
|
.symbol_period => ".",
|
||||||
|
.symbol_colon => ":",
|
||||||
|
.symbol_less_than => "<",
|
||||||
|
.symbol_less_equals => "<=",
|
||||||
|
.symbol_greater_than => ">",
|
||||||
|
.symbol_greater_equals => ">=",
|
||||||
|
.symbol_equals => "=",
|
||||||
|
.symbol_double_equals => "==",
|
||||||
|
|
||||||
|
.number => |literal| literal,
|
||||||
|
.string => |literal| literal,
|
||||||
|
.template_string => |literal| literal,
|
||||||
|
|
||||||
|
.keyword_const => "const",
|
||||||
|
.keyword_nil => "nil",
|
||||||
|
.keyword_false => "false",
|
||||||
|
.keyword_true => "true",
|
||||||
|
.keyword_return => "return",
|
||||||
|
.keyword_self => "self",
|
||||||
|
.keyword_if => "if",
|
||||||
|
.keyword_do => "do",
|
||||||
|
.keyword_end => "end",
|
||||||
|
.keyword_while => "while",
|
||||||
|
.keyword_elif => "elif",
|
||||||
|
.keyword_else => "else",
|
||||||
|
.keyword_var => "var",
|
||||||
|
.keyword_vec2 => "vec2",
|
||||||
|
.keyword_vec3 => "vec3",
|
||||||
|
.keyword_let => "let",
|
||||||
|
.keyword_lambda => "lambda",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Stream = struct {
|
||||||
|
source: []const u8,
|
||||||
|
line: Line = .{.number = 1},
|
||||||
|
token: Token = .newline,
|
||||||
|
|
||||||
|
pub fn skip_newlines(self: *Stream) void {
|
||||||
|
self.step();
|
||||||
|
|
||||||
|
while (self.token == .newline) {
|
||||||
|
self.step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(self: *Stream) void {
|
||||||
|
var cursor = @as(usize, 0);
|
||||||
|
|
||||||
|
defer self.source = self.source[cursor ..];
|
||||||
|
|
||||||
|
while (cursor < self.source.len) {
|
||||||
|
switch (self.source[cursor]) {
|
||||||
|
'#' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (cursor < self.source.len and self.source[cursor] != '\n') {
|
||||||
|
cursor += 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
' ', '\t' => cursor += 1,
|
||||||
|
|
||||||
|
'\n' => {
|
||||||
|
cursor += 1;
|
||||||
|
self.token = .newline;
|
||||||
|
self.line.number += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'0' ... '9' => {
|
||||||
|
const begin = cursor;
|
||||||
|
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'0' ... '9' => cursor += 1,
|
||||||
|
|
||||||
|
'.' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'0' ... '9' => cursor += 1,
|
||||||
|
else => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.token = .{.number = self.source[begin .. cursor]};
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.token = .{.number = self.source[begin .. cursor]};
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'A' ... 'Z', 'a' ... 'z', '_' => {
|
||||||
|
const begin = cursor;
|
||||||
|
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'0'...'9', 'A'...'Z', 'a'...'z', '_' => cursor += 1,
|
||||||
|
else => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
const identifier = self.source[begin .. cursor];
|
||||||
|
|
||||||
|
std.debug.assert(identifier.len != 0);
|
||||||
|
|
||||||
|
switch (identifier[0]) {
|
||||||
|
'c' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "onst")) {
|
||||||
|
self.token = .keyword_const;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'd' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "o")) {
|
||||||
|
self.token = .keyword_do;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'e' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "lse")) {
|
||||||
|
self.token = .keyword_else;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.are_equal(identifier[1 ..], "lif")) {
|
||||||
|
self.token = .keyword_elif;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.are_equal(identifier[1 ..], "nd")) {
|
||||||
|
self.token = .keyword_end;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'f' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "alse")) {
|
||||||
|
self.token = .keyword_false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'i' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "f")) {
|
||||||
|
self.token = .keyword_if;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'l' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "ambda")) {
|
||||||
|
self.token = .keyword_lambda;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.are_equal(identifier[1 ..], "et")) {
|
||||||
|
self.token = .keyword_let;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'n' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "il")) {
|
||||||
|
self.token = .keyword_nil;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'r' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "eturn")) {
|
||||||
|
self.token = .keyword_return;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
's' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "elf")) {
|
||||||
|
self.token = .keyword_self;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
't' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "rue")) {
|
||||||
|
self.token = .keyword_true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'v' => {
|
||||||
|
const rest = identifier[1 ..];
|
||||||
|
|
||||||
|
if (coral.are_equal(rest, "ar")) {
|
||||||
|
self.token = .keyword_var;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.are_equal(rest, "vec2")) {
|
||||||
|
self.token = .keyword_vec2;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coral.are_equal(rest, "vec3")) {
|
||||||
|
self.token = .keyword_vec3;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
'w' => {
|
||||||
|
if (coral.are_equal(identifier[1 ..], "hile")) {
|
||||||
|
self.token = .keyword_while;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.token = .{.identifier = identifier};
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'`' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
const begin = cursor;
|
||||||
|
|
||||||
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'`' => break,
|
||||||
|
else => cursor += 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.token = .{.template_string = self.source[begin .. cursor]};
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'"' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
const begin = cursor;
|
||||||
|
|
||||||
|
while (cursor < self.source.len) switch (self.source[cursor]) {
|
||||||
|
'"' => break,
|
||||||
|
else => cursor += 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.token = .{.string = self.source[begin .. cursor]};
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'{' => {
|
||||||
|
self.token = .symbol_brace_left;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'}' => {
|
||||||
|
self.token = .symbol_brace_right;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'[' => {
|
||||||
|
self.token = .symbol_bracket_left;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
']' => {
|
||||||
|
self.token = .symbol_bracket_right;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
',' => {
|
||||||
|
self.token = .symbol_comma;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'!' => {
|
||||||
|
self.token = .symbol_bang;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
')' => {
|
||||||
|
self.token = .symbol_paren_right;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'(' => {
|
||||||
|
self.token = .symbol_paren_left;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'/' => {
|
||||||
|
self.token = .symbol_forward_slash;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'*' => {
|
||||||
|
self.token = .symbol_asterisk;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'-' => {
|
||||||
|
self.token = .symbol_minus;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'+' => {
|
||||||
|
self.token = .symbol_plus;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
':' => {
|
||||||
|
self.token = .symbol_colon;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'=' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
if (cursor < self.source.len) {
|
||||||
|
switch (self.source[cursor]) {
|
||||||
|
'=' => {
|
||||||
|
cursor += 1;
|
||||||
|
self.token = .symbol_double_equals;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.token = .symbol_equals;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'<' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
if (cursor < self.source.len and (self.source[cursor] == '=')) {
|
||||||
|
cursor += 1;
|
||||||
|
self.token = .symbol_less_equals;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.token = .symbol_less_than;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'>' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
if (cursor < self.source.len and (self.source[cursor] == '=')) {
|
||||||
|
cursor += 1;
|
||||||
|
self.token = .symbol_greater_equals;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.token = .symbol_greater_than;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'.' => {
|
||||||
|
self.token = .symbol_period;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
'@' => {
|
||||||
|
self.token = .symbol_at;
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
self.token = .{.unknown = self.source[cursor]};
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.token = .end;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,268 @@
|
||||||
|
pub const Expr = @import("./tree/Expr.zig");
|
||||||
|
|
||||||
|
pub const Stmt = @import("./tree/Stmt.zig");
|
||||||
|
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const script = @import("../script.zig");
|
||||||
|
|
||||||
|
const tokens = @import("./tokens.zig");
|
||||||
|
|
||||||
|
pub const Declaration = struct {
|
||||||
|
identifier: []const coral.Byte,
|
||||||
|
|
||||||
|
is: packed struct {
|
||||||
|
readonly: bool = false,
|
||||||
|
captured: bool = false,
|
||||||
|
} = .{},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Environment = struct {
|
||||||
|
captures: [capture_max]Capture = [_]Capture{.{.declaration_index = 0}} ** capture_max,
|
||||||
|
capture_count: u8 = 0,
|
||||||
|
declarations: [declaration_max]Declaration = [_]Declaration{.{.identifier = ""}} ** declaration_max,
|
||||||
|
declaration_count: u8 = 0,
|
||||||
|
argument_count: u8 = 0,
|
||||||
|
statement: ?*const Stmt = null,
|
||||||
|
enclosing: ?*Environment = null,
|
||||||
|
|
||||||
|
pub const Capture = union (enum) {
|
||||||
|
declaration_index: u8,
|
||||||
|
capture_index: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeclareError = std.mem.Allocator.Error || error {
|
||||||
|
DeclarationExists,
|
||||||
|
};
|
||||||
|
|
||||||
|
const capture_max = std.math.maxInt(u8);
|
||||||
|
|
||||||
|
const declaration_max = std.math.maxInt(u8);
|
||||||
|
|
||||||
|
pub fn create_enclosed(self: *Environment, root: *Root) std.mem.Allocator.Error!*Environment {
|
||||||
|
const environment = try root.arena.allocator().create(Environment);
|
||||||
|
|
||||||
|
environment.* = .{.enclosing = self};
|
||||||
|
|
||||||
|
return environment;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare(self: *Environment, declaration: Declaration) DeclareError!*const Declaration {
|
||||||
|
if (self.declaration_count == self.declarations.len) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var environment = self;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
|
while (remaining_count != 0) {
|
||||||
|
remaining_count -= 1;
|
||||||
|
|
||||||
|
if (coral.are_equal(environment.declarations[remaining_count].identifier, declaration.identifier)) {
|
||||||
|
return error.DeclarationExists;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment = environment.enclosing orelse break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const declaration_slot = &self.declarations[self.declaration_count];
|
||||||
|
|
||||||
|
declaration_slot.* = declaration;
|
||||||
|
self.declaration_count += 1;
|
||||||
|
|
||||||
|
return declaration_slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_argument(self: *Environment, identifier: []const u8) DeclareError!*const Declaration {
|
||||||
|
std.debug.assert(self.declaration_count <= self.argument_count);
|
||||||
|
|
||||||
|
defer self.argument_count += 1;
|
||||||
|
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_constant(self: *Environment, identifier: []const u8) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{
|
||||||
|
.identifier = identifier,
|
||||||
|
.is = .{.readonly = true},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_variable(self: *Environment, identifier: []const u8) DeclareError!*const Declaration {
|
||||||
|
return self.declare(.{.identifier = identifier});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_declaration(self: *Environment, identifier: []const u8) std.mem.Allocator.Error!?*const Declaration {
|
||||||
|
var environment = self;
|
||||||
|
var ancestry = @as(u32, 0);
|
||||||
|
|
||||||
|
while (true) : (ancestry += 1) {
|
||||||
|
var remaining_count = environment.declaration_count;
|
||||||
|
|
||||||
|
while (remaining_count != 0) {
|
||||||
|
remaining_count -= 1;
|
||||||
|
|
||||||
|
const declaration = &environment.declarations[remaining_count];
|
||||||
|
|
||||||
|
if (coral.are_equal(declaration.identifier, identifier)) {
|
||||||
|
if (ancestry != 0) {
|
||||||
|
declaration.is.captured = true;
|
||||||
|
environment = self;
|
||||||
|
ancestry -= 1;
|
||||||
|
|
||||||
|
while (ancestry != 0) : (ancestry -= 1) {
|
||||||
|
if (environment.capture_count == environment.captures.len) {
|
||||||
|
return error.OutOfMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
environment.captures[environment.capture_count] = .{
|
||||||
|
.capture_index = environment.enclosing.?.capture_count
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.capture_count += 1;
|
||||||
|
environment = environment.enclosing.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
environment.captures[environment.capture_count] = .{.declaration_index = remaining_count};
|
||||||
|
environment.capture_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return declaration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
environment = environment.enclosing orelse return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_captures(self: *const Environment) []const Capture {
|
||||||
|
return self.captures[0 .. self.capture_count];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_declarations(self: *const Environment) []const Declaration {
|
||||||
|
return self.declarations[0 .. self.declaration_count];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn NodeChain(comptime Value: type) type {
|
||||||
|
return struct {
|
||||||
|
head: ?*Value = null,
|
||||||
|
tail: ?*Value = null,
|
||||||
|
|
||||||
|
pub const Nodes = struct {
|
||||||
|
current: ?*const Value,
|
||||||
|
|
||||||
|
pub fn next(self: *Nodes) ?*const Value {
|
||||||
|
const current = self.current orelse return null;
|
||||||
|
|
||||||
|
defer self.current = current.next;
|
||||||
|
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn append(self: *Self, value: *Value) void {
|
||||||
|
if (self.tail) |node| {
|
||||||
|
node.next = value;
|
||||||
|
self.tail = value;
|
||||||
|
} else {
|
||||||
|
self.tail = value;
|
||||||
|
self.head = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nodes(self: *const Self) Nodes {
|
||||||
|
return .{.current = self.head};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const ParseError = std.mem.Allocator.Error || error {
|
||||||
|
BadSyntax,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Root = struct {
|
||||||
|
arena: std.heap.ArenaAllocator,
|
||||||
|
environment: Environment,
|
||||||
|
error_messages: MessageList,
|
||||||
|
|
||||||
|
const MessageList = coral.Stack([]coral.Byte);
|
||||||
|
|
||||||
|
pub fn report_error(self: *Root, line: tokens.Line, comptime format: []const u8, args: anytype) ParseError {
|
||||||
|
const allocator = self.arena.allocator();
|
||||||
|
const message = try coral.utf8.alloc_formatted(allocator, format, args);
|
||||||
|
|
||||||
|
defer allocator.free(message);
|
||||||
|
|
||||||
|
try self.error_messages.push(try coral.utf8.alloc_formatted(allocator, "{line_number}: {message}", .{
|
||||||
|
.message = message,
|
||||||
|
.line_number = line.number,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return error.BadSyntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn report_declare_error(self: *Root, line: tokens.Line, identifier: []const u8, @"error": Environment.DeclareError) ParseError {
|
||||||
|
return switch (@"error") {
|
||||||
|
error.OutOfMemory => error.OutOfMemory,
|
||||||
|
|
||||||
|
error.DeclarationExists => self.report_error(line, "declaration `{identifier}` already exists", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_node(self: *Root, node: anytype) std.mem.Allocator.Error!*@TypeOf(node) {
|
||||||
|
const copy = try self.arena.allocator().create(@TypeOf(node));
|
||||||
|
|
||||||
|
copy.* = node;
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_string(self: *Root, comptime format: []const u8, args: anytype) std.mem.Allocator.Error![]const u8 {
|
||||||
|
return coral.utf8.alloc_formatted(self.arena.allocator(), format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *Root) void {
|
||||||
|
self.error_messages.deinit();
|
||||||
|
self.arena.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(allocator: std.mem.Allocator) std.mem.Allocator.Error!Root {
|
||||||
|
return .{
|
||||||
|
.arena = std.heap.ArenaAllocator.init(allocator),
|
||||||
|
.error_messages = .{.allocator = allocator},
|
||||||
|
.environment = .{},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(self: *Root, stream: *tokens.Stream) ParseError!void {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const first_statement = try Stmt.parse(self, stream, &self.environment);
|
||||||
|
var current_statement = first_statement;
|
||||||
|
|
||||||
|
while (stream.token != .end) {
|
||||||
|
const next_statement = try Stmt.parse(self, stream, &self.environment);
|
||||||
|
|
||||||
|
current_statement.next = next_statement;
|
||||||
|
current_statement = next_statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.environment.statement = first_statement;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,906 @@
|
||||||
|
const Stmt = @import("./Stmt.zig");
|
||||||
|
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const tokens = @import("../tokens.zig");
|
||||||
|
|
||||||
|
const tree = @import("../tree.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
next: ?*const Self = null,
|
||||||
|
line: tokens.Line,
|
||||||
|
kind: Kind,
|
||||||
|
|
||||||
|
pub const BinaryOp = struct {
|
||||||
|
rhs_operand: *Self,
|
||||||
|
lhs_operand: *Self,
|
||||||
|
operation: Operation,
|
||||||
|
|
||||||
|
pub const Operation = enum {
|
||||||
|
addition,
|
||||||
|
subtraction,
|
||||||
|
multiplication,
|
||||||
|
divsion,
|
||||||
|
equals_comparison,
|
||||||
|
greater_than_comparison,
|
||||||
|
greater_equals_comparison,
|
||||||
|
less_than_comparison,
|
||||||
|
less_equals_comparison,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn parser(comptime parse_next: Parser, comptime operations: []const BinaryOp.Operation) Parser {
|
||||||
|
const BinaryOpParser = struct {
|
||||||
|
fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
var expression = try parse_next(root, stream, environment);
|
||||||
|
|
||||||
|
inline for (operations) |operation| {
|
||||||
|
const token = comptime @as(tokens.Token, switch (operation) {
|
||||||
|
.addition => .symbol_plus,
|
||||||
|
.subtraction => .symbol_minus,
|
||||||
|
.multiplication => .symbol_asterisk,
|
||||||
|
.divsion => .symbol_forward_slash,
|
||||||
|
.equals_comparison => .symbol_double_equals,
|
||||||
|
.greater_than_comparison => .symbol_greater_than,
|
||||||
|
.greater_equals_comparison => .symbol_greater_equals,
|
||||||
|
.less_than_comparison => .symbol_less_than,
|
||||||
|
.less_equals_comparison => .symbol_less_equals,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stream.token == std.meta.activeTag(token)) {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
if (stream.token == .end) {
|
||||||
|
return root.report_error(stream.line, "expected other half of expression after `" ++ comptime token.text() ++ "`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove once Zig has fixed struct self-reassignment.
|
||||||
|
const unnecessary_temp = expression;
|
||||||
|
|
||||||
|
expression = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.binary_op = .{
|
||||||
|
.rhs_operand = try parse_next(root, stream, environment),
|
||||||
|
.operation = operation,
|
||||||
|
.lhs_operand = unnecessary_temp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return BinaryOpParser.parse;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeclarationGet = struct {
|
||||||
|
declaration: *const tree.Declaration,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DeclarationSet = struct {
|
||||||
|
declaration: *const tree.Declaration,
|
||||||
|
assign: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FieldGet = struct {
|
||||||
|
identifier: []const coral.Byte,
|
||||||
|
object: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const FieldSet = struct {
|
||||||
|
identifier: []const coral.Byte,
|
||||||
|
object: *const Self,
|
||||||
|
assign: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Invoke = struct {
|
||||||
|
argument: ?*const Self,
|
||||||
|
object: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Kind = union (enum) {
|
||||||
|
nil_literal,
|
||||||
|
true_literal,
|
||||||
|
false_literal,
|
||||||
|
number_literal: []const u8,
|
||||||
|
string_literal: []const u8,
|
||||||
|
string_template,
|
||||||
|
symbol_literal: []const u8,
|
||||||
|
vector2: Vector2,
|
||||||
|
vector3: Vector3,
|
||||||
|
table: tree.NodeChain(TableEntry),
|
||||||
|
group: *Self,
|
||||||
|
lambda_construct: LambdaConstruct,
|
||||||
|
declaration_get: DeclarationGet,
|
||||||
|
declaration_set: DeclarationSet,
|
||||||
|
field_get: FieldGet,
|
||||||
|
field_set: FieldSet,
|
||||||
|
external_get: ExternalGet,
|
||||||
|
subscript_get: SubscriptGet,
|
||||||
|
subscript_set: SubscriptSet,
|
||||||
|
binary_op: BinaryOp,
|
||||||
|
unary_op: UnaryOp,
|
||||||
|
invoke: Invoke,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const LambdaConstruct = struct {
|
||||||
|
environment: *const tree.Environment,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Parser = fn (root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self;
|
||||||
|
|
||||||
|
const ExternalGet = struct {
|
||||||
|
name: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub const SubscriptGet = struct {
|
||||||
|
index: *const Self,
|
||||||
|
object: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SubscriptSet = struct {
|
||||||
|
index: *const Self,
|
||||||
|
object: *const Self,
|
||||||
|
assign: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TableEntry = struct {
|
||||||
|
next: ?*const TableEntry = null,
|
||||||
|
key: *const Self,
|
||||||
|
value: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
const TemplateToken = union (enum) {
|
||||||
|
invalid: []const coral.Byte,
|
||||||
|
literal: []const coral.Byte,
|
||||||
|
expression: []const coral.Byte,
|
||||||
|
|
||||||
|
fn extract(source: *[]const coral.Byte) ?TemplateToken {
|
||||||
|
var cursor = @as(usize, 0);
|
||||||
|
|
||||||
|
defer source.* = source.*[cursor ..];
|
||||||
|
|
||||||
|
while (cursor < source.len) {
|
||||||
|
switch (source.*[cursor]) {
|
||||||
|
'{' => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (true) : (cursor += 1) {
|
||||||
|
if (cursor == source.len) {
|
||||||
|
return .{.invalid = source.*[0 .. cursor]};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.*[cursor] == '}') {
|
||||||
|
const token = TemplateToken{.expression = source.*[1 .. cursor]};
|
||||||
|
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
cursor += 1;
|
||||||
|
|
||||||
|
while (true) : (cursor += 1) {
|
||||||
|
if (cursor == source.len) {
|
||||||
|
return .{.literal = source.*[0 .. cursor]};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.*[cursor] == '{') {
|
||||||
|
const cursor_next = cursor + 1;
|
||||||
|
|
||||||
|
if (cursor_next == source.len) {
|
||||||
|
return .{.invalid = source.*[0 .. cursor]};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.*[cursor_next] == '{') {
|
||||||
|
cursor = cursor_next;
|
||||||
|
|
||||||
|
return .{.literal = source.*[0 .. cursor]};
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{.literal = source.*[0 .. cursor]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const UnaryOp = struct {
|
||||||
|
operand: *Self,
|
||||||
|
operation: Operation,
|
||||||
|
|
||||||
|
pub const Operation = enum {
|
||||||
|
numeric_negation,
|
||||||
|
boolean_negation,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vector2 = struct {
|
||||||
|
x: *const Self,
|
||||||
|
y: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Vector3 = struct {
|
||||||
|
x: *const Self,
|
||||||
|
y: *const Self,
|
||||||
|
z: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
const expression = try parse_additive(root, stream, environment);
|
||||||
|
|
||||||
|
if (stream.token == .symbol_equals) {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token == .end) {
|
||||||
|
return root.report_error(stream.line, "expected assignment after `=`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = switch (expression.kind) {
|
||||||
|
.declaration_get => |declaration_get| convert: {
|
||||||
|
if (declaration_get.declaration.is.readonly) {
|
||||||
|
return root.report_error(stream.line, "readonly declarations cannot be re-assigned", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
break: convert .{
|
||||||
|
.declaration_set = .{
|
||||||
|
.assign = try parse(root, stream, environment),
|
||||||
|
.declaration = declaration_get.declaration,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
.field_get => |field_get| .{
|
||||||
|
.field_set = .{
|
||||||
|
.assign = try parse(root, stream, environment),
|
||||||
|
.object = field_get.object,
|
||||||
|
.identifier = field_get.identifier,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
.subscript_get => |subscript_get| .{
|
||||||
|
.subscript_set = .{
|
||||||
|
.assign = try parse(root, stream, environment),
|
||||||
|
.object = subscript_get.object,
|
||||||
|
.index = subscript_get.index,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return root.report_error(stream.line, "expected local or field on left-hand side of expression", .{}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parse_additive = BinaryOp.parser(parse_equality, &.{
|
||||||
|
.addition,
|
||||||
|
.subtraction,
|
||||||
|
});
|
||||||
|
|
||||||
|
const parse_comparison = BinaryOp.parser(parse_term, &.{
|
||||||
|
.greater_than_comparison,
|
||||||
|
.greater_equals_comparison,
|
||||||
|
.less_than_comparison,
|
||||||
|
.less_equals_comparison
|
||||||
|
});
|
||||||
|
|
||||||
|
const parse_equality = BinaryOp.parser(parse_comparison, &.{
|
||||||
|
.equals_comparison,
|
||||||
|
});
|
||||||
|
|
||||||
|
fn parse_factor(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
var expression = try parse_operand(root, stream, environment);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_period => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
// TODO: Remove when Zig fixes miscompilation with in-place struct re-assignment.
|
||||||
|
const unnecessary_temp = expression;
|
||||||
|
|
||||||
|
expression = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.field_get = .{
|
||||||
|
.identifier = switch (stream.token) {
|
||||||
|
.identifier => |field_identifier| field_identifier,
|
||||||
|
else => return root.report_error(stream.line, "expected identifier after `.`", .{}),
|
||||||
|
},
|
||||||
|
|
||||||
|
.object = unnecessary_temp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_bracket_left => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
// TODO: Remove when Zig fixes miscompilation with in-place struct re-assignment.
|
||||||
|
const unnecessary_temp = expression;
|
||||||
|
|
||||||
|
expression = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.subscript_get = .{
|
||||||
|
.index = try parse(root, stream, environment),
|
||||||
|
.object = unnecessary_temp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (stream.token != .symbol_bracket_right) {
|
||||||
|
return root.report_error(stream.line, "expected closing `]` on subscript", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_paren_left => {
|
||||||
|
const lines_stepped = stream.line;
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
var first_argument = @as(?*Self, null);
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_right) {
|
||||||
|
var argument = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
first_argument = argument;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_comma => stream.skip_newlines(),
|
||||||
|
.symbol_paren_right => break,
|
||||||
|
else => return root.report_error(stream.line, "expected `,` or `)` after lambda argument", .{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const next_argument = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
argument.next = next_argument;
|
||||||
|
argument = next_argument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
// TODO: Remove when Zig fixes miscompilation with in-place struct re-assignment.
|
||||||
|
const unnecessary_temp = expression;
|
||||||
|
|
||||||
|
expression = try root.create_node(Self{
|
||||||
|
.line = lines_stepped,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.invoke = .{
|
||||||
|
.argument = first_argument,
|
||||||
|
.object = unnecessary_temp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_operand(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_paren_left => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_right) {
|
||||||
|
return root.report_error(stream.line, "expected a closing `)` after expression", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.group = expression},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_nil => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .nil_literal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_true => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .true_literal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_false => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .false_literal,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_vec2 => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_left) {
|
||||||
|
return root.report_error(stream.line, "expected an opening `(` after `vec2`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const x_expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_paren_right => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.vector2 = .{
|
||||||
|
.x = x_expression,
|
||||||
|
.y = x_expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_comma => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const y_expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_right) {
|
||||||
|
return root.report_error(stream.line, "expected a closing `)` after `vec3`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.vector2 = .{
|
||||||
|
.x = x_expression,
|
||||||
|
.y = y_expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return root.report_error(stream.line, "expected a closing `)` after `vec3`", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_vec3 => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_left) {
|
||||||
|
return root.report_error(stream.line, "expected an opening `(` after `vec2`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const x_expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_paren_right => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.vector3 = .{
|
||||||
|
.x = x_expression,
|
||||||
|
.y = x_expression,
|
||||||
|
.z = x_expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_comma => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const y_expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const z_expression = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_right) {
|
||||||
|
return root.report_error(stream.line, "expected a closing `)` after `vec3`", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.vector3 = .{
|
||||||
|
.x = x_expression,
|
||||||
|
.y = y_expression,
|
||||||
|
.z = z_expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return root.report_error(stream.line, "expected a closing `)` after `vec3`", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
.number => |value| {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.number_literal = value},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.string => |value| {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.string_literal = value},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.template_string => |value| {
|
||||||
|
const line = stream.line;
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return parse_template(root, value, line, environment);
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_at => {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
const identifier = switch (stream.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream.line, "expected identifier after `@`", .{}),
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.external_get = .{.name = identifier}},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_period => {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
const identifier = switch (stream.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream.line, "expected identifier after `.`", .{}),
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.symbol_literal = identifier},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.identifier => |identifier| {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.declaration_get = .{
|
||||||
|
.declaration = (try environment.resolve_declaration(identifier)) orelse {
|
||||||
|
return root.report_error(stream.line, "undefined identifier `{identifier}`", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_lambda => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_paren_left) {
|
||||||
|
return root.report_error(stream.line, "expected `(` after opening lambda block", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
var lambda_environment = try environment.create_enclosed(root);
|
||||||
|
|
||||||
|
while (stream.token != .symbol_paren_right) {
|
||||||
|
const identifier = switch (stream.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream.line, "expected identifier", .{}),
|
||||||
|
};
|
||||||
|
|
||||||
|
_ = lambda_environment.declare_argument(identifier) catch |declare_error| {
|
||||||
|
return root.report_declare_error(stream.line, identifier, declare_error);
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_comma => stream.skip_newlines(),
|
||||||
|
.symbol_paren_right => break,
|
||||||
|
else => return root.report_error(stream.line, "expected `,` or `)` after identifier", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_colon) {
|
||||||
|
return root.report_error(stream.line, "expected `:` after closing `)` of lambda identifiers", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .keyword_end) {
|
||||||
|
const first_statement = try Stmt.parse(root, stream, lambda_environment);
|
||||||
|
var current_statement = first_statement;
|
||||||
|
|
||||||
|
while (stream.token != .keyword_end) {
|
||||||
|
const next_statement = try Stmt.parse(root, stream, lambda_environment);
|
||||||
|
|
||||||
|
current_statement.next = next_statement;
|
||||||
|
current_statement = next_statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
lambda_environment.statement = first_statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.lambda_construct = .{.environment = lambda_environment}},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_brace_left => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return parse_table(root, stream, environment);
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_minus => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.unary_op = .{
|
||||||
|
.operand = try parse_factor(root, stream, environment),
|
||||||
|
.operation = .numeric_negation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_bang => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.unary_op = .{
|
||||||
|
.operand = try parse_factor(root, stream, environment),
|
||||||
|
.operation = .boolean_negation,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return root.report_error(stream.line, "unexpected token in expression", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_table(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
var entries = tree.NodeChain(TableEntry){};
|
||||||
|
var sequential_index = @as(usize, 0);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_brace_right => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.table = entries},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_bracket_left => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const key = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
if (stream.token != .symbol_bracket_right) {
|
||||||
|
return root.report_error(stream.line, "expected `]` after subscript index expression", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_equals) {
|
||||||
|
return root.report_error(stream.line, "expected `=` after table expression key", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
entries.append(try root.create_node(TableEntry{
|
||||||
|
.value = try parse(root, stream, environment),
|
||||||
|
.key = key,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_period => {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
const field = switch (stream.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream.line, "invalid symbol literal", .{}),
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_comma => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
entries.append(try root.create_node(TableEntry{
|
||||||
|
.key = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.number_literal = try root.create_string("{i}", .{.i = sequential_index})},
|
||||||
|
}),
|
||||||
|
|
||||||
|
.value = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.symbol_literal = field},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
sequential_index += 1;
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_equals => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
entries.append(try root.create_node(TableEntry{
|
||||||
|
.value = try parse(root, stream, environment),
|
||||||
|
|
||||||
|
.key = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.symbol_literal = field},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
else => return root.report_error(stream.line, "expected `,` or `=` after symbol", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
entries.append(try root.create_node(TableEntry{
|
||||||
|
.value = try parse(root, stream, environment),
|
||||||
|
|
||||||
|
.key = try root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.number_literal = try root.create_string("{i}", .{.i = sequential_index})},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
sequential_index += 1;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (stream.token) {
|
||||||
|
.symbol_brace_right => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.table = entries},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.symbol_comma => stream.skip_newlines(),
|
||||||
|
else => return root.report_error(stream.line, "expected `,` or '}' after table key value pair", .{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_template(root: *tree.Root, template: []const coral.Byte, line: tokens.Line, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
const expression_head = try root.create_node(Self{
|
||||||
|
.line = line,
|
||||||
|
.kind = .string_template,
|
||||||
|
});
|
||||||
|
|
||||||
|
var expression_tail = expression_head;
|
||||||
|
var source = template;
|
||||||
|
|
||||||
|
while (TemplateToken.extract(&source)) |token| {
|
||||||
|
const expression = try switch (token) {
|
||||||
|
.invalid => |invalid| root.report_error(line, "invalid template format: `{invalid}`", .{
|
||||||
|
.invalid = invalid,
|
||||||
|
}),
|
||||||
|
|
||||||
|
.literal => |literal| root.create_node(Self{
|
||||||
|
.line = line,
|
||||||
|
.kind = .{.string_literal = literal},
|
||||||
|
}),
|
||||||
|
|
||||||
|
.expression => |expression| create: {
|
||||||
|
var stream = tokens.Stream{
|
||||||
|
.source = expression,
|
||||||
|
.line = line,
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
break: create try parse(root, &stream, environment);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expression_tail.next = expression;
|
||||||
|
expression_tail = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parse_term = BinaryOp.parser(parse_factor, &.{
|
||||||
|
.multiplication,
|
||||||
|
.divsion,
|
||||||
|
});
|
|
@ -0,0 +1,242 @@
|
||||||
|
const Expr = @import("./Expr.zig");
|
||||||
|
|
||||||
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const tokens = @import("../tokens.zig");
|
||||||
|
|
||||||
|
const tree = @import("../tree.zig");
|
||||||
|
|
||||||
|
next: ?*const Self = null,
|
||||||
|
line: tokens.Line,
|
||||||
|
|
||||||
|
kind: union (enum) {
|
||||||
|
top_expression: *const Expr,
|
||||||
|
@"return": Return,
|
||||||
|
declare: Declare,
|
||||||
|
@"if": If,
|
||||||
|
@"while": While,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub const Declare = struct {
|
||||||
|
declaration: *const tree.Declaration,
|
||||||
|
initial_expression: *const Expr,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const If = struct {
|
||||||
|
then_expression: *const Expr,
|
||||||
|
@"then": *const Self,
|
||||||
|
@"else": ?*const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Return = struct {
|
||||||
|
returned_expression: ?*const Expr,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub const While = struct {
|
||||||
|
loop_expression: *const Expr,
|
||||||
|
loop: *const Self,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
switch (stream.token) {
|
||||||
|
.keyword_return => {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
if (stream.token != .end and stream.token != .newline) {
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.@"return" = .{.returned_expression = try Expr.parse(root, stream, environment)}},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream.token != .end and stream.token != .newline) {
|
||||||
|
return root.report_error(stream.line, "expected end or newline after return statement", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.@"return" = .{.returned_expression = null}},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_while => {
|
||||||
|
defer stream.skip_newlines();
|
||||||
|
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
const condition_expression = try Expr.parse(root, stream, environment);
|
||||||
|
|
||||||
|
if (stream.token != .symbol_colon) {
|
||||||
|
return root.report_error(stream.line, "expected `:` after `while` statement", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const first_statement = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
{
|
||||||
|
var current_statement = first_statement;
|
||||||
|
|
||||||
|
while (stream.token != .keyword_end) {
|
||||||
|
const next_statement = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
current_statement.next = next_statement;
|
||||||
|
current_statement = next_statement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.@"while" = .{
|
||||||
|
.loop = first_statement,
|
||||||
|
.loop_expression = condition_expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_var, .keyword_let => {
|
||||||
|
const is_constant = stream.token == .keyword_let;
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const identifier = switch (stream.token) {
|
||||||
|
.identifier => |identifier| identifier,
|
||||||
|
else => return root.report_error(stream.line, "expected identifier after declaration", .{}),
|
||||||
|
};
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_equals) {
|
||||||
|
return root.report_error(stream.line, "expected `=` after declaration `{identifier}`", .{
|
||||||
|
.identifier = identifier,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.declare = .{
|
||||||
|
.initial_expression = try Expr.parse(root, stream, environment),
|
||||||
|
|
||||||
|
.declaration = declare: {
|
||||||
|
if (is_constant) {
|
||||||
|
break: declare environment.declare_constant(identifier) catch |declaration_error| {
|
||||||
|
return root.report_declare_error(stream.line, identifier, declaration_error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break: declare environment.declare_variable(identifier) catch |declaration_error| {
|
||||||
|
return root.report_declare_error(stream.line, identifier, declaration_error);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_if => return parse_branch(root, stream, environment),
|
||||||
|
|
||||||
|
else => return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
.kind = .{.top_expression = try Expr.parse(root, stream, environment)},
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_branch(root: *tree.Root, stream: *tokens.Stream, environment: *tree.Environment) tree.ParseError!*Self {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
const expression = try Expr.parse(root, stream, environment);
|
||||||
|
|
||||||
|
if (stream.token != .symbol_colon) {
|
||||||
|
return root.report_error(stream.line, "expected `:` after `{token}`", .{.token = stream.token.text()});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const first_then_statement = try parse(root, stream, environment);
|
||||||
|
var current_then_statement = first_then_statement;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (stream.token) {
|
||||||
|
.keyword_end => {
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.@"if" = .{
|
||||||
|
.then_expression = expression,
|
||||||
|
.@"then" = first_then_statement,
|
||||||
|
.@"else" = null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_else => {
|
||||||
|
stream.step();
|
||||||
|
|
||||||
|
if (stream.token != .symbol_colon) {
|
||||||
|
return root.report_error(stream.line, "expected `:` after `if` statement condition", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
const first_else_statement = try parse(root, stream, environment);
|
||||||
|
var current_else_statement = first_else_statement;
|
||||||
|
|
||||||
|
while (stream.token != .keyword_end) {
|
||||||
|
const next_statement = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
current_else_statement.next = next_statement;
|
||||||
|
current_else_statement = next_statement;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.skip_newlines();
|
||||||
|
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.@"if" = .{
|
||||||
|
.@"else" = first_else_statement,
|
||||||
|
.@"then" = first_then_statement,
|
||||||
|
.then_expression = expression,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
.keyword_elif => {
|
||||||
|
return root.create_node(Self{
|
||||||
|
.line = stream.line,
|
||||||
|
|
||||||
|
.kind = .{
|
||||||
|
.@"if" = .{
|
||||||
|
.@"else" = try parse_branch(root, stream, environment),
|
||||||
|
.@"then" = first_then_statement,
|
||||||
|
.then_expression = expression,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
const next_statement = try parse(root, stream, environment);
|
||||||
|
|
||||||
|
current_then_statement.next = next_statement;
|
||||||
|
current_then_statement = next_statement;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
const slices = @import("./slices.zig");
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn Parallel(comptime Value: type) type {
|
||||||
|
const Slices = slices.Parallel(Value);
|
||||||
|
const alignment = @alignOf(Value);
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
slices: slices.Parallel(Value) = .{},
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn len(self: Self) usize {
|
||||||
|
return self.slices.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn values(self: *Self, comptime field: Slices.Field) []align (alignment) Slices.Element(field) {
|
||||||
|
return self.slices.slice(field);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
const coral = @import("coral");
|
|
||||||
|
|
||||||
const dag = @import("./dag.zig");
|
const dag = @import("./dag.zig");
|
||||||
|
|
||||||
const states = @import("./states.zig");
|
const heap = @import("./heap.zig");
|
||||||
|
|
||||||
|
const map = @import("./map.zig");
|
||||||
|
|
||||||
|
const resource = @import("./resource.zig");
|
||||||
|
|
||||||
|
const slices = @import("./slices.zig");
|
||||||
|
|
||||||
|
const stack = @import("./stack.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
@ -15,7 +21,7 @@ pub const BindContext = struct {
|
||||||
|
|
||||||
pub const ResourceAccess = std.meta.Tag(Schedule.ResourceAccess);
|
pub const ResourceAccess = std.meta.Tag(Schedule.ResourceAccess);
|
||||||
|
|
||||||
pub fn accesses_resource(self: BindContext, access: ResourceAccess, id: states.TypeID) bool {
|
pub fn accesses_resource(self: BindContext, access: ResourceAccess, id: resource.TypeID) bool {
|
||||||
const resource_accesses = &self.systems.graph.get_ptr(self.node).?.resource_accesses;
|
const resource_accesses = &self.systems.graph.get_ptr(self.node).?.resource_accesses;
|
||||||
|
|
||||||
for (resource_accesses.values) |resource_access| {
|
for (resource_accesses.values) |resource_access| {
|
||||||
|
@ -37,12 +43,12 @@ pub const BindContext = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_writable_resource_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*Resource {
|
pub fn register_read_write_resource_access(self: BindContext, thread_restriction: World.ThreadRestriction, comptime Resource: type) std.mem.Allocator.Error!?*Resource {
|
||||||
const value = self.world.get_resource(Resource) orelse {
|
const value = self.world.get_resource(thread_restriction, Resource) orelse {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const id = states.type_id(Resource);
|
const id = resource.type_id(Resource);
|
||||||
|
|
||||||
if (!self.accesses_resource(.read_write, id)) {
|
if (!self.accesses_resource(.read_write, id)) {
|
||||||
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_write = id});
|
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_write = id});
|
||||||
|
@ -51,26 +57,26 @@ pub const BindContext = struct {
|
||||||
const read_write_resource_nodes = lazily_create: {
|
const read_write_resource_nodes = lazily_create: {
|
||||||
break: lazily_create self.systems.read_write_resource_id_nodes.get_ptr(id) orelse insert: {
|
break: lazily_create self.systems.read_write_resource_id_nodes.get_ptr(id) orelse insert: {
|
||||||
std.debug.assert(try self.systems.read_write_resource_id_nodes.emplace(id, .{
|
std.debug.assert(try self.systems.read_write_resource_id_nodes.emplace(id, .{
|
||||||
.allocator = coral.heap.allocator,
|
.allocator = heap.allocator,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
break: insert self.systems.read_write_resource_id_nodes.get_ptr(id).?;
|
break: insert self.systems.read_write_resource_id_nodes.get_ptr(id).?;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (coral.slices.index_of(read_write_resource_nodes.values, 0, self.node) == null) {
|
if (slices.index_of(read_write_resource_nodes.values, 0, self.node) == null) {
|
||||||
try read_write_resource_nodes.push_grow(self.node);
|
try read_write_resource_nodes.push_grow(self.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_readable_resource_access(self: BindContext, comptime Resource: type) std.mem.Allocator.Error!?*const Resource {
|
pub fn register_read_only_resource_access(self: BindContext, thread_restriction: World.ThreadRestriction, comptime Resource: type) std.mem.Allocator.Error!?*const Resource {
|
||||||
const value = self.world.get_resource(Resource) orelse {
|
const value = self.world.get_resource(thread_restriction, Resource) orelse {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const id = states.type_id(Resource);
|
const id = resource.type_id(Resource);
|
||||||
|
|
||||||
if (!self.accesses_resource(.read_only, id)) {
|
if (!self.accesses_resource(.read_only, id)) {
|
||||||
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_only = id});
|
try self.systems.graph.get_ptr(self.node).?.resource_accesses.push_grow(.{.read_only = id});
|
||||||
|
@ -79,14 +85,14 @@ pub const BindContext = struct {
|
||||||
const read_only_resource_nodes = lazily_create: {
|
const read_only_resource_nodes = lazily_create: {
|
||||||
break: lazily_create self.systems.read_only_resource_id_nodes.get_ptr(id) orelse insert: {
|
break: lazily_create self.systems.read_only_resource_id_nodes.get_ptr(id) orelse insert: {
|
||||||
std.debug.assert(try self.systems.read_only_resource_id_nodes.emplace(id, .{
|
std.debug.assert(try self.systems.read_only_resource_id_nodes.emplace(id, .{
|
||||||
.allocator = coral.heap.allocator,
|
.allocator = heap.allocator,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
break: insert self.systems.read_only_resource_id_nodes.get_ptr(id).?;
|
break: insert self.systems.read_only_resource_id_nodes.get_ptr(id).?;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
if (coral.slices.index_of(read_only_resource_nodes.values, 0, self.node) == null) {
|
if (slices.index_of(read_only_resource_nodes.values, 0, self.node) == null) {
|
||||||
try read_only_resource_nodes.push_grow(self.node);
|
try read_only_resource_nodes.push_grow(self.node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +104,10 @@ pub const Info = struct {
|
||||||
execute: *const fn ([]const *const Parameter, *const [max_parameters]?*anyopaque) anyerror!void,
|
execute: *const fn ([]const *const Parameter, *const [max_parameters]?*anyopaque) anyerror!void,
|
||||||
parameters: [max_parameters]*const Parameter = undefined,
|
parameters: [max_parameters]*const Parameter = undefined,
|
||||||
parameter_count: u4 = 0,
|
parameter_count: u4 = 0,
|
||||||
thread_restriction: states.ThreadRestriction = .none,
|
thread_restriction: World.ThreadRestriction = .none,
|
||||||
|
|
||||||
pub const Parameter = struct {
|
pub const Parameter = struct {
|
||||||
thread_restriction: states.ThreadRestriction,
|
thread_restriction: World.ThreadRestriction,
|
||||||
init: *const fn (*anyopaque, ?*anyopaque) void,
|
init: *const fn (*anyopaque, ?*anyopaque) void,
|
||||||
bind: *const fn (std.mem.Allocator, BindContext) std.mem.Allocator.Error!?*anyopaque,
|
bind: *const fn (std.mem.Allocator, BindContext) std.mem.Allocator.Error!?*anyopaque,
|
||||||
unbind: *const fn (std.mem.Allocator, ?*anyopaque) void,
|
unbind: *const fn (std.mem.Allocator, ?*anyopaque) void,
|
||||||
|
@ -122,7 +128,7 @@ pub const Schedule = struct {
|
||||||
label: [:0]const u8,
|
label: [:0]const u8,
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
system_id_nodes: coral.map.Hashed(usize, NodeBundle, coral.map.usize_traits),
|
system_id_nodes: map.Hashed(usize, NodeBundle, map.usize_traits),
|
||||||
read_write_resource_id_nodes: ResourceNodeBundle,
|
read_write_resource_id_nodes: ResourceNodeBundle,
|
||||||
read_only_resource_id_nodes: ResourceNodeBundle,
|
read_only_resource_id_nodes: ResourceNodeBundle,
|
||||||
parallel_work_bundles: ParallelNodeBundles,
|
parallel_work_bundles: ParallelNodeBundles,
|
||||||
|
@ -143,19 +149,19 @@ pub const Schedule = struct {
|
||||||
label: [:0]u8,
|
label: [:0]u8,
|
||||||
dependencies: []Dependency,
|
dependencies: []Dependency,
|
||||||
parameter_states: [max_parameters]?*anyopaque = [_]?*anyopaque{null} ** max_parameters,
|
parameter_states: [max_parameters]?*anyopaque = [_]?*anyopaque{null} ** max_parameters,
|
||||||
resource_accesses: coral.stack.Sequential(ResourceAccess),
|
resource_accesses: stack.Sequential(ResourceAccess),
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeBundle = coral.stack.Sequential(dag.Node);
|
const NodeBundle = stack.Sequential(dag.Node);
|
||||||
|
|
||||||
const ParallelNodeBundles = coral.stack.Sequential(NodeBundle);
|
const ParallelNodeBundles = stack.Sequential(NodeBundle);
|
||||||
|
|
||||||
const ResourceAccess = union (enum) {
|
const ResourceAccess = union (enum) {
|
||||||
read_only: states.TypeID,
|
read_only: resource.TypeID,
|
||||||
read_write: states.TypeID,
|
read_write: resource.TypeID,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResourceNodeBundle = coral.map.Hashed(states.TypeID, NodeBundle, coral.map.enum_traits(states.TypeID));
|
const ResourceNodeBundle = map.Hashed(resource.TypeID, NodeBundle, map.enum_traits(resource.TypeID));
|
||||||
|
|
||||||
pub fn deinit(self: *Schedule) void {
|
pub fn deinit(self: *Schedule) void {
|
||||||
{
|
{
|
||||||
|
@ -192,8 +198,8 @@ pub const Schedule = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
system.resource_accesses.deinit();
|
system.resource_accesses.deinit();
|
||||||
coral.heap.allocator.free(system.dependencies);
|
heap.allocator.free(system.dependencies);
|
||||||
coral.heap.allocator.free(system.label);
|
heap.allocator.free(system.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (self.parallel_work_bundles.values) |*bundle| {
|
for (self.parallel_work_bundles.values) |*bundle| {
|
||||||
|
@ -298,7 +304,7 @@ pub const Schedule = struct {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try schedule.parallel_work_bundles.push_grow(.{.allocator = coral.heap.allocator});
|
try schedule.parallel_work_bundles.push_grow(.{.allocator = heap.allocator});
|
||||||
|
|
||||||
const bundle = schedule.parallel_work_bundles.get_ptr().?;
|
const bundle = schedule.parallel_work_bundles.get_ptr().?;
|
||||||
|
|
||||||
|
@ -379,21 +385,21 @@ pub const Schedule = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(label: []const u8) std.mem.Allocator.Error!Schedule {
|
pub fn init(label: []const u8) std.mem.Allocator.Error!Schedule {
|
||||||
var arena = std.heap.ArenaAllocator.init(coral.heap.allocator);
|
var arena = std.heap.ArenaAllocator.init(heap.allocator);
|
||||||
|
|
||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
|
|
||||||
const duped_label = try arena.allocator().dupeZ(u8, label);
|
const duped_label = try arena.allocator().dupeZ(u8, label);
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.graph = Graph.init(coral.heap.allocator),
|
.graph = Graph.init(heap.allocator),
|
||||||
.label = duped_label,
|
.label = duped_label,
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.system_id_nodes = .{.allocator = coral.heap.allocator},
|
.system_id_nodes = .{.allocator = heap.allocator},
|
||||||
.read_write_resource_id_nodes = .{.allocator = coral.heap.allocator},
|
.read_write_resource_id_nodes = .{.allocator = heap.allocator},
|
||||||
.read_only_resource_id_nodes = .{.allocator = coral.heap.allocator},
|
.read_only_resource_id_nodes = .{.allocator = heap.allocator},
|
||||||
.parallel_work_bundles = .{.allocator = coral.heap.allocator},
|
.parallel_work_bundles = .{.allocator = heap.allocator},
|
||||||
.blocking_work = .{.allocator = coral.heap.allocator},
|
.blocking_work = .{.allocator = heap.allocator},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,7 +432,7 @@ pub const Schedule = struct {
|
||||||
|
|
||||||
const dependencies = init: {
|
const dependencies = init: {
|
||||||
const total_run_orders = order.run_after.len + order.run_before.len;
|
const total_run_orders = order.run_after.len + order.run_before.len;
|
||||||
const dependencies = try coral.heap.allocator.alloc(Dependency, total_run_orders);
|
const dependencies = try heap.allocator.alloc(Dependency, total_run_orders);
|
||||||
var dependencies_written = @as(usize, 0);
|
var dependencies_written = @as(usize, 0);
|
||||||
|
|
||||||
for (order.run_after) |after_system| {
|
for (order.run_after) |after_system| {
|
||||||
|
@ -450,17 +456,17 @@ pub const Schedule = struct {
|
||||||
break: init dependencies;
|
break: init dependencies;
|
||||||
};
|
};
|
||||||
|
|
||||||
errdefer coral.heap.allocator.free(dependencies);
|
errdefer heap.allocator.free(dependencies);
|
||||||
|
|
||||||
const label = try coral.heap.allocator.dupeZ(u8, if (order.label.len == 0) "anonymous system" else order.label);
|
const label = try heap.allocator.dupeZ(u8, if (order.label.len == 0) "anonymous system" else order.label);
|
||||||
|
|
||||||
errdefer coral.heap.allocator.free(label);
|
errdefer heap.allocator.free(label);
|
||||||
|
|
||||||
const node = try self.graph.append(.{
|
const node = try self.graph.append(.{
|
||||||
.info = info,
|
.info = info,
|
||||||
.label = label,
|
.label = label,
|
||||||
.dependencies = dependencies,
|
.dependencies = dependencies,
|
||||||
.resource_accesses = .{.allocator = coral.heap.allocator},
|
.resource_accesses = .{.allocator = heap.allocator},
|
||||||
});
|
});
|
||||||
|
|
||||||
const system = self.graph.get_ptr(node).?;
|
const system = self.graph.get_ptr(node).?;
|
|
@ -19,54 +19,15 @@ pub fn alloc_formatted(allocator: std.mem.Allocator, comptime format: []const u8
|
||||||
return buffer.to_allocation(formatted_len, 0);
|
return buffer.to_allocation(formatted_len, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_formatted(comptime format: []const u8, args: anytype) usize {
|
fn count_formatted(comptime format: []const u8, args: anytype) usize {
|
||||||
var count = io.NullWritable{};
|
var count = io.defaultWritable{};
|
||||||
|
|
||||||
write_formatted(count.writer(), format, args) catch unreachable;
|
print_formatted(count.writer(), format, args) catch unreachable;
|
||||||
|
|
||||||
return count.written;
|
return count.written;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_formatted(buffer: [:0]coral.io.Byte, comptime format: []const u8, args: anytype) io.Error![:0]u8 {
|
pub fn print_formatted(writer: io.Writer, comptime format: []const u8, args: anytype) io.PrintError!void {
|
||||||
const Seekable = struct {
|
|
||||||
buffer: []coral.io.Byte,
|
|
||||||
cursor: usize,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
fn write(self: *Self, input: []const coral.io.Byte) io.Error!usize {
|
|
||||||
const range = @min(input.len, self.buffer.len - self.cursor);
|
|
||||||
const tail = self.cursor + range;
|
|
||||||
|
|
||||||
@memcpy(self.buffer[self.cursor .. tail], input);
|
|
||||||
|
|
||||||
self.cursor = tail;
|
|
||||||
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const len = count_formatted(format, args);
|
|
||||||
|
|
||||||
if (len > buffer.len) {
|
|
||||||
return error.UnavailableResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seekable = Seekable{
|
|
||||||
.buffer = buffer,
|
|
||||||
.cursor = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
try write_formatted(coral.io.Writer.bind(Seekable, &seekable, Seekable.write), format, args);
|
|
||||||
|
|
||||||
if (buffer.len < len) {
|
|
||||||
buffer[len] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer[0 .. len:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_formatted(writer: io.Writer, comptime format: []const u8, args: anytype) io.Error!void {
|
|
||||||
switch (@typeInfo(@TypeOf(args))) {
|
switch (@typeInfo(@TypeOf(args))) {
|
||||||
.Struct => |arguments_struct| {
|
.Struct => |arguments_struct| {
|
||||||
comptime var arg_index = 0;
|
comptime var arg_index = 0;
|
||||||
|
@ -106,7 +67,7 @@ pub fn write_formatted(writer: io.Writer, comptime format: []const u8, args: any
|
||||||
@compileError("format specifiers cannot be named when using a tuple struct");
|
@compileError("format specifiers cannot be named when using a tuple struct");
|
||||||
}
|
}
|
||||||
|
|
||||||
try io.write_all(writer, format[head .. (tail - 1)]);
|
try io.print(writer, format[head .. (tail - 1)]);
|
||||||
|
|
||||||
head = tail;
|
head = tail;
|
||||||
tail += 1;
|
tail += 1;
|
||||||
|
@ -132,14 +93,14 @@ pub fn write_formatted(writer: io.Writer, comptime format: []const u8, args: any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try io.write_all(writer, format[head .. ]);
|
try io.print(writer, format[head .. ]);
|
||||||
},
|
},
|
||||||
|
|
||||||
else => @compileError("`arguments` must be a struct type"),
|
else => @compileError("`arguments` must be a struct type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.Error!void {
|
noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.PrintError!void {
|
||||||
const Value = @TypeOf(value);
|
const Value = @TypeOf(value);
|
||||||
|
|
||||||
return switch (@typeInfo(Value)) {
|
return switch (@typeInfo(Value)) {
|
||||||
|
@ -148,9 +109,9 @@ noinline fn print_formatted_value(writer: io.Writer, value: anytype) io.Error!vo
|
||||||
.Enum => io.print(writer, @tagName(value)),
|
.Enum => io.print(writer, @tagName(value)),
|
||||||
|
|
||||||
.Pointer => |pointer| switch (pointer.size) {
|
.Pointer => |pointer| switch (pointer.size) {
|
||||||
.Many, .C => ascii.HexadecimalFormat.default.format(writer, @intFromPtr(value)),
|
.Many, .C => ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
||||||
.One => if (pointer.child == []const u8) io.write_all(writer, *value) else ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
.One => if (pointer.child == []const u8) io.print(writer, *value) else ascii.HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
||||||
.Slice => if (pointer.child == u8) io.write_all(writer, value) else @compileError(unformattableMessage(Value)),
|
.Slice => if (pointer.child == u8) io.print(writer, value) else @compileError(unformattableMessage(Value)),
|
||||||
},
|
},
|
||||||
|
|
||||||
else => @compileError(unformattableMessage(Value)),
|
else => @compileError(unformattableMessage(Value)),
|
||||||
|
|
|
@ -1,273 +0,0 @@
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const states = @import("./states.zig");
|
|
||||||
|
|
||||||
pub const system = @import("./system.zig");
|
|
||||||
|
|
||||||
pub const World = @import("./World.zig");
|
|
||||||
|
|
||||||
pub fn Read(comptime Value: type) type {
|
|
||||||
return Shared(Value, .{
|
|
||||||
.thread_restriction = states.thread_restriction(Value),
|
|
||||||
.read_only = true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const ShareInfo = struct {
|
|
||||||
thread_restriction: states.ThreadRestriction,
|
|
||||||
read_only: bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn Shared(comptime Value: type, comptime info: ShareInfo) type {
|
|
||||||
const value_info = @typeInfo(Value);
|
|
||||||
|
|
||||||
const Qualified = switch (value_info) {
|
|
||||||
.Optional => @Type(.{
|
|
||||||
.Optional = .{
|
|
||||||
.child = .{
|
|
||||||
.Pointer = .{
|
|
||||||
.is_allowzero = false,
|
|
||||||
.sentinel = null,
|
|
||||||
.address_space = .generic,
|
|
||||||
.is_volatile = false,
|
|
||||||
.alignment = @alignOf(Value),
|
|
||||||
.size = .One,
|
|
||||||
.child = Value,
|
|
||||||
.is_const = info.read_only,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
|
|
||||||
else => @Type(.{
|
|
||||||
.Pointer = .{
|
|
||||||
.is_allowzero = false,
|
|
||||||
.sentinel = null,
|
|
||||||
.address_space = .generic,
|
|
||||||
.is_volatile = false,
|
|
||||||
.alignment = @alignOf(Value),
|
|
||||||
.size = .One,
|
|
||||||
.child = Value,
|
|
||||||
.is_const = info.read_only,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return struct {
|
|
||||||
res: Qualified,
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const State = struct {
|
|
||||||
res: Qualified,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn bind(context: system.BindContext) std.mem.Allocator.Error!State {
|
|
||||||
const thread_restriction_name = switch (info.thread_restriction) {
|
|
||||||
.main => "main thread-restricted ",
|
|
||||||
.none => ""
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = switch (info.read_only) {
|
|
||||||
true => (try context.register_readable_resource_access(Value)),
|
|
||||||
false => (try context.register_writable_resource_access(Value)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.res = switch (value_info) {
|
|
||||||
.Optional => res,
|
|
||||||
|
|
||||||
else => res orelse {
|
|
||||||
@panic(std.fmt.comptimePrint("attempt to use {s}{s} {s} that has not yet been set", .{
|
|
||||||
thread_restriction_name,
|
|
||||||
if (info.read_only) "read-only" else "read-write",
|
|
||||||
@typeName(Value),
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init(state: *State) Self {
|
|
||||||
return .{
|
|
||||||
.res = state.res,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Write(comptime Value: type) type {
|
|
||||||
return Shared(Value, .{
|
|
||||||
.thread_restriction = states.thread_restriction(Value),
|
|
||||||
.read_only = false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parameter_type(comptime Value: type) *const system.Info.Parameter {
|
|
||||||
const has_state = @hasDecl(Value, "State");
|
|
||||||
|
|
||||||
if (@sizeOf(Value) == 0) {
|
|
||||||
@compileError("System parameters must have a non-zero size");
|
|
||||||
}
|
|
||||||
|
|
||||||
const parameters = struct {
|
|
||||||
fn bind(allocator: std.mem.Allocator, context: system.BindContext) std.mem.Allocator.Error!?*anyopaque {
|
|
||||||
if (has_state) {
|
|
||||||
const value_name = @typeName(Value);
|
|
||||||
|
|
||||||
if (!@hasDecl(Value, "bind")) {
|
|
||||||
@compileError(
|
|
||||||
"a `bind` declaration on " ++
|
|
||||||
value_name ++
|
|
||||||
" is requied for parameter types with a `State` declaration");
|
|
||||||
}
|
|
||||||
|
|
||||||
const bind_type = @typeInfo(@TypeOf(Value.bind));
|
|
||||||
|
|
||||||
if (bind_type != .Fn) {
|
|
||||||
@compileError("`bind` declaration on " ++ value_name ++ " must be a fn");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bind_type.Fn.params.len != 1 or bind_type.Fn.params[0].type.? != system.BindContext) {
|
|
||||||
@compileError(
|
|
||||||
"`bind` fn on " ++
|
|
||||||
value_name ++
|
|
||||||
" must accept " ++
|
|
||||||
@typeName(system.BindContext) ++
|
|
||||||
" as it's one and only argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = try allocator.create(Value.State);
|
|
||||||
|
|
||||||
state.* = switch (bind_type.Fn.return_type.?) {
|
|
||||||
Value.State => Value.bind(context),
|
|
||||||
std.mem.Allocator.Error!Value.State => try Value.bind(context),
|
|
||||||
else => @compileError(
|
|
||||||
"`bind` fn on " ++
|
|
||||||
@typeName(Value) ++
|
|
||||||
" must return " ++
|
|
||||||
@typeName(Value.State) ++
|
|
||||||
" or " ++
|
|
||||||
@typeName(std.mem.Allocator.Error!Value.State)),
|
|
||||||
};
|
|
||||||
|
|
||||||
return @ptrCast(state);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(argument: *anyopaque, state: ?*anyopaque) void {
|
|
||||||
const value_name = @typeName(Value);
|
|
||||||
|
|
||||||
if (!@hasDecl(Value, "init")) {
|
|
||||||
@compileError("an `init` declaration on " ++ value_name ++ " is requied for parameter types");
|
|
||||||
}
|
|
||||||
|
|
||||||
const init_type = @typeInfo(@TypeOf(Value.init));
|
|
||||||
|
|
||||||
if (init_type != .Fn) {
|
|
||||||
@compileError("`init` declaration on " ++ value_name ++ " must be a fn");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (init_type.Fn.return_type.? != Value) {
|
|
||||||
@compileError("`init` fn on " ++ value_name ++ " must return a " ++ value_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const concrete_argument = @as(*Value, @ptrCast(@alignCast(argument)));
|
|
||||||
|
|
||||||
if (has_state) {
|
|
||||||
if (init_type.Fn.params.len != 1 or init_type.Fn.params[0].type.? != *Value.State) {
|
|
||||||
@compileError("`init` fn on stateful " ++ value_name ++ " must accept a " ++ @typeName(*Value.State));
|
|
||||||
}
|
|
||||||
|
|
||||||
concrete_argument.* = Value.init(@ptrCast(@alignCast(state.?)));
|
|
||||||
} else {
|
|
||||||
if (init_type.Fn.params.len != 0) {
|
|
||||||
@compileError("`init` fn on statelss " ++ value_name ++ " cannot use parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
concrete_argument.* = Value.init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unbind(allocator: std.mem.Allocator, state: ?*anyopaque) void {
|
|
||||||
if (@hasDecl(Value, "unbind")) {
|
|
||||||
if (has_state) {
|
|
||||||
const typed_state = @as(*Value.State, @ptrCast(@alignCast(state.?)));
|
|
||||||
|
|
||||||
Value.unbind(typed_state);
|
|
||||||
allocator.destroy(typed_state);
|
|
||||||
} else {
|
|
||||||
Value.unbind();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return comptime &.{
|
|
||||||
.thread_restriction = if (@hasDecl(Value, "thread_restriction")) Value.thread_restriction else .none,
|
|
||||||
.init = parameters.init,
|
|
||||||
.bind = parameters.bind,
|
|
||||||
.unbind = parameters.unbind,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn system_fn(comptime call: anytype) *const system.Info {
|
|
||||||
const Call = @TypeOf(call);
|
|
||||||
|
|
||||||
const system_info = comptime generate: {
|
|
||||||
switch (@typeInfo(Call)) {
|
|
||||||
.Fn => |call_fn| {
|
|
||||||
if (call_fn.params.len > system.max_parameters) {
|
|
||||||
@compileError("number of parameters to `call` cannot be more than 16");
|
|
||||||
}
|
|
||||||
|
|
||||||
const systems = struct {
|
|
||||||
fn run(parameters: []const *const system.Info.Parameter, data: *const [system.max_parameters]?*anyopaque) anyerror!void {
|
|
||||||
var call_args = @as(std.meta.ArgsTuple(Call), undefined);
|
|
||||||
|
|
||||||
inline for (parameters, &call_args, data[0 .. parameters.len]) |parameter, *call_arg, state| {
|
|
||||||
parameter.init(call_arg, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (@typeInfo(call_fn.return_type.?)) {
|
|
||||||
.Void => @call(.auto, call, call_args),
|
|
||||||
.ErrorUnion => try @call(.auto, call, call_args),
|
|
||||||
else => @compileError("number of parameters to `call` must return void or !void"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var parameters = @as([system.max_parameters]*const system.Info.Parameter, undefined);
|
|
||||||
var thread_restriction = states.ThreadRestriction.none;
|
|
||||||
|
|
||||||
for (0 .. call_fn.params.len) |index| {
|
|
||||||
const CallParam = call_fn.params[index].type.?;
|
|
||||||
const parameter = parameter_type(CallParam);
|
|
||||||
|
|
||||||
if (parameter.thread_restriction != .none) {
|
|
||||||
if (thread_restriction != .none and thread_restriction != parameter.thread_restriction) {
|
|
||||||
@compileError("a system may not have conflicting thread restrictions");
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_restriction = parameter.thread_restriction;
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters[index] = parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
break: generate &.{
|
|
||||||
.parameters = parameters,
|
|
||||||
.parameter_count = call_fn.params.len,
|
|
||||||
.execute = systems.run,
|
|
||||||
.thread_restriction = thread_restriction,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
else => @compileError("parameter `call` must be a function"),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return system_info;
|
|
||||||
}
|
|
22
src/main.zig
22
src/main.zig
|
@ -23,7 +23,7 @@ pub fn main() !void {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(display: ona.Write(ona.gfx.Display), actors: ona.Write(Actors), assets: ona.Write(ona.gfx.Assets)) !void {
|
fn load(display: coral.Write(ona.gfx.Display), actors: coral.Write(Actors), assets: coral.Write(ona.gfx.Assets)) !void {
|
||||||
display.res.width, display.res.height = .{1280, 720};
|
display.res.width, display.res.height = .{1280, 720};
|
||||||
actors.res.body_texture = try assets.res.create_from_file(coral.files.bundle, "actor.bmp");
|
actors.res.body_texture = try assets.res.create_from_file(coral.files.bundle, "actor.bmp");
|
||||||
actors.res.quad_mesh_2d = try assets.res.create_quad_mesh_2d(@splat(1));
|
actors.res.quad_mesh_2d = try assets.res.create_quad_mesh_2d(@splat(1));
|
||||||
|
@ -44,11 +44,11 @@ fn load(display: ona.Write(ona.gfx.Display), actors: ona.Write(Actors), assets:
|
||||||
try actors.res.instances.push_grow(.{0, 0});
|
try actors.res.instances.push_grow(.{0, 0});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit(actors: ona.Write(Actors)) void {
|
fn exit(actors: coral.Write(Actors)) void {
|
||||||
actors.res.instances.deinit();
|
actors.res.instances.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(queue: ona.gfx.Queue, actors: ona.Write(Actors), display: ona.Read(ona.gfx.Display)) !void {
|
fn render(queue: ona.gfx.Queue, actors: coral.Write(Actors), display: coral.Read(ona.gfx.Display)) !void {
|
||||||
try queue.commands.append(.{.target = .{
|
try queue.commands.append(.{.target = .{
|
||||||
.texture = actors.res.render_texture.?,
|
.texture = actors.res.render_texture.?,
|
||||||
.clear_color = .{0, 0, 0, 0},
|
.clear_color = .{0, 0, 0, 0},
|
||||||
|
@ -85,19 +85,19 @@ fn render(queue: ona.gfx.Queue, actors: ona.Write(Actors), display: ona.Read(ona
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(player: ona.Read(Player), actors: ona.Write(Actors), mapping: ona.Read(ona.act.Mapping)) !void {
|
fn update(player: coral.Read(Player), actors: coral.Write(Actors), mapping: coral.Read(ona.act.Mapping)) !void {
|
||||||
actors.res.instances.values[0] += .{
|
actors.res.instances.values[0] += .{
|
||||||
mapping.res.axis_strength(player.res.move_x) * 10,
|
mapping.res.axis_strength(player.res.move_x) * 10,
|
||||||
mapping.res.axis_strength(player.res.move_y) * 10,
|
mapping.res.axis_strength(player.res.move_y) * 10,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(world: *ona.World, events: ona.App.Events) !void {
|
fn setup(world: *coral.World, events: ona.App.Events) !void {
|
||||||
try world.set_resource(Actors{});
|
try world.set_resource(.none, Actors{});
|
||||||
try world.set_resource(Player{});
|
try world.set_resource(.none, Player{});
|
||||||
|
|
||||||
try world.on_event(events.load, ona.system_fn(load), .{.label = "load"});
|
try world.on_event(events.load, coral.system_fn(load), .{.label = "load"});
|
||||||
try world.on_event(events.update, ona.system_fn(update), .{.label = "update"});
|
try world.on_event(events.update, coral.system_fn(update), .{.label = "update"});
|
||||||
try world.on_event(events.exit, ona.system_fn(exit), .{.label = "exit"});
|
try world.on_event(events.exit, coral.system_fn(exit), .{.label = "exit"});
|
||||||
try world.on_event(events.render, ona.system_fn(render), .{.label = "render actors"});
|
try world.on_event(events.render, coral.system_fn(render), .{.label = "render actors"});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const flow = @import("flow");
|
|
||||||
|
|
||||||
events: *const Events,
|
events: *const Events,
|
||||||
target_frame_time: f64,
|
target_frame_time: f64,
|
||||||
is_running: bool,
|
is_running: bool,
|
||||||
|
|
||||||
pub const Events = struct {
|
pub const Events = struct {
|
||||||
load: flow.World.Event,
|
load: coral.World.Event,
|
||||||
pre_update: flow.World.Event,
|
pre_update: coral.World.Event,
|
||||||
update: flow.World.Event,
|
update: coral.World.Event,
|
||||||
post_update: flow.World.Event,
|
post_update: coral.World.Event,
|
||||||
render: flow.World.Event,
|
render: coral.World.Event,
|
||||||
finish: flow.World.Event,
|
finish: coral.World.Event,
|
||||||
exit: flow.World.Event,
|
exit: coral.World.Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
|
@ -2,8 +2,6 @@ const App = @import("./App.zig");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const flow = @import("flow");
|
|
||||||
|
|
||||||
const gfx = @import("./gfx.zig");
|
const gfx = @import("./gfx.zig");
|
||||||
|
|
||||||
const msg = @import("./msg.zig");
|
const msg = @import("./msg.zig");
|
||||||
|
@ -37,15 +35,15 @@ pub const Mapping = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn setup(world: *flow.World, events: App.Events) std.mem.Allocator.Error!void {
|
pub fn setup(world: *coral.World, events: App.Events) std.mem.Allocator.Error!void {
|
||||||
try world.set_resource(Mapping{});
|
try world.set_resource(.none, Mapping{});
|
||||||
|
|
||||||
try world.on_event(events.pre_update, flow.system_fn(update), .{
|
try world.on_event(events.pre_update, coral.system_fn(update), .{
|
||||||
.label = "update act",
|
.label = "update act",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(inputs: msg.Receive(gfx.Input), mapping: flow.Write(Mapping)) void {
|
pub fn update(inputs: msg.Receive(gfx.Input), mapping: coral.Write(Mapping)) void {
|
||||||
mapping.res.keys_pressed = Mapping.ScancodeSet.initEmpty();
|
mapping.res.keys_pressed = Mapping.ScancodeSet.initEmpty();
|
||||||
|
|
||||||
for (inputs.messages()) |message| {
|
for (inputs.messages()) |message| {
|
||||||
|
|
|
@ -6,35 +6,27 @@ const device = @import("./gfx/device.zig");
|
||||||
|
|
||||||
const ext = @import("./ext.zig");
|
const ext = @import("./ext.zig");
|
||||||
|
|
||||||
const flow = @import("flow");
|
|
||||||
|
|
||||||
const formats = @import("./gfx/formats.zig");
|
const formats = @import("./gfx/formats.zig");
|
||||||
|
|
||||||
const msg = @import("./msg.zig");
|
const msg = @import("./msg.zig");
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const AssetFormat = struct {
|
|
||||||
extension: []const u8,
|
|
||||||
file_desc: *const FileDesc,
|
|
||||||
|
|
||||||
pub const Error = std.mem.Allocator.Error || coral.files.Error || error {
|
|
||||||
FormatUnsupported,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const FileDesc = fn (*std.heap.ArenaAllocator, coral.files.Storage, []const u8) Error!Desc;
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Assets = struct {
|
pub const Assets = struct {
|
||||||
context: device.Context,
|
context: device.Context,
|
||||||
formats: coral.stack.Sequential(AssetFormat),
|
formats: coral.stack.Sequential(Format),
|
||||||
staging_arena: std.heap.ArenaAllocator,
|
staging_arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
pub fn create_from_file(
|
pub const Format = struct {
|
||||||
self: *Assets,
|
extension: []const u8,
|
||||||
storage: coral.files.Storage,
|
file_desc: *const fn (*std.heap.ArenaAllocator, coral.files.Storage, []const u8) Error!Desc,
|
||||||
path: []const u8,
|
|
||||||
) (OpenError || AssetFormat.Error)!*Handle {
|
pub const Error = std.mem.Allocator.Error || coral.files.Error || error {
|
||||||
|
FormatUnsupported,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn create_from_file(self: *Assets, storage: coral.files.Storage, path: []const u8) (OpenError || Format.Error)!*Handle {
|
||||||
defer {
|
defer {
|
||||||
const max_cache_size = 536870912;
|
const max_cache_size = 536870912;
|
||||||
|
|
||||||
|
@ -70,8 +62,6 @@ pub const Assets = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const thread_restriction = .main;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Color = @Vector(4, f32);
|
pub const Color = @Vector(4, f32);
|
||||||
|
@ -173,7 +163,7 @@ pub const Queue = struct {
|
||||||
command_index: usize,
|
command_index: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(_: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(_: coral.system.BindContext) std.mem.Allocator.Error!State {
|
||||||
// TODO: Review how good of an idea this global state is, even if bind is guaranteed to always be ran on main.
|
// TODO: Review how good of an idea this global state is, even if bind is guaranteed to always be ran on main.
|
||||||
if (renders.is_empty()) {
|
if (renders.is_empty()) {
|
||||||
renders = .{.allocator = coral.heap.allocator};
|
renders = .{.allocator = coral.heap.allocator};
|
||||||
|
@ -217,7 +207,7 @@ pub const Transform2D = extern struct {
|
||||||
origin: Point2D = @splat(0),
|
origin: Point2D = @splat(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
const builtin_formats = [_]AssetFormat{
|
const builtin_formats = [_]Assets.Format{
|
||||||
.{
|
.{
|
||||||
.extension = "bmp",
|
.extension = "bmp",
|
||||||
.file_desc = formats.bmp_file_desc,
|
.file_desc = formats.bmp_file_desc,
|
||||||
|
@ -238,7 +228,7 @@ pub const colors = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
pub fn poll(app: coral.Write(App), inputs: msg.Send(Input)) !void {
|
||||||
var event = @as(ext.SDL_Event, undefined);
|
var event = @as(ext.SDL_Event, undefined);
|
||||||
|
|
||||||
while (ext.SDL_PollEvent(&event) != 0) {
|
while (ext.SDL_PollEvent(&event) != 0) {
|
||||||
|
@ -251,7 +241,7 @@ pub fn poll(app: flow.Write(App), inputs: msg.Send(Input)) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(world: *flow.World, events: App.Events) (error {Unsupported} || std.Thread.SpawnError || std.mem.Allocator.Error)!void {
|
pub fn setup(world: *coral.World, events: App.Events) (error {Unsupported} || std.Thread.SpawnError || std.mem.Allocator.Error)!void {
|
||||||
if (ext.SDL_Init(ext.SDL_INIT_VIDEO) != 0) {
|
if (ext.SDL_Init(ext.SDL_INIT_VIDEO) != 0) {
|
||||||
return error.Unsupported;
|
return error.Unsupported;
|
||||||
}
|
}
|
||||||
|
@ -260,32 +250,32 @@ pub fn setup(world: *flow.World, events: App.Events) (error {Unsupported} || std
|
||||||
|
|
||||||
errdefer context.deinit();
|
errdefer context.deinit();
|
||||||
|
|
||||||
var registered_formats = coral.stack.Sequential(AssetFormat){.allocator = coral.heap.allocator};
|
var registered_formats = coral.stack.Sequential(Assets.Format){.allocator = coral.heap.allocator};
|
||||||
|
|
||||||
errdefer registered_formats.deinit();
|
errdefer registered_formats.deinit();
|
||||||
|
|
||||||
try registered_formats.grow(builtin_formats.len);
|
try registered_formats.grow(builtin_formats.len);
|
||||||
std.debug.assert(registered_formats.push_all(&builtin_formats));
|
std.debug.assert(registered_formats.push_all(&builtin_formats));
|
||||||
|
|
||||||
try world.set_resource(Assets{
|
try world.set_resource(.none, Assets{
|
||||||
.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
.staging_arena = std.heap.ArenaAllocator.init(coral.heap.allocator),
|
||||||
.formats = registered_formats,
|
.formats = registered_formats,
|
||||||
.context = context,
|
.context = context,
|
||||||
});
|
});
|
||||||
|
|
||||||
try world.set_resource(Display{});
|
try world.set_resource(.none, Display{});
|
||||||
try world.on_event(events.pre_update, flow.system_fn(poll), .{.label = "poll gfx"});
|
try world.on_event(events.pre_update, coral.system_fn(poll), .{.label = "poll gfx"});
|
||||||
try world.on_event(events.exit, flow.system_fn(stop), .{.label = "stop gfx"});
|
try world.on_event(events.exit, coral.system_fn(stop), .{.label = "stop gfx"});
|
||||||
try world.on_event(events.finish, flow.system_fn(synchronize), .{.label = "synchronize gfx"});
|
try world.on_event(events.finish, coral.system_fn(synchronize), .{.label = "synchronize gfx"});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(assets: flow.Write(Assets)) void {
|
pub fn stop(assets: coral.Write(Assets)) void {
|
||||||
assets.res.staging_arena.deinit();
|
assets.res.staging_arena.deinit();
|
||||||
assets.res.formats.deinit();
|
assets.res.formats.deinit();
|
||||||
assets.res.context.deinit();
|
assets.res.context.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn synchronize(assets: flow.Write(Assets), display: flow.Read(Display)) !void {
|
pub fn synchronize(assets: coral.Write(Assets), display: coral.Read(Display)) !void {
|
||||||
assets.res.context.submit(.{
|
assets.res.context.submit(.{
|
||||||
.width = display.res.width,
|
.width = display.res.width,
|
||||||
.height = display.res.height,
|
.height = display.res.height,
|
||||||
|
|
|
@ -2,14 +2,14 @@ const commands = @import("./commands.zig");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
|
const default_2d = @import("./shaders/default_2d.glsl.zig");
|
||||||
|
|
||||||
const ext = @import("../ext.zig");
|
const ext = @import("../ext.zig");
|
||||||
|
|
||||||
const gfx = @import("../gfx.zig");
|
const gfx = @import("../gfx.zig");
|
||||||
|
|
||||||
const sokol = @import("sokol");
|
const sokol = @import("sokol");
|
||||||
|
|
||||||
const spirv = @import("./spirv.zig");
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
pub const Context = struct {
|
pub const Context = struct {
|
||||||
|
@ -125,7 +125,7 @@ pub const Context = struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
const Frame = struct {
|
const Frame = struct {
|
||||||
projection: Projection = .{@splat(0), @splat(0), @splat(0), @splat(0)},
|
projection: default_2d.Mat4 = .{@splat(0), @splat(0), @splat(0), @splat(0)},
|
||||||
flushed_batch_count: usize = 0,
|
flushed_batch_count: usize = 0,
|
||||||
pushed_batch_count: usize = 0,
|
pushed_batch_count: usize = 0,
|
||||||
render_passes: usize = 0,
|
render_passes: usize = 0,
|
||||||
|
@ -380,7 +380,7 @@ const Loop = struct {
|
||||||
ext.SDL_GL_DeleteContext(context);
|
ext.SDL_GL_DeleteContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rendering_2d = try Rendering2D.init();
|
var rendering_2d = Rendering2D.init();
|
||||||
|
|
||||||
defer rendering_2d.deinit();
|
defer rendering_2d.deinit();
|
||||||
|
|
||||||
|
@ -455,8 +455,6 @@ const Loop = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Projection = [4]@Vector(4, f32);
|
|
||||||
|
|
||||||
pub const RenderChain = commands.Chain(gfx.Command, clone_command);
|
pub const RenderChain = commands.Chain(gfx.Command, clone_command);
|
||||||
|
|
||||||
pub const RenderList = commands.List(gfx.Command, clone_command);
|
pub const RenderList = commands.List(gfx.Command, clone_command);
|
||||||
|
@ -531,16 +529,13 @@ const Rendering2D = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
sokol.gfx.applyPipeline(self.batching_pipeline);
|
sokol.gfx.applyPipeline(self.batching_pipeline);
|
||||||
sokol.gfx.applyUniforms(.VS, 0, sokol.gfx.asRange(&frame.projection));
|
|
||||||
|
sokol.gfx.applyUniforms(.VS, default_2d.SLOT_Projection, sokol.gfx.asRange(&default_2d.Projection{
|
||||||
|
.projection = frame.projection,
|
||||||
|
}));
|
||||||
|
|
||||||
const mesh_2d = resource_cast(frame.mesh.?).payload.mesh_2d;
|
const mesh_2d = resource_cast(frame.mesh.?).payload.mesh_2d;
|
||||||
|
|
||||||
const texture_image, const texture_sampler = switch (resource_cast(frame.texture.?).payload) {
|
|
||||||
.texture => |texture| .{texture.image, texture.sampler},
|
|
||||||
.render_target => |render_target| .{render_target.color_image, render_target.sampler},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
var bindings = sokol.gfx.Bindings{
|
var bindings = sokol.gfx.Bindings{
|
||||||
.vertex_buffers = get: {
|
.vertex_buffers = get: {
|
||||||
var buffers = [_]sokol.gfx.Buffer{.{}} ** 8;
|
var buffers = [_]sokol.gfx.Buffer{.{}} ** 8;
|
||||||
|
@ -552,40 +547,44 @@ const Rendering2D = struct {
|
||||||
|
|
||||||
.index_buffer = mesh_2d.index_buffer,
|
.index_buffer = mesh_2d.index_buffer,
|
||||||
|
|
||||||
.fs = .{
|
.fs = switch (resource_cast(frame.texture.?).payload) {
|
||||||
.images = get: {
|
.texture => |texture| .{
|
||||||
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
.images = get: {
|
||||||
|
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
||||||
|
|
||||||
images[0] = texture_image;
|
images[0] = texture.image;
|
||||||
|
|
||||||
break: get images;
|
break: get images;
|
||||||
|
},
|
||||||
|
|
||||||
|
.samplers = get: {
|
||||||
|
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
||||||
|
|
||||||
|
samplers[0] = texture.sampler;
|
||||||
|
|
||||||
|
break: get samplers;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.samplers = get: {
|
.render_target => |render_target| .{
|
||||||
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
.images = get: {
|
||||||
|
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
||||||
|
|
||||||
samplers[0] = texture_sampler;
|
images[0] = render_target.color_image;
|
||||||
|
|
||||||
break: get samplers;
|
break: get images;
|
||||||
},
|
},
|
||||||
},
|
|
||||||
|
|
||||||
.vs = .{
|
.samplers = get: {
|
||||||
.images = get: {
|
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
||||||
var images = [_]sokol.gfx.Image{.{}} ** 12;
|
|
||||||
|
|
||||||
images[0] = texture_image;
|
samplers[0] = render_target.sampler;
|
||||||
|
|
||||||
break: get images;
|
break: get samplers;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.samplers = get: {
|
else => unreachable,
|
||||||
var samplers = [_]sokol.gfx.Sampler{.{}} ** 8;
|
|
||||||
|
|
||||||
samplers[0] = texture_sampler;
|
|
||||||
|
|
||||||
break: get samplers;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -604,7 +603,7 @@ const Rendering2D = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init() spirv.Error!Rendering2D {
|
fn init() Rendering2D {
|
||||||
sokol.gfx.setup(.{
|
sokol.gfx.setup(.{
|
||||||
.environment = .{
|
.environment = .{
|
||||||
.defaults = .{
|
.defaults = .{
|
||||||
|
@ -619,16 +618,6 @@ const Rendering2D = struct {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var spirv_unit = try spirv.Unit.init();
|
|
||||||
|
|
||||||
defer spirv_unit.deinit();
|
|
||||||
|
|
||||||
try spirv_unit.compile(shader_spirv[0 ..], .vertex);
|
|
||||||
try spirv_unit.compile(shader_spirv[0 ..], .fragment);
|
|
||||||
|
|
||||||
std.log.info("{s}\n", .{spirv_unit.shader_desc.fs.source});
|
|
||||||
std.log.info("{s}", .{spirv_unit.shader_desc.vs.source});
|
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.batching_pipeline = sokol.gfx.makePipeline(.{
|
.batching_pipeline = sokol.gfx.makePipeline(.{
|
||||||
.label = "2D drawing pipeline",
|
.label = "2D drawing pipeline",
|
||||||
|
@ -637,42 +626,42 @@ const Rendering2D = struct {
|
||||||
.attrs = get: {
|
.attrs = get: {
|
||||||
var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16;
|
var attrs = [_]sokol.gfx.VertexAttrState{.{}} ** 16;
|
||||||
|
|
||||||
attrs[0] = .{
|
attrs[default_2d.ATTR_vs_mesh_xy] = .{
|
||||||
.format = .FLOAT2,
|
.format = .FLOAT2,
|
||||||
.buffer_index = buffer_indices.mesh,
|
.buffer_index = buffer_indices.mesh,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[1] = .{
|
attrs[default_2d.ATTR_vs_mesh_uv] = .{
|
||||||
.format = .FLOAT2,
|
.format = .FLOAT2,
|
||||||
.buffer_index = buffer_indices.mesh,
|
.buffer_index = buffer_indices.mesh,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[2] = .{
|
attrs[default_2d.ATTR_vs_instance_xbasis] = .{
|
||||||
.format = .FLOAT2,
|
.format = .FLOAT2,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[3] = .{
|
attrs[default_2d.ATTR_vs_instance_ybasis] = .{
|
||||||
.format = .FLOAT2,
|
.format = .FLOAT2,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[4] = .{
|
attrs[default_2d.ATTR_vs_instance_origin] = .{
|
||||||
.format = .FLOAT2,
|
.format = .FLOAT2,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[5] = .{
|
attrs[default_2d.ATTR_vs_instance_tint] = .{
|
||||||
.format = .UBYTE4N,
|
.format = .UBYTE4N,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[6] = .{
|
attrs[default_2d.ATTR_vs_instance_depth] = .{
|
||||||
.format = .FLOAT,
|
.format = .FLOAT,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
|
||||||
attrs[7] = .{
|
attrs[default_2d.ATTR_vs_instance_rect] = .{
|
||||||
.format = .FLOAT4,
|
.format = .FLOAT4,
|
||||||
.buffer_index = buffer_indices.instance,
|
.buffer_index = buffer_indices.instance,
|
||||||
};
|
};
|
||||||
|
@ -689,7 +678,7 @@ const Rendering2D = struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
.shader = sokol.gfx.makeShader(spirv_unit.shader_desc),
|
.shader = sokol.gfx.makeShader(default_2d.draw2dShaderDesc(sokol.gfx.queryBackend())),
|
||||||
.index_type = .UINT16,
|
.index_type = .UINT16,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -709,6 +698,7 @@ const Rendering2D = struct {
|
||||||
|
|
||||||
var pass = sokol.gfx.Pass{
|
var pass = sokol.gfx.Pass{
|
||||||
.action = .{
|
.action = .{
|
||||||
|
// TODO: Review if stencil buffer is needed.
|
||||||
.stencil = .{.load_action = .CLEAR},
|
.stencil = .{.load_action = .CLEAR},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -735,7 +725,7 @@ const Rendering2D = struct {
|
||||||
const render_target = resource_cast(render_texture).payload.render_target;
|
const render_target = resource_cast(render_texture).payload.render_target;
|
||||||
|
|
||||||
pass.attachments = render_target.attachments;
|
pass.attachments = render_target.attachments;
|
||||||
frame.projection = orthographic_projection(0.0, @floatFromInt(render_target.width), 0.0, @floatFromInt(render_target.height), -1.0, 1.0);
|
frame.projection = ortho_projection(0.0, @floatFromInt(render_target.width), 0.0, @floatFromInt(render_target.height), -1.0, 1.0);
|
||||||
} else {
|
} else {
|
||||||
var target_width, var target_height = [_]c_int{0, 0};
|
var target_width, var target_height = [_]c_int{0, 0};
|
||||||
|
|
||||||
|
@ -751,115 +741,11 @@ const Rendering2D = struct {
|
||||||
.gl = .{.framebuffer = 0},
|
.gl = .{.framebuffer = 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
frame.projection = orthographic_projection(0.0, @floatFromInt(target_width), @floatFromInt(target_height), 0.0, -1.0, 1.0);
|
frame.projection = ortho_projection(0.0, @floatFromInt(target_width), @floatFromInt(target_height), 0.0, -1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sokol.gfx.beginPass(pass);
|
sokol.gfx.beginPass(pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
const shader_spirv = &[_]u32{
|
|
||||||
0x07230203, 0x00010000, 0x00110000, 0x0000006f, 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
|
|
||||||
0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e, 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
|
|
||||||
0x0008000f, 0x00000004, 0x00000002, 0x6e69616d, 0x00000000, 0x00000003, 0x00000004, 0x00000005,
|
|
||||||
0x0010000f, 0x00000000, 0x00000006, 0x6e69616d, 0x00000000, 0x00000007, 0x00000008, 0x00000009,
|
|
||||||
0x0000000a, 0x0000000b, 0x0000000c, 0x0000000d, 0x0000000e, 0x0000000f, 0x00000010, 0x00000011,
|
|
||||||
0x00030010, 0x00000002, 0x00000007, 0x00030003, 0x00000002, 0x000001ae, 0x00030003, 0x00000002,
|
|
||||||
0x000001ae, 0x00040005, 0x00000002, 0x6e69616d, 0x00000000, 0x00040005, 0x00000003, 0x65786574,
|
|
||||||
0x0000006c, 0x00030005, 0x00000012, 0x00786574, 0x00030005, 0x00000013, 0x00706d73, 0x00030005,
|
|
||||||
0x00000004, 0x00007675, 0x00040005, 0x00000005, 0x6f6c6f63, 0x00000072, 0x00040005, 0x00000006,
|
|
||||||
0x6e69616d, 0x00000000, 0x00060005, 0x00000014, 0x6c726f77, 0x6f705f64, 0x69746973, 0x00006e6f,
|
|
||||||
0x00060005, 0x00000007, 0x74736e69, 0x65636e61, 0x69726f5f, 0x006e6967, 0x00040005, 0x00000008,
|
|
||||||
0x6873656d, 0x0079785f, 0x00060005, 0x00000009, 0x74736e69, 0x65636e61, 0x6162785f, 0x00736973,
|
|
||||||
0x00060005, 0x0000000a, 0x74736e69, 0x65636e61, 0x6162795f, 0x00736973, 0x00070005, 0x00000015,
|
|
||||||
0x6a6f7270, 0x65746365, 0x6f705f64, 0x69746973, 0x00006e6f, 0x00050005, 0x00000016, 0x6a6f7250,
|
|
||||||
0x69746365, 0x00006e6f, 0x00080006, 0x00000016, 0x00000000, 0x6a6f7270, 0x69746365, 0x6d5f6e6f,
|
|
||||||
0x69727461, 0x00000078, 0x00030005, 0x00000017, 0x00000000, 0x00060005, 0x00000018, 0x505f6c67,
|
|
||||||
0x65567265, 0x78657472, 0x00000000, 0x00060006, 0x00000018, 0x00000000, 0x505f6c67, 0x7469736f,
|
|
||||||
0x006e6f69, 0x00070006, 0x00000018, 0x00000001, 0x505f6c67, 0x746e696f, 0x657a6953, 0x00000000,
|
|
||||||
0x00070006, 0x00000018, 0x00000002, 0x435f6c67, 0x4470696c, 0x61747369, 0x0065636e, 0x00030005,
|
|
||||||
0x0000000b, 0x00000000, 0x00060005, 0x0000000c, 0x74736e69, 0x65636e61, 0x7065645f, 0x00006874,
|
|
||||||
0x00040005, 0x0000000d, 0x6f6c6f63, 0x00000072, 0x00060005, 0x0000000e, 0x74736e69, 0x65636e61,
|
|
||||||
0x6e69745f, 0x00000074, 0x00050005, 0x00000019, 0x74636572, 0x736f705f, 0x00000000, 0x00060005,
|
|
||||||
0x0000000f, 0x74736e69, 0x65636e61, 0x6365725f, 0x00000074, 0x00050005, 0x0000001a, 0x74636572,
|
|
||||||
0x7a69735f, 0x00000065, 0x00030005, 0x00000010, 0x00007675, 0x00040005, 0x00000011, 0x6873656d,
|
|
||||||
0x0076755f, 0x00040047, 0x00000003, 0x0000001e, 0x00000000, 0x00040047, 0x00000012, 0x00000022,
|
|
||||||
0x00000000, 0x00040047, 0x00000012, 0x00000021, 0x00000000, 0x00040047, 0x00000013, 0x00000022,
|
|
||||||
0x00000000, 0x00040047, 0x00000013, 0x00000021, 0x00000000, 0x00040047, 0x00000004, 0x0000001e,
|
|
||||||
0x00000001, 0x00040047, 0x00000005, 0x0000001e, 0x00000000, 0x00040047, 0x00000007, 0x0000001e,
|
|
||||||
0x00000004, 0x00040047, 0x00000008, 0x0000001e, 0x00000000, 0x00040047, 0x00000009, 0x0000001e,
|
|
||||||
0x00000002, 0x00040047, 0x0000000a, 0x0000001e, 0x00000003, 0x00040048, 0x00000016, 0x00000000,
|
|
||||||
0x00000005, 0x00050048, 0x00000016, 0x00000000, 0x00000023, 0x00000000, 0x00050048, 0x00000016,
|
|
||||||
0x00000000, 0x00000007, 0x00000010, 0x00030047, 0x00000016, 0x00000002, 0x00040047, 0x00000017,
|
|
||||||
0x00000022, 0x00000000, 0x00040047, 0x00000017, 0x00000021, 0x00000000, 0x00050048, 0x00000018,
|
|
||||||
0x00000000, 0x0000000b, 0x00000000, 0x00050048, 0x00000018, 0x00000001, 0x0000000b, 0x00000001,
|
|
||||||
0x00050048, 0x00000018, 0x00000002, 0x0000000b, 0x00000003, 0x00030047, 0x00000018, 0x00000002,
|
|
||||||
0x00040047, 0x0000000c, 0x0000001e, 0x00000006, 0x00040047, 0x0000000d, 0x0000001e, 0x00000000,
|
|
||||||
0x00040047, 0x0000000e, 0x0000001e, 0x00000005, 0x00040047, 0x0000000f, 0x0000001e, 0x00000007,
|
|
||||||
0x00040047, 0x00000010, 0x0000001e, 0x00000001, 0x00040047, 0x00000011, 0x0000001e, 0x00000001,
|
|
||||||
0x00020013, 0x0000001b, 0x00030021, 0x0000001c, 0x0000001b, 0x00030016, 0x0000001d, 0x00000020,
|
|
||||||
0x00040017, 0x0000001e, 0x0000001d, 0x00000004, 0x00040020, 0x0000001f, 0x00000003, 0x0000001e,
|
|
||||||
0x0004003b, 0x0000001f, 0x00000003, 0x00000003, 0x00090019, 0x00000020, 0x0000001d, 0x00000001,
|
|
||||||
0x00000000, 0x00000000, 0x00000000, 0x00000001, 0x00000000, 0x00040020, 0x00000021, 0x00000000,
|
|
||||||
0x00000020, 0x0004003b, 0x00000021, 0x00000012, 0x00000000, 0x0002001a, 0x00000022, 0x00040020,
|
|
||||||
0x00000023, 0x00000000, 0x00000022, 0x0004003b, 0x00000023, 0x00000013, 0x00000000, 0x0003001b,
|
|
||||||
0x00000024, 0x00000020, 0x00040017, 0x00000025, 0x0000001d, 0x00000002, 0x00040020, 0x00000026,
|
|
||||||
0x00000001, 0x00000025, 0x0004003b, 0x00000026, 0x00000004, 0x00000001, 0x00040020, 0x00000027,
|
|
||||||
0x00000001, 0x0000001e, 0x0004003b, 0x00000027, 0x00000005, 0x00000001, 0x00040015, 0x00000028,
|
|
||||||
0x00000020, 0x00000000, 0x0004002b, 0x00000028, 0x00000029, 0x00000003, 0x00040020, 0x0000002a,
|
|
||||||
0x00000003, 0x0000001d, 0x0004002b, 0x0000001d, 0x0000002b, 0x00000000, 0x00020014, 0x0000002c,
|
|
||||||
0x00040020, 0x0000002d, 0x00000007, 0x00000025, 0x0004003b, 0x00000026, 0x00000007, 0x00000001,
|
|
||||||
0x0004003b, 0x00000026, 0x00000008, 0x00000001, 0x0004002b, 0x00000028, 0x0000002e, 0x00000000,
|
|
||||||
0x00040020, 0x0000002f, 0x00000001, 0x0000001d, 0x0004003b, 0x00000026, 0x00000009, 0x00000001,
|
|
||||||
0x0004002b, 0x00000028, 0x00000030, 0x00000001, 0x0004003b, 0x00000026, 0x0000000a, 0x00000001,
|
|
||||||
0x00040018, 0x00000031, 0x0000001e, 0x00000004, 0x0003001e, 0x00000016, 0x00000031, 0x00040020,
|
|
||||||
0x00000032, 0x00000002, 0x00000016, 0x0004003b, 0x00000032, 0x00000017, 0x00000002, 0x00040015,
|
|
||||||
0x00000033, 0x00000020, 0x00000001, 0x0004002b, 0x00000033, 0x00000034, 0x00000000, 0x00040020,
|
|
||||||
0x00000035, 0x00000002, 0x00000031, 0x0004002b, 0x0000001d, 0x00000036, 0x00000000, 0x0004002b,
|
|
||||||
0x0000001d, 0x00000037, 0x3f800000, 0x0004001c, 0x00000038, 0x0000001d, 0x00000030, 0x0005001e,
|
|
||||||
0x00000018, 0x0000001e, 0x0000001d, 0x00000038, 0x00040020, 0x00000039, 0x00000003, 0x00000018,
|
|
||||||
0x0004003b, 0x00000039, 0x0000000b, 0x00000003, 0x0004003b, 0x0000002f, 0x0000000c, 0x00000001,
|
|
||||||
0x0004003b, 0x0000001f, 0x0000000d, 0x00000003, 0x0004003b, 0x00000027, 0x0000000e, 0x00000001,
|
|
||||||
0x0004003b, 0x00000027, 0x0000000f, 0x00000001, 0x00040020, 0x0000003a, 0x00000003, 0x00000025,
|
|
||||||
0x0004003b, 0x0000003a, 0x00000010, 0x00000003, 0x0004003b, 0x00000026, 0x00000011, 0x00000001,
|
|
||||||
0x00050036, 0x0000001b, 0x00000002, 0x00000000, 0x0000001c, 0x000200f8, 0x0000003b, 0x0004003d,
|
|
||||||
0x00000020, 0x0000003c, 0x00000012, 0x0004003d, 0x00000022, 0x0000003d, 0x00000013, 0x00050056,
|
|
||||||
0x00000024, 0x0000003e, 0x0000003c, 0x0000003d, 0x0004003d, 0x00000025, 0x0000003f, 0x00000004,
|
|
||||||
0x00050057, 0x0000001e, 0x00000040, 0x0000003e, 0x0000003f, 0x0004003d, 0x0000001e, 0x00000041,
|
|
||||||
0x00000005, 0x00050085, 0x0000001e, 0x00000042, 0x00000040, 0x00000041, 0x0003003e, 0x00000003,
|
|
||||||
0x00000042, 0x00050041, 0x0000002a, 0x00000043, 0x00000003, 0x00000029, 0x0004003d, 0x0000001d,
|
|
||||||
0x00000044, 0x00000043, 0x000500b4, 0x0000002c, 0x00000045, 0x00000044, 0x0000002b, 0x000300f7,
|
|
||||||
0x00000046, 0x00000000, 0x000400fa, 0x00000045, 0x00000047, 0x00000046, 0x000200f8, 0x00000047,
|
|
||||||
0x000100fc, 0x000200f8, 0x00000046, 0x000100fd, 0x00010038, 0x00050036, 0x0000001b, 0x00000006,
|
|
||||||
0x00000000, 0x0000001c, 0x000200f8, 0x00000048, 0x0004003b, 0x0000002d, 0x00000014, 0x00000007,
|
|
||||||
0x0004003b, 0x0000002d, 0x00000015, 0x00000007, 0x0004003b, 0x0000002d, 0x00000019, 0x00000007,
|
|
||||||
0x0004003b, 0x0000002d, 0x0000001a, 0x00000007, 0x0004003d, 0x00000025, 0x00000049, 0x00000007,
|
|
||||||
0x00050041, 0x0000002f, 0x0000004a, 0x00000008, 0x0000002e, 0x0004003d, 0x0000001d, 0x0000004b,
|
|
||||||
0x0000004a, 0x0004003d, 0x00000025, 0x0000004c, 0x00000009, 0x0005008e, 0x00000025, 0x0000004d,
|
|
||||||
0x0000004c, 0x0000004b, 0x00050081, 0x00000025, 0x0000004e, 0x00000049, 0x0000004d, 0x00050041,
|
|
||||||
0x0000002f, 0x0000004f, 0x00000008, 0x00000030, 0x0004003d, 0x0000001d, 0x00000050, 0x0000004f,
|
|
||||||
0x0004003d, 0x00000025, 0x00000051, 0x0000000a, 0x0005008e, 0x00000025, 0x00000052, 0x00000051,
|
|
||||||
0x00000050, 0x00050081, 0x00000025, 0x00000053, 0x0000004e, 0x00000052, 0x0003003e, 0x00000014,
|
|
||||||
0x00000053, 0x00050041, 0x00000035, 0x00000054, 0x00000017, 0x00000034, 0x0004003d, 0x00000031,
|
|
||||||
0x00000055, 0x00000054, 0x0004003d, 0x00000025, 0x00000056, 0x00000014, 0x00050051, 0x0000001d,
|
|
||||||
0x00000057, 0x00000056, 0x00000000, 0x00050051, 0x0000001d, 0x00000058, 0x00000056, 0x00000001,
|
|
||||||
0x00070050, 0x0000001e, 0x00000059, 0x00000057, 0x00000058, 0x00000036, 0x00000037, 0x00050091,
|
|
||||||
0x0000001e, 0x0000005a, 0x00000055, 0x00000059, 0x0007004f, 0x00000025, 0x0000005b, 0x0000005a,
|
|
||||||
0x0000005a, 0x00000000, 0x00000001, 0x0003003e, 0x00000015, 0x0000005b, 0x0004003d, 0x00000025,
|
|
||||||
0x0000005c, 0x00000015, 0x0004003d, 0x0000001d, 0x0000005d, 0x0000000c, 0x00050051, 0x0000001d,
|
|
||||||
0x0000005e, 0x0000005c, 0x00000000, 0x00050051, 0x0000001d, 0x0000005f, 0x0000005c, 0x00000001,
|
|
||||||
0x00070050, 0x0000001e, 0x00000060, 0x0000005e, 0x0000005f, 0x0000005d, 0x00000037, 0x00050041,
|
|
||||||
0x0000001f, 0x00000061, 0x0000000b, 0x00000034, 0x0003003e, 0x00000061, 0x00000060, 0x0004003d,
|
|
||||||
0x0000001e, 0x00000062, 0x0000000e, 0x0003003e, 0x0000000d, 0x00000062, 0x0004003d, 0x0000001e,
|
|
||||||
0x00000063, 0x0000000f, 0x0007004f, 0x00000025, 0x00000064, 0x00000063, 0x00000063, 0x00000000,
|
|
||||||
0x00000001, 0x0003003e, 0x00000019, 0x00000064, 0x0004003d, 0x0000001e, 0x00000065, 0x0000000f,
|
|
||||||
0x0007004f, 0x00000025, 0x00000066, 0x00000065, 0x00000065, 0x00000002, 0x00000003, 0x0004003d,
|
|
||||||
0x0000001e, 0x00000067, 0x0000000f, 0x0007004f, 0x00000025, 0x00000068, 0x00000067, 0x00000067,
|
|
||||||
0x00000000, 0x00000001, 0x00050083, 0x00000025, 0x00000069, 0x00000066, 0x00000068, 0x0003003e,
|
|
||||||
0x0000001a, 0x00000069, 0x0004003d, 0x00000025, 0x0000006a, 0x00000019, 0x0004003d, 0x00000025,
|
|
||||||
0x0000006b, 0x00000011, 0x0004003d, 0x00000025, 0x0000006c, 0x0000001a, 0x00050085, 0x00000025,
|
|
||||||
0x0000006d, 0x0000006b, 0x0000006c, 0x00050081, 0x00000025, 0x0000006e, 0x0000006a, 0x0000006d,
|
|
||||||
0x0003003e, 0x00000010, 0x0000006e, 0x000100fd, 0x00010038,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Resource = struct {
|
const Resource = struct {
|
||||||
|
@ -919,19 +805,19 @@ fn clone_command(self: gfx.Command, arena: *std.heap.ArenaAllocator) std.mem.All
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn orthographic_projection(left: f32, right: f32, bottom: f32, top: f32, zNear: f32, zFar: f32) Projection {
|
|
||||||
const width = right - left;
|
|
||||||
const height = top - bottom;
|
|
||||||
const depth = zFar - zNear;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.{2 / width, 0, 0, 0},
|
|
||||||
.{0, 2 / height, 0, 0},
|
|
||||||
.{0, 0, -(2 / depth), 0},
|
|
||||||
.{-((right + left) / width), -((top + bottom) / height), -((zFar + zNear) / depth), 1},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resource_cast(handle: *gfx.Handle) *Resource {
|
fn resource_cast(handle: *gfx.Handle) *Resource {
|
||||||
return @ptrCast(@alignCast(handle));
|
return @ptrCast(@alignCast(handle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ortho_projection(left: f32, right: f32, bottom: f32, top: f32, zNear: f32, zFar: f32) default_2d.Mat4 {
|
||||||
|
var result = default_2d.Mat4{.{1, 0, 0, 0}, .{0, 1, 0, 0}, .{0, 0, 1, 0}, .{0, 0, 0, 1}};
|
||||||
|
|
||||||
|
result[0][0] = 2 / (right - left);
|
||||||
|
result[1][1] = 2 / (top - bottom);
|
||||||
|
result[2][2] = - 2 / (zFar - zNear);
|
||||||
|
result[3][0] = - (right + left) / (right - left);
|
||||||
|
result[3][1] = - (top + bottom) / (top - bottom);
|
||||||
|
result[3][2] = - (zFar + zNear) / (zFar - zNear);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub fn bmp_file_desc(
|
||||||
arena: *std.heap.ArenaAllocator,
|
arena: *std.heap.ArenaAllocator,
|
||||||
storage: coral.files.Storage,
|
storage: coral.files.Storage,
|
||||||
path: []const u8,
|
path: []const u8,
|
||||||
) gfx.AssetFormat.Error!gfx.Desc {
|
) gfx.Assets.Format.Error!gfx.Desc {
|
||||||
const header = try storage.read_little(path, 0, extern struct {
|
const header = try storage.read_little(path, 0, extern struct {
|
||||||
type: [2]u8 align (1),
|
type: [2]u8 align (1),
|
||||||
file_size: u32 align (1),
|
file_size: u32 align (1),
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
@header pub const Vec2 = @Vector(2, f32)
|
||||||
|
@header pub const Mat4 = [4]@Vector(4, f32)
|
||||||
|
@ctype vec2 Vec2
|
||||||
|
@ctype mat4 Mat4
|
||||||
|
|
||||||
|
@vs vs
|
||||||
|
in vec2 mesh_xy;
|
||||||
|
in vec2 mesh_uv;
|
||||||
|
|
||||||
|
in vec2 instance_xbasis;
|
||||||
|
in vec2 instance_ybasis;
|
||||||
|
in vec2 instance_origin;
|
||||||
|
in vec4 instance_tint;
|
||||||
|
in float instance_depth;
|
||||||
|
in vec4 instance_rect;
|
||||||
|
|
||||||
|
uniform Projection {
|
||||||
|
mat4 projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
out vec4 color;
|
||||||
|
out vec2 uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Calculate the world position of the vertex
|
||||||
|
const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis;
|
||||||
|
const vec2 projected_position = (projection * vec4(world_position, 0, 1)).xy;
|
||||||
|
|
||||||
|
// Set the position of the vertex in clip space
|
||||||
|
gl_Position = vec4(projected_position, instance_depth, 1.0);
|
||||||
|
color = instance_tint;
|
||||||
|
|
||||||
|
// Calculate the width and height from left, top, right, bottom configuration
|
||||||
|
const vec2 rect_pos = instance_rect.xy; // left, top
|
||||||
|
const vec2 rect_size = instance_rect.zw - instance_rect.xy; // right - left, bottom - top
|
||||||
|
|
||||||
|
// Calculate the adjusted UV coordinates using the instance_rect
|
||||||
|
uv = rect_pos + (mesh_uv * rect_size);
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@fs fs
|
||||||
|
uniform texture2D tex;
|
||||||
|
uniform sampler smp;
|
||||||
|
|
||||||
|
in vec4 color;
|
||||||
|
in vec2 uv;
|
||||||
|
|
||||||
|
out vec4 texel;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
texel = texture(sampler2D(tex, smp), uv) * color;
|
||||||
|
|
||||||
|
if (texel.a == 0) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@program draw_2d vs fs
|
|
@ -1,17 +0,0 @@
|
||||||
#version 430
|
|
||||||
|
|
||||||
layout (binding = 0) uniform texture2D tex;
|
|
||||||
layout (binding = 0) uniform sampler smp;
|
|
||||||
|
|
||||||
layout (location = 0) in vec4 color;
|
|
||||||
layout (location = 1) in vec2 uv;
|
|
||||||
|
|
||||||
layout (location = 0) out vec4 texel;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
texel = texture(sampler2D(tex, smp), uv) * color;
|
|
||||||
|
|
||||||
if (texel.a == 0) {
|
|
||||||
discard;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
#version 430
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 mesh_xy;
|
|
||||||
layout (location = 1) in vec2 mesh_uv;
|
|
||||||
|
|
||||||
layout (location = 2) in vec2 instance_xbasis;
|
|
||||||
layout (location = 3) in vec2 instance_ybasis;
|
|
||||||
layout (location = 4) in vec2 instance_origin;
|
|
||||||
layout (location = 5) in vec4 instance_tint;
|
|
||||||
layout (location = 6) in float instance_depth;
|
|
||||||
layout (location = 7) in vec4 instance_rect;
|
|
||||||
|
|
||||||
layout (binding = 0) uniform Projection {
|
|
||||||
mat4 projection_matrix;
|
|
||||||
};
|
|
||||||
|
|
||||||
layout (location = 0) out vec4 color;
|
|
||||||
layout (location = 1) out vec2 uv;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
// Calculate the world position of the vertex
|
|
||||||
const vec2 world_position = instance_origin + mesh_xy.x * instance_xbasis + mesh_xy.y * instance_ybasis;
|
|
||||||
const vec2 projected_position = (projection_matrix * vec4(world_position, 0, 1)).xy;
|
|
||||||
|
|
||||||
// Set the position of the vertex in clip space
|
|
||||||
gl_Position = vec4(projected_position, instance_depth, 1.0);
|
|
||||||
color = instance_tint;
|
|
||||||
|
|
||||||
// Calculate the width and height from left, top, right, bottom configuration
|
|
||||||
const vec2 rect_pos = instance_rect.xy; // left, top
|
|
||||||
const vec2 rect_size = instance_rect.zw - instance_rect.xy; // right - left, bottom - top
|
|
||||||
|
|
||||||
// Calculate the adjusted UV coordinates using the instance_rect
|
|
||||||
uv = rect_pos + (mesh_uv * rect_size);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
const coral = @import("coral");
|
|
||||||
|
|
||||||
const ext = @cImport({
|
|
||||||
@cInclude("spirv-cross/spirv_cross_c.h");
|
|
||||||
});
|
|
||||||
|
|
||||||
const sokol = @import("sokol");
|
|
||||||
|
|
||||||
const std = @import("std");
|
|
||||||
|
|
||||||
pub const Error = std.mem.Allocator.Error || error {
|
|
||||||
UnsupportedTarget,
|
|
||||||
InvalidSPIRV,
|
|
||||||
UnsupportedSPIRV,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Unit = struct {
|
|
||||||
context: ext.spvc_context,
|
|
||||||
shader_desc: sokol.gfx.ShaderDesc,
|
|
||||||
attrs_used: u32 = 0,
|
|
||||||
|
|
||||||
pub fn compile(self: *Unit, spirv: []const u32, stage: Stage) Error!void {
|
|
||||||
const execution_model, const stage_desc = switch (stage) {
|
|
||||||
.vertex => .{ext.SpvExecutionModelVertex, &self.shader_desc.vs},
|
|
||||||
.fragment => .{ext.SpvExecutionModelFragment, &self.shader_desc.fs},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Backend = struct {
|
|
||||||
target: ext.spvc_backend,
|
|
||||||
option_values: []const struct {ext.spvc_compiler_option, c_uint},
|
|
||||||
};
|
|
||||||
|
|
||||||
const backend: Backend = switch (sokol.gfx.queryBackend()) {
|
|
||||||
.GLCORE => .{
|
|
||||||
.target = ext.SPVC_BACKEND_GLSL,
|
|
||||||
|
|
||||||
.option_values = &.{
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_VERSION, 430},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_ES, @intFromBool(false)},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_VULKAN_SEMANTICS, @intFromBool(false)},
|
|
||||||
.{ext.SPVC_COMPILER_OPTION_GLSL_EMIT_UNIFORM_BUFFER_AS_PLAIN_UNIFORMS, @intFromBool(true)},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
else => @panic("Unimplemented"),
|
|
||||||
};
|
|
||||||
|
|
||||||
const compiler = parse_and_configure: {
|
|
||||||
var parsed_ir: ext.spvc_parsed_ir = null;
|
|
||||||
|
|
||||||
try to_error(ext.spvc_context_parse_spirv(self.context, spirv.ptr, spirv.len, &parsed_ir));
|
|
||||||
|
|
||||||
var compiler: ext.spvc_compiler = null;
|
|
||||||
|
|
||||||
try to_error(ext.spvc_context_create_compiler(self.context, backend.target, parsed_ir, ext.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler));
|
|
||||||
try to_error(ext.spvc_compiler_build_combined_image_samplers(compiler));
|
|
||||||
|
|
||||||
var combined_image_samplers: []const ext.spvc_combined_image_sampler = &.{};
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_get_combined_image_samplers(compiler, @ptrCast(&combined_image_samplers.ptr), &combined_image_samplers.len));
|
|
||||||
|
|
||||||
var binding: u32 = 0;
|
|
||||||
|
|
||||||
for (combined_image_samplers) |combined_image_sampler| {
|
|
||||||
var name_buffer = [_:0]u8{0} ** 255;
|
|
||||||
|
|
||||||
const name = coral.utf8.print_formatted(&name_buffer, "{image_name}_{sampler_name}", .{
|
|
||||||
.image_name = coral.io.slice_sentineled(u8, 0, ext.spvc_compiler_get_name(compiler, combined_image_sampler.image_id)),
|
|
||||||
.sampler_name = coral.io.slice_sentineled(u8, 0, ext.spvc_compiler_get_name(compiler, combined_image_sampler.sampler_id)),
|
|
||||||
}) catch {
|
|
||||||
return error.InvalidSPIRV;
|
|
||||||
};
|
|
||||||
|
|
||||||
ext.spvc_compiler_set_name(compiler, combined_image_sampler.combined_id, name);
|
|
||||||
ext.spvc_compiler_set_decoration(compiler, combined_image_sampler.combined_id, ext.SpvDecorationBinding, binding);
|
|
||||||
|
|
||||||
binding += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
break: parse_and_configure compiler;
|
|
||||||
};
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_set_entry_point(compiler, stage_desc.entry, @intCast(execution_model)));
|
|
||||||
|
|
||||||
const resources = create: {
|
|
||||||
var resources: ext.spvc_resources = null;
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_create_shader_resources(compiler, &resources));
|
|
||||||
|
|
||||||
break: create resources;
|
|
||||||
};
|
|
||||||
|
|
||||||
try reflect_uniform_blocks(compiler, resources, stage_desc);
|
|
||||||
try reflect_image_samplers(compiler, resources, stage_desc);
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_install_compiler_options(compiler, create: {
|
|
||||||
var options: ext.spvc_compiler_options = null;
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_create_compiler_options(compiler, &options));
|
|
||||||
|
|
||||||
for (backend.option_values) |option_value| {
|
|
||||||
const entry, const value = option_value;
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_options_set_uint(options, entry, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
break: create options;
|
|
||||||
}));
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_compile(compiler, @ptrCast(&stage_desc.source)));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Unit) void {
|
|
||||||
ext.spvc_context_destroy(self.context);
|
|
||||||
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init() std.mem.Allocator.Error!Unit {
|
|
||||||
var context: ext.spvc_context = null;
|
|
||||||
|
|
||||||
if (ext.spvc_context_create(&context) != ext.SPVC_SUCCESS) {
|
|
||||||
return error.OutOfMemory;
|
|
||||||
}
|
|
||||||
|
|
||||||
errdefer ext.spvc_context_destroy(context);
|
|
||||||
|
|
||||||
ext.spvc_context_set_error_callback(context, log_context_errors, null);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.context = context,
|
|
||||||
|
|
||||||
.shader_desc = .{
|
|
||||||
.vs = .{.entry = "main"},
|
|
||||||
.fs = .{.entry = "main"},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Stage = enum {
|
|
||||||
fragment,
|
|
||||||
vertex,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn log_context_errors(userdata: ?*anyopaque, error_message: [*c]const u8) callconv(.C) void {
|
|
||||||
std.debug.assert(userdata == null);
|
|
||||||
std.log.err("{s}", .{error_message});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reflect_image_samplers(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage: *sokol.gfx.ShaderStageDesc) Error!void {
|
|
||||||
var reflected_sampled_images: []const ext.spvc_reflected_resource = &.{};
|
|
||||||
|
|
||||||
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
|
||||||
resources,
|
|
||||||
ext.SPVC_RESOURCE_TYPE_SAMPLED_IMAGE,
|
|
||||||
@ptrCast(&reflected_sampled_images.ptr),
|
|
||||||
&reflected_sampled_images.len,
|
|
||||||
));
|
|
||||||
|
|
||||||
if (reflected_sampled_images.len > stage.image_sampler_pairs.len) {
|
|
||||||
return error.UnsupportedSPIRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (0 .. reflected_sampled_images.len, reflected_sampled_images) |i, reflected_sampled_image| {
|
|
||||||
const sampled_image_type = ext.spvc_compiler_get_type_handle(compiler, reflected_sampled_image.type_id);
|
|
||||||
|
|
||||||
if (ext.spvc_type_get_basetype(sampled_image_type) != ext.SPVC_BASETYPE_SAMPLED_IMAGE) {
|
|
||||||
return error.InvalidSPIRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
stage.images[i] = .{
|
|
||||||
.multisampled = ext.spvc_type_get_image_multisampled(sampled_image_type) != 0,
|
|
||||||
|
|
||||||
.image_type = try switch (ext.spvc_type_get_image_dimension(sampled_image_type)) {
|
|
||||||
ext.SpvDim2D => sokol.gfx.ImageType._2D,
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
.sample_type = try switch (ext.spvc_type_get_basetype(ext.spvc_compiler_get_type_handle(compiler, ext.spvc_type_get_image_sampled_type(sampled_image_type)))) {
|
|
||||||
ext.SPVC_BASETYPE_FP32 => sokol.gfx.ImageSampleType.FLOAT,
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
stage.samplers[i] = .{
|
|
||||||
.sampler_type = .DEFAULT,
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
stage.image_sampler_pairs[i] = .{
|
|
||||||
.glsl_name = ext.spvc_compiler_get_name(compiler, reflected_sampled_image.id),
|
|
||||||
.image_slot = @intCast(i),
|
|
||||||
.sampler_slot = @intCast(i),
|
|
||||||
.used = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reflect_uniform_blocks(compiler: ext.spvc_compiler, resources: ext.spvc_resources, stage: *sokol.gfx.ShaderStageDesc) Error!void {
|
|
||||||
var reflected_uniform_buffers: []const ext.spvc_reflected_resource = &.{};
|
|
||||||
|
|
||||||
try to_error(ext.spvc_resources_get_resource_list_for_type(
|
|
||||||
resources,
|
|
||||||
ext.SPVC_RESOURCE_TYPE_UNIFORM_BUFFER,
|
|
||||||
@ptrCast(&reflected_uniform_buffers.ptr),
|
|
||||||
&reflected_uniform_buffers.len,
|
|
||||||
));
|
|
||||||
|
|
||||||
if (reflected_uniform_buffers.len > stage.uniform_blocks.len) {
|
|
||||||
return error.UnsupportedSPIRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (stage.uniform_blocks[0 .. reflected_uniform_buffers.len], reflected_uniform_buffers) |*uniform_block, reflected_uniform_buffer| {
|
|
||||||
const uniform_buffer_type = ext.spvc_compiler_get_type_handle(compiler, reflected_uniform_buffer.type_id);
|
|
||||||
|
|
||||||
if (ext.spvc_type_get_basetype(uniform_buffer_type) != ext.SPVC_BASETYPE_STRUCT) {
|
|
||||||
return error.InvalidSPIRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
const member_count = ext.spvc_type_get_num_member_types(uniform_buffer_type);
|
|
||||||
|
|
||||||
if (member_count > uniform_block.uniforms.len) {
|
|
||||||
return error.UnsupportedSPIRV;
|
|
||||||
}
|
|
||||||
|
|
||||||
try to_error(ext.spvc_compiler_get_declared_struct_size(compiler, uniform_buffer_type, &uniform_block.size));
|
|
||||||
|
|
||||||
var uniform_blocks_used: u32 = 0;
|
|
||||||
|
|
||||||
while (uniform_blocks_used < member_count) : (uniform_blocks_used += 1) {
|
|
||||||
const member_type_id = ext.spvc_type_get_member_type(uniform_buffer_type, uniform_blocks_used);
|
|
||||||
const member_type = ext.spvc_compiler_get_type_handle(compiler, member_type_id);
|
|
||||||
|
|
||||||
uniform_block.uniforms[uniform_blocks_used] = .{
|
|
||||||
// .name = ext.spvc_compiler_get_member_name(compiler, ext.spvc_type_get_base_type_id(uniform_buffer_type), uniform_blocks_used),
|
|
||||||
|
|
||||||
.array_count = @intCast(try switch (ext.spvc_type_get_num_array_dimensions(member_type)) {
|
|
||||||
0 => 0,
|
|
||||||
|
|
||||||
1 => switch (ext.spvc_type_array_dimension_is_literal(member_type, 1) != 0) {
|
|
||||||
true => ext.spvc_type_get_array_dimension(member_type, 1),
|
|
||||||
false => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
}),
|
|
||||||
|
|
||||||
.type = try switch (ext.spvc_type_get_basetype(member_type)) {
|
|
||||||
ext.SPVC_BASETYPE_INT32 => switch (ext.spvc_type_get_vector_size(member_type)) {
|
|
||||||
1 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT else error.InvalidSPIRV,
|
|
||||||
2 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT2 else error.InvalidSPIRV,
|
|
||||||
3 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT3 else error.InvalidSPIRV,
|
|
||||||
4 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.INT4 else error.InvalidSPIRV,
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
ext.SPVC_BASETYPE_FP32 => switch (ext.spvc_type_get_vector_size(member_type)) {
|
|
||||||
1 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT else error.InvalidSPIRV,
|
|
||||||
2 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT2 else error.InvalidSPIRV,
|
|
||||||
3 => if (ext.spvc_type_get_columns(member_type) == 1) sokol.gfx.UniformType.FLOAT3 else error.InvalidSPIRV,
|
|
||||||
|
|
||||||
4 => switch (ext.spvc_type_get_columns(member_type)) {
|
|
||||||
1 => sokol.gfx.UniformType.FLOAT4,
|
|
||||||
4 => sokol.gfx.UniformType.MAT4,
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => error.InvalidSPIRV,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// uniform_block.uniforms[uniform_blocks_used].name = ext.spvc_compiler_get_member_name(compiler, ext.spvc_type_get_base_type_id(uniform_buffer_type), uniform_blocks_used);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_error(result: ext.spvc_result) Error!void {
|
|
||||||
return switch (result) {
|
|
||||||
ext.SPVC_SUCCESS => {},
|
|
||||||
ext.SPVC_ERROR_INVALID_SPIRV => error.InvalidSPIRV,
|
|
||||||
ext.SPVC_ERROR_UNSUPPORTED_SPIRV => error.UnsupportedSPIRV,
|
|
||||||
ext.SPVC_ERROR_OUT_OF_MEMORY => error.OutOfMemory,
|
|
||||||
ext.SPVC_ERROR_INVALID_ARGUMENT, ext.SPVC_ERROR_INT_MAX => unreachable,
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -2,8 +2,6 @@ const App = @import("./App.zig");
|
||||||
|
|
||||||
const coral = @import("coral");
|
const coral = @import("coral");
|
||||||
|
|
||||||
const flow = @import("flow");
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
fn Channel(comptime Message: type) type {
|
fn Channel(comptime Message: type) type {
|
||||||
|
@ -14,7 +12,7 @@ fn Channel(comptime Message: type) type {
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
fn cleanup(channel: flow.Write(Self)) void {
|
fn cleanup(channel: coral.Write(Self)) void {
|
||||||
channel.res.deinit();
|
channel.res.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +24,7 @@ fn Channel(comptime Message: type) type {
|
||||||
self.* = undefined;
|
self.* = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn swap(channel: flow.Write(Self)) void {
|
fn swap(channel: coral.Write(Self)) void {
|
||||||
channel.res.ticks = coral.scalars.add(channel.res.ticks, 1) orelse 0;
|
channel.res.ticks = coral.scalars.add(channel.res.ticks, 1) orelse 0;
|
||||||
|
|
||||||
if (channel.res.ticks == 0) {
|
if (channel.res.ticks == 0) {
|
||||||
|
@ -67,12 +65,12 @@ pub fn Receive(comptime Message: type) type {
|
||||||
channel: *const TypedChannel,
|
channel: *const TypedChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(context: coral.system.BindContext) std.mem.Allocator.Error!State {
|
||||||
return .{
|
return .{
|
||||||
.channel = (try context.register_readable_resource_access(TypedChannel)) orelse set: {
|
.channel = (try context.register_read_only_resource_access(thread_restriction, TypedChannel)) orelse set: {
|
||||||
try context.world.set_resource(TypedChannel.init(coral.heap.allocator));
|
try context.world.set_resource(thread_restriction, TypedChannel.init(coral.heap.allocator));
|
||||||
|
|
||||||
break: set (try context.register_readable_resource_access(TypedChannel)).?;
|
break: set (try context.register_read_only_resource_access(thread_restriction, TypedChannel)).?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -101,22 +99,22 @@ pub fn Send(comptime Message: type) type {
|
||||||
channel: *TypedChannel,
|
channel: *TypedChannel,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn bind(context: flow.system.BindContext) std.mem.Allocator.Error!State {
|
pub fn bind(context: coral.system.BindContext) std.mem.Allocator.Error!State {
|
||||||
return .{
|
return .{
|
||||||
.channel = (try context.register_writable_resource_access(TypedChannel)) orelse set: {
|
.channel = (try context.register_read_write_resource_access(thread_restriction, TypedChannel)) orelse set: {
|
||||||
try context.world.set_resource(TypedChannel.init(coral.heap.allocator));
|
try context.world.set_resource(thread_restriction, TypedChannel.init(coral.heap.allocator));
|
||||||
|
|
||||||
const app = context.world.get_resource(App).?;
|
const app = context.world.get_resource(.none, App).?;
|
||||||
|
|
||||||
try context.world.on_event(app.events.post_update, flow.system_fn(TypedChannel.swap), .{
|
try context.world.on_event(app.events.post_update, coral.system_fn(TypedChannel.swap), .{
|
||||||
.label = "swap channel of " ++ @typeName(Message),
|
.label = "swap channel of " ++ @typeName(Message),
|
||||||
});
|
});
|
||||||
|
|
||||||
try context.world.on_event(app.events.exit, flow.system_fn(TypedChannel.cleanup), .{
|
try context.world.on_event(app.events.exit, coral.system_fn(TypedChannel.cleanup), .{
|
||||||
.label = "clean up channel of " ++ @typeName(Message),
|
.label = "clean up channel of " ++ @typeName(Message),
|
||||||
});
|
});
|
||||||
|
|
||||||
break: set (try context.register_writable_resource_access(TypedChannel)).?;
|
break: set (try context.register_read_write_resource_access(thread_restriction, TypedChannel)).?;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -132,3 +130,5 @@ pub fn Send(comptime Message: type) type {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const thread_restriction = coral.World.ThreadRestriction.none;
|
||||||
|
|
|
@ -6,8 +6,6 @@ const coral = @import("coral");
|
||||||
|
|
||||||
const ext = @import("./ext.zig");
|
const ext = @import("./ext.zig");
|
||||||
|
|
||||||
const flow = @import("flow");
|
|
||||||
|
|
||||||
pub const gfx = @import("./gfx.zig");
|
pub const gfx = @import("./gfx.zig");
|
||||||
|
|
||||||
pub const msg = @import("./msg.zig");
|
pub const msg = @import("./msg.zig");
|
||||||
|
@ -25,13 +23,7 @@ pub const Options = struct {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Read = flow.Read;
|
pub const Setup = fn (*coral.World, App.Events) anyerror!void;
|
||||||
|
|
||||||
pub const Setup = fn (*flow.World, App.Events) anyerror!void;
|
|
||||||
|
|
||||||
pub const World = flow.World;
|
|
||||||
|
|
||||||
pub const Write = flow.Write;
|
|
||||||
|
|
||||||
pub const default_middlewares = &.{
|
pub const default_middlewares = &.{
|
||||||
gfx.setup,
|
gfx.setup,
|
||||||
|
@ -45,7 +37,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
}
|
}
|
||||||
|
|
||||||
var world = try switch (options.execution) {
|
var world = try switch (options.execution) {
|
||||||
.single_threaded => flow.World.init(0),
|
.single_threaded => coral.World.init(0),
|
||||||
|
|
||||||
.thread_share => |thread_share| init: {
|
.thread_share => |thread_share| init: {
|
||||||
const cpu_count = @as(u32, @intCast(std.math.clamp(std.Thread.getCpuCount() catch |cpu_count_error| {
|
const cpu_count = @as(u32, @intCast(std.math.clamp(std.Thread.getCpuCount() catch |cpu_count_error| {
|
||||||
|
@ -56,7 +48,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
});
|
});
|
||||||
}, 0, std.math.maxInt(u32))));
|
}, 0, std.math.maxInt(u32))));
|
||||||
|
|
||||||
break: init flow.World.init(coral.scalars.fractional(cpu_count, thread_share) orelse 0);
|
break: init coral.World.init(coral.scalars.fractional(cpu_count, thread_share) orelse 0);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,7 +64,7 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
.exit = try world.create_event("exit"),
|
.exit = try world.create_event("exit"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const app = try world.set_get_resource(App{
|
const app = try world.set_get_resource(.none, App{
|
||||||
.events = &events,
|
.events = &events,
|
||||||
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
.target_frame_time = 1.0 / @as(f64, @floatFromInt(options.tick_rate)),
|
||||||
.is_running = true,
|
.is_running = true,
|
||||||
|
@ -109,5 +101,3 @@ pub fn start_app(setup: Setup, options: Options) anyerror!void {
|
||||||
|
|
||||||
try world.run_event(events.exit);
|
try world.run_event(events.exit);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const system_fn = flow.system_fn;
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue