feat: initial commit
This commit is contained in:
commit
b26942dc4e
|
@ -0,0 +1,5 @@
|
|||
zig-cache/
|
||||
zig-out/
|
||||
deps.zig
|
||||
gyro.lock
|
||||
.gyro
|
|
@ -0,0 +1,46 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard target options allows the person running `zig build` to choose
|
||||
// what target to build for. Here we do not override the defaults, which
|
||||
// means any target is allowed, and the default is native. Other options
|
||||
// for restricting supported target set are available.
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const exe = b.addExecutable("confgen", "src/main.zig");
|
||||
exe.setTarget(target);
|
||||
exe.setBuildMode(mode);
|
||||
|
||||
exe.strip = mode != .Debug and mode != .ReleaseSafe;
|
||||
setupExe(exe);
|
||||
|
||||
exe.install();
|
||||
|
||||
const run_cmd = exe.run();
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const exe_tests = b.addTest("src/main.zig");
|
||||
exe_tests.setTarget(target);
|
||||
exe_tests.setBuildMode(mode);
|
||||
setupExe(exe_tests);
|
||||
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&exe_tests.step);
|
||||
}
|
||||
|
||||
fn setupExe(exe: *std.build.LibExeObjStep) void {
|
||||
exe.linkLibC();
|
||||
exe.linkSystemLibrary("luajit");
|
||||
|
||||
exe.unwind_tables = true;
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
str: []const u8,
|
||||
pos: usize,
|
||||
|
||||
pub const Parser = @This();
|
||||
|
||||
pub const Token = struct {
|
||||
token_type: TokenType,
|
||||
str: []const u8,
|
||||
};
|
||||
|
||||
pub const TokenType = enum {
|
||||
text,
|
||||
lua,
|
||||
lua_literal,
|
||||
};
|
||||
|
||||
pub fn next(self: *Parser) !?Token {
|
||||
var current_type = TokenType.text;
|
||||
|
||||
// TODO: this approach allows for stuff like <% <! %> %> to be considered valid.
|
||||
// that sucks, but I cannot be bothered
|
||||
var depth: usize = 0;
|
||||
|
||||
var i = self.pos;
|
||||
while (i < self.str.len) : (i += 1) {
|
||||
const charpair = self.str[i..@min(i + 2, self.str.len)];
|
||||
|
||||
if (std.mem.eql(u8, charpair, "<%")) {
|
||||
if (current_type == .text and self.pos != i) {
|
||||
const tok = Token{
|
||||
.token_type = .text,
|
||||
.str = self.str[self.pos..i],
|
||||
};
|
||||
|
||||
self.pos = i;
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
defer depth += 1;
|
||||
|
||||
if (depth > 0)
|
||||
continue;
|
||||
|
||||
i += 1;
|
||||
self.pos = i + 1;
|
||||
current_type = .lua_literal;
|
||||
} else if (std.mem.eql(u8, charpair, "%>")) {
|
||||
depth -= 1;
|
||||
if (depth > 0)
|
||||
continue;
|
||||
|
||||
// Can't check for != .lua_literal here, as that would require using
|
||||
// sort of a stack for nested blocks
|
||||
if (current_type == .text)
|
||||
return error.UnexpectedClose;
|
||||
|
||||
const tok = Token{
|
||||
.token_type = .lua_literal,
|
||||
.str = std.mem.trim(u8, self.str[self.pos..i], &std.ascii.whitespace),
|
||||
};
|
||||
|
||||
self.pos = i + 2;
|
||||
|
||||
return tok;
|
||||
} else if (std.mem.eql(u8, charpair, "<!")) {
|
||||
if (current_type == .text and self.pos != i) {
|
||||
const tok = Token{
|
||||
.token_type = .text,
|
||||
.str = self.str[self.pos..i],
|
||||
};
|
||||
|
||||
self.pos = i;
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
defer depth += 1;
|
||||
|
||||
if (depth > 0)
|
||||
continue;
|
||||
|
||||
i += 1;
|
||||
self.pos = i + 1;
|
||||
current_type = .lua;
|
||||
} else if (std.mem.eql(u8, charpair, "!>")) {
|
||||
depth -= 1;
|
||||
|
||||
if (depth > 0)
|
||||
continue;
|
||||
|
||||
if (current_type == .text)
|
||||
return error.UnexpectedClose;
|
||||
|
||||
const tok = Token{
|
||||
.token_type = .lua,
|
||||
.str = std.mem.trim(u8, self.str[self.pos..i], &std.ascii.whitespace),
|
||||
};
|
||||
|
||||
self.pos = i + 2;
|
||||
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.pos == i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (current_type != .text) {
|
||||
return error.UnclosedDelimeter;
|
||||
}
|
||||
|
||||
const tok = Token{
|
||||
.token_type = .text,
|
||||
.str = self.str[self.pos..],
|
||||
};
|
||||
|
||||
self.pos = i;
|
||||
|
||||
return tok;
|
||||
}
|
||||
|
||||
test "lua literal" {
|
||||
const input =
|
||||
\\bla
|
||||
\\<% <% test %> %>
|
||||
\\bla
|
||||
;
|
||||
|
||||
var parser = Parser{ .str = input, .pos = 0 };
|
||||
|
||||
try std.testing.expectEqual(TokenType.text, (try parser.next()).?.token_type);
|
||||
try std.testing.expectEqual(TokenType.lua_literal, (try parser.next()).?.token_type);
|
||||
try std.testing.expectEqual(TokenType.text, (try parser.next()).?.token_type);
|
||||
|
||||
try std.testing.expectEqual(@as(?Token, null), try parser.next());
|
||||
}
|
||||
|
||||
test "lua" {
|
||||
const input =
|
||||
\\bla
|
||||
\\<! test !>
|
||||
\\bla
|
||||
;
|
||||
|
||||
var parser = Parser{ .str = input, .pos = 0 };
|
||||
|
||||
try std.testing.expectEqual(TokenType.text, (try parser.next()).?.token_type);
|
||||
try std.testing.expectEqual(TokenType.lua, (try parser.next()).?.token_type);
|
||||
try std.testing.expectEqual(TokenType.text, (try parser.next()).?.token_type);
|
||||
|
||||
try std.testing.expectEqual(@as(?Token, null), try parser.next());
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const c = @cImport({
|
||||
@cInclude("lua.h");
|
||||
@cInclude("lauxlib.h");
|
||||
@cInclude("lualib.h");
|
||||
});
|
||||
|
||||
/// Generates a wrapper function with error handling for a lua CFunction
|
||||
pub fn luaFunc(comptime func: fn (*c.lua_State) anyerror!c_int) c.lua_CFunction {
|
||||
return &struct {
|
||||
fn f(l: ?*c.lua_State) callconv(.C) c_int {
|
||||
return func(l.?) catch |e| {
|
||||
var buf: [128]u8 = undefined;
|
||||
const err_s = std.fmt.bufPrintZ(
|
||||
&buf,
|
||||
"Zig Error: {s}",
|
||||
.{@errorName(e)},
|
||||
) catch unreachable;
|
||||
c.lua_pushstring(l, err_s.ptr);
|
||||
_ = c.lua_error(l);
|
||||
unreachable;
|
||||
};
|
||||
}
|
||||
}.f;
|
||||
}
|
||||
|
||||
/// Convenience function for pushing some full userdata onto the lua stack
|
||||
pub fn luaPushUdata(l: *c.lua_State, udata: anytype, tname: [*:0]const u8) void {
|
||||
const T = @TypeOf(udata);
|
||||
|
||||
// create and set data
|
||||
@ptrCast(*T, @alignCast(@alignOf(*T), c.lua_newuserdata(l, @sizeOf(T)).?)).* = udata;
|
||||
|
||||
// set metatable
|
||||
c.luaL_getmetatable(l, tname);
|
||||
_ = c.lua_setmetatable(l, -2);
|
||||
}
|
||||
|
||||
pub fn luaGetUdata(comptime T: type, l: *c.lua_State, param: c_int, tname: [*:0]const u8) *T {
|
||||
return @ptrCast(*T, @alignCast(@alignOf(*T), c.luaL_checkudata(l, param, tname)));
|
||||
}
|
|
@ -0,0 +1,265 @@
|
|||
const std = @import("std");
|
||||
const ffi = @import("ffi.zig");
|
||||
const c = ffi.c;
|
||||
|
||||
const TemplateCode = @import("luagen.zig").TemplateCode;
|
||||
|
||||
pub const state_key = "cg_state";
|
||||
|
||||
pub const CgState = struct {
|
||||
outpath: []const u8,
|
||||
rootpath: []const u8,
|
||||
files: std.ArrayList(CgFile),
|
||||
|
||||
pub fn deinit(self: *CgState) void {
|
||||
for (self.files.items) |*file| {
|
||||
file.deinit();
|
||||
}
|
||||
self.files.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
pub const CgFile = struct {
|
||||
outpath: []const u8,
|
||||
content: CgFileContent,
|
||||
|
||||
/// If set, this is a normal file that should just be copied.
|
||||
copy: bool = false,
|
||||
|
||||
pub fn deinit(self: *CgFile) void {
|
||||
std.heap.c_allocator.free(self.outpath);
|
||||
switch (self.content) {
|
||||
.path => |x| std.heap.c_allocator.free(x),
|
||||
.string => |x| std.heap.c_allocator.free(x),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const CgFileContent = union(enum) {
|
||||
path: []const u8,
|
||||
string: []const u8,
|
||||
};
|
||||
|
||||
pub fn initLuaState(cgstate: *CgState) !*c.lua_State {
|
||||
const l = c.luaL_newstate().?;
|
||||
|
||||
// open all lua libs
|
||||
c.luaL_openlibs(l);
|
||||
|
||||
// create opt table
|
||||
c.lua_newtable(l);
|
||||
|
||||
// init cg table
|
||||
c.lua_newtable(l);
|
||||
c.lua_setfield(l, -2, "opt");
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lAddString));
|
||||
c.lua_setfield(l, -2, "addString");
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lAddPath));
|
||||
c.lua_setfield(l, -2, "addPath");
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lAddFile));
|
||||
c.lua_setfield(l, -2, "addFile");
|
||||
|
||||
// add cg table to globals
|
||||
c.lua_setglobal(l, "cg");
|
||||
|
||||
// add state to registry
|
||||
c.lua_pushlightuserdata(l, cgstate);
|
||||
c.lua_setfield(l, c.LUA_REGISTRYINDEX, state_key);
|
||||
|
||||
LTemplate.initMetatable(l);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
pub fn getState(l: *c.lua_State) *CgState {
|
||||
c.lua_getfield(l, c.LUA_REGISTRYINDEX, state_key);
|
||||
const state_ptr = c.lua_touserdata(l, -1);
|
||||
c.lua_pop(l, 1);
|
||||
return @ptrCast(*CgState, @alignCast(@alignOf(*CgState), state_ptr));
|
||||
}
|
||||
|
||||
pub fn generate(l: *c.lua_State, code: TemplateCode) ![]const u8 {
|
||||
const prevtop = c.lua_gettop(l);
|
||||
defer c.lua_settop(l, prevtop);
|
||||
|
||||
if (c.luaL_loadbuffer(l, code.content.ptr, code.content.len, code.name) != 0) {
|
||||
std.log.err("failed to load template: {s}", .{c.lua_tolstring(l, -1, null)});
|
||||
|
||||
return error.LoadTemplate;
|
||||
}
|
||||
|
||||
// create template environment
|
||||
c.lua_newtable(l);
|
||||
|
||||
// initialize environment
|
||||
c.lua_getglobal(l, "_G");
|
||||
c.lua_setfield(l, -2, "_G");
|
||||
|
||||
// add cg.opt to context
|
||||
c.lua_getglobal(l, "cg");
|
||||
c.lua_getfield(l, -1, "opt");
|
||||
c.lua_setfield(l, -3, "opt");
|
||||
c.lua_pop(l, 1);
|
||||
|
||||
// initialize template
|
||||
const tmpl = try LTemplate.init(code);
|
||||
tmpl.push(l);
|
||||
|
||||
c.lua_setfield(l, -2, "tmpl");
|
||||
_ = c.lua_setfenv(l, -2);
|
||||
|
||||
if (c.lua_pcall(l, 0, 0, 0) != 0) {
|
||||
std.log.err("failed to run template: {s}", .{c.lua_tolstring(l, -1, null)});
|
||||
|
||||
return error.RunTemplate;
|
||||
}
|
||||
|
||||
return try tmpl.output.toOwnedSlice();
|
||||
}
|
||||
|
||||
fn lAddString(l: *c.lua_State) !c_int {
|
||||
var outpath_len: usize = 0;
|
||||
const outpath = c.luaL_checklstring(l, 1, &outpath_len);
|
||||
var data_len: usize = 0;
|
||||
const data = c.luaL_checklstring(l, 2, &data_len);
|
||||
|
||||
const state = getState(l);
|
||||
|
||||
try state.files.append(CgFile{
|
||||
.outpath = try std.heap.c_allocator.dupe(u8, outpath[0..outpath_len]),
|
||||
.content = .{ .string = try std.heap.c_allocator.dupe(u8, data[0..data_len]) },
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn lAddPath(l: *c.lua_State) !c_int {
|
||||
var path_len: usize = 0;
|
||||
const path = c.luaL_checklstring(l, 1, &path_len);
|
||||
|
||||
const state = getState(l);
|
||||
|
||||
var dir = try std.fs.cwd().openIterableDir(path[0..path_len], .{});
|
||||
defer dir.close();
|
||||
|
||||
var iter = try dir.walk(std.heap.c_allocator);
|
||||
defer iter.deinit();
|
||||
|
||||
while (try iter.next()) |entry| {
|
||||
if (entry.kind == .Directory)
|
||||
continue;
|
||||
|
||||
const outpath = if (std.mem.endsWith(u8, entry.path, ".cgt"))
|
||||
entry.path[0 .. entry.path.len - 4]
|
||||
else
|
||||
entry.path;
|
||||
|
||||
try state.files.append(.{
|
||||
.outpath = try std.heap.c_allocator.dupe(u8, outpath),
|
||||
.content = .{ .path = try std.heap.c_allocator.dupe(u8, entry.path) },
|
||||
.copy = !std.mem.endsWith(u8, entry.path, ".cgt"),
|
||||
});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn lAddFile(l: *c.lua_State) !c_int {
|
||||
const argc = c.lua_gettop(l);
|
||||
|
||||
var inpath_len: usize = 0;
|
||||
const inpath = c.luaL_checklstring(l, 1, &inpath_len)[0..inpath_len];
|
||||
|
||||
const outpath = if (argc >= 2) blk: {
|
||||
var outpath_len: usize = 0;
|
||||
break :blk c.luaL_checklstring(l, 2, &outpath_len)[0..outpath_len];
|
||||
} else blk: {
|
||||
if (std.mem.endsWith(u8, inpath, ".cgt")) {
|
||||
break :blk inpath[0 .. inpath.len - 4];
|
||||
}
|
||||
break :blk inpath;
|
||||
};
|
||||
|
||||
const state = getState(l);
|
||||
|
||||
try state.files.append(.{
|
||||
.outpath = try std.heap.c_allocator.dupe(u8, outpath),
|
||||
.content = .{ .path = try std.heap.c_allocator.dupe(u8, inpath) },
|
||||
.copy = !std.mem.endsWith(u8, inpath, ".cgt"),
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub const LTemplate = struct {
|
||||
pub const registry_key = "confgen_template";
|
||||
|
||||
code: TemplateCode,
|
||||
output: std.ArrayList(u8),
|
||||
|
||||
pub fn init(code: TemplateCode) !*LTemplate {
|
||||
const self = try std.heap.c_allocator.create(LTemplate);
|
||||
self.* = .{
|
||||
.output = std.ArrayList(u8).init(std.heap.c_allocator),
|
||||
.code = code,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *LTemplate) void {
|
||||
self.output.deinit();
|
||||
std.heap.c_allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn push(self: *LTemplate, l: *c.lua_State) void {
|
||||
ffi.luaPushUdata(l, self, registry_key);
|
||||
}
|
||||
|
||||
fn lGC(l: *c.lua_State) !c_int {
|
||||
const self = ffi.luaGetUdata(*LTemplate, l, 1, registry_key).*;
|
||||
|
||||
self.deinit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn lPushLitIdx(l: *c.lua_State) !c_int {
|
||||
const self = ffi.luaGetUdata(*LTemplate, l, 1, registry_key).*;
|
||||
const idx = std.math.cast(usize, c.luaL_checkint(l, 2)) orelse return error.InvalidIndex;
|
||||
|
||||
if (idx >= self.code.literals.len)
|
||||
return error.InvalidIndex;
|
||||
|
||||
try self.output.appendSlice(self.code.literals[idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn lPushValue(l: *c.lua_State) !c_int {
|
||||
const self = ffi.luaGetUdata(*LTemplate, l, 1, registry_key).*;
|
||||
const val = c.luaL_checklstring(l, 2, null);
|
||||
|
||||
try self.output.appendSlice(std.mem.span(val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fn initMetatable(l: *c.lua_State) void {
|
||||
_ = c.luaL_newmetatable(l, registry_key);
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lGC));
|
||||
c.lua_setfield(l, -2, "__gc");
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lPushLitIdx));
|
||||
c.lua_setfield(l, -2, "pushLitIdx");
|
||||
|
||||
c.lua_pushcfunction(l, ffi.luaFunc(lPushValue));
|
||||
c.lua_setfield(l, -2, "pushValue");
|
||||
|
||||
c.lua_pushvalue(l, -1);
|
||||
c.lua_setfield(l, -2, "__index");
|
||||
}
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
const std = @import("std");
|
||||
const Parser = @import("Parser.zig");
|
||||
|
||||
/// A compiled lua file for a template.
|
||||
/// Contains references to template input string!
|
||||
pub const TemplateCode = struct {
|
||||
name: [:0]const u8,
|
||||
content: []const u8,
|
||||
literals: []const []const u8,
|
||||
|
||||
pub fn deinit(self: TemplateCode) void {
|
||||
std.heap.c_allocator.free(self.name);
|
||||
std.heap.c_allocator.free(self.literals);
|
||||
std.heap.c_allocator.free(self.content);
|
||||
}
|
||||
};
|
||||
|
||||
/// Generates a lua script that allows getting the output from a given parser.
|
||||
pub fn generateLua(parser: *Parser, name: []const u8) !TemplateCode {
|
||||
var outbuf = std.ArrayList(u8).init(std.heap.c_allocator);
|
||||
var literals = std.ArrayList([]const u8).init(std.heap.c_allocator);
|
||||
|
||||
while (try parser.next()) |token| {
|
||||
switch (token.token_type) {
|
||||
.text => {
|
||||
try literals.append(token.str);
|
||||
try outbuf.writer().print(
|
||||
"tmpl:pushLitIdx({d})\n",
|
||||
.{literals.items.len - 1},
|
||||
);
|
||||
},
|
||||
.lua => {
|
||||
try outbuf.appendSlice(token.str);
|
||||
try outbuf.append('\n');
|
||||
},
|
||||
.lua_literal => {
|
||||
try outbuf.writer().print(
|
||||
"tmpl:pushValue({s})\n",
|
||||
.{token.str},
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return .{
|
||||
.name = try std.heap.c_allocator.dupeZ(u8, name),
|
||||
.content = try outbuf.toOwnedSlice(),
|
||||
.literals = try literals.toOwnedSlice(),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
const std = @import("std");
|
||||
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
const luagen = @import("luagen.zig");
|
||||
const lapi = @import("lua_api.zig");
|
||||
const rootfile = @import("rootfile.zig");
|
||||
|
||||
const Parser = @import("Parser.zig");
|
||||
|
||||
comptime {
|
||||
if (@import("builtin").is_test) {
|
||||
std.testing.refAllDeclsRecursive(@This());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
// TODO: add flag to emit generated lua files
|
||||
if (std.os.argv.len != 2) {
|
||||
// TODO: print usage
|
||||
std.log.err("Expected one argument.", .{});
|
||||
return error.InvalidArgs;
|
||||
}
|
||||
|
||||
const conf_dir = (try rootfile.findRootDir()) orelse {
|
||||
std.log.err("Couldn't find confgen.lua file!", .{});
|
||||
return error.RootfileNotFound;
|
||||
};
|
||||
defer std.heap.c_allocator.free(conf_dir);
|
||||
|
||||
var state = lapi.CgState{
|
||||
.outpath = std.mem.span(std.os.argv[1]),
|
||||
.rootpath = conf_dir,
|
||||
.files = std.ArrayList(lapi.CgFile).init(std.heap.c_allocator),
|
||||
};
|
||||
defer state.deinit();
|
||||
|
||||
const l = try lapi.initLuaState(&state);
|
||||
defer c.lua_close(l);
|
||||
|
||||
const conf_file_path = try std.fs.path.joinZ(
|
||||
std.heap.c_allocator,
|
||||
&.{ conf_dir, "confgen.lua" },
|
||||
);
|
||||
defer std.heap.c_allocator.free(conf_file_path);
|
||||
|
||||
if (c.luaL_loadfile(l, conf_file_path.ptr) != 0) {
|
||||
std.log.err("loading confgen.lua: {s}", .{c.lua_tolstring(l, -1, null)});
|
||||
return error.RootfileExec;
|
||||
}
|
||||
|
||||
if (c.lua_pcall(l, 0, 0, 0) != 0) {
|
||||
std.log.err("running confgen.lua: {s}", .{c.lua_tolstring(l, -1, null)});
|
||||
return error.RootfileExec;
|
||||
}
|
||||
|
||||
var content_buf = std.ArrayList(u8).init(std.heap.c_allocator);
|
||||
defer content_buf.deinit();
|
||||
|
||||
for (state.files.items) |file| {
|
||||
if (file.copy) {
|
||||
std.log.info("copying {s}", .{file.outpath});
|
||||
} else {
|
||||
std.log.info("generating {s}", .{file.outpath});
|
||||
}
|
||||
genfile(l, file, &content_buf) catch |e| {
|
||||
std.log.err("generating {s}: {}", .{ file.outpath, e });
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn genfile(
|
||||
l: *c.lua_State,
|
||||
file: lapi.CgFile,
|
||||
content_buf: *std.ArrayList(u8),
|
||||
) !void {
|
||||
const state = lapi.getState(l);
|
||||
|
||||
if (file.copy) {
|
||||
const from_path = try std.fs.path.join(
|
||||
std.heap.c_allocator,
|
||||
&.{ state.rootpath, file.content.path },
|
||||
);
|
||||
defer std.heap.c_allocator.free(from_path);
|
||||
|
||||
const to_path = try std.fs.path.join(
|
||||
std.heap.c_allocator,
|
||||
&.{ state.outpath, file.outpath },
|
||||
);
|
||||
defer std.heap.c_allocator.free(to_path);
|
||||
|
||||
if (std.fs.path.dirname(to_path)) |dir| {
|
||||
try std.fs.cwd().makePath(dir);
|
||||
}
|
||||
|
||||
try std.fs.cwd().copyFile(from_path, std.fs.cwd(), to_path, .{});
|
||||
|
||||
return;
|
||||
}
|
||||
content_buf.clearRetainingCapacity();
|
||||
|
||||
var content: []const u8 = undefined;
|
||||
var fname: ?[]const u8 = null;
|
||||
switch (file.content) {
|
||||
.string => |s| content = s,
|
||||
.path => |p| {
|
||||
fname = std.fs.path.basename(p);
|
||||
const path = try std.fs.path.join(std.heap.c_allocator, &.{ state.rootpath, p });
|
||||
defer std.heap.c_allocator.free(path);
|
||||
|
||||
const f = try std.fs.cwd().openFile(path, .{});
|
||||
defer f.close();
|
||||
|
||||
try f.reader().readAllArrayList(content_buf, std.math.maxInt(usize));
|
||||
|
||||
content = content_buf.items;
|
||||
},
|
||||
}
|
||||
|
||||
var parser = Parser{
|
||||
.str = content,
|
||||
.pos = 0,
|
||||
};
|
||||
|
||||
const tmpl = try luagen.generateLua(&parser, fname orelse file.outpath);
|
||||
defer tmpl.deinit();
|
||||
|
||||
const out = try lapi.generate(l, tmpl);
|
||||
defer std.heap.c_allocator.free(out);
|
||||
|
||||
const path = try std.fs.path.join(
|
||||
std.heap.c_allocator,
|
||||
&.{ state.outpath, file.outpath },
|
||||
);
|
||||
defer std.heap.c_allocator.free(path);
|
||||
|
||||
if (std.fs.path.dirname(path)) |dir| {
|
||||
try std.fs.cwd().makePath(dir);
|
||||
}
|
||||
|
||||
var outfile = try std.fs.cwd().createFile(path, .{});
|
||||
defer outfile.close();
|
||||
|
||||
try outfile.writeAll(out);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
const std = @import("std");
|
||||
const c = @import("ffi.zig").c;
|
||||
|
||||
/// Tries to find the confgen.lua file by walking up the directory tree.
|
||||
/// Returns the directory path, NOT THE FILE PATH.
|
||||
/// Returned path is malloc'd
|
||||
pub fn findRootDir() !?[]const u8 {
|
||||
// TODO: walk upwards
|
||||
_ = std.fs.cwd().statFile("confgen.lua") catch |e| {
|
||||
if (e == error.FileNotFound) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
return try std.heap.c_allocator.dupe(u8, ".");
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
cg.addString("test1.cfg", [[foo <% opt.test %> bar]])
|
||||
cg.addPath(".")
|
||||
|
||||
cg.opt.test = "I'm a test option!"
|
|
@ -0,0 +1,4 @@
|
|||
Hello, I'm a config file
|
||||
|
||||
<! for i=0, 4 do !>test string #<% i + 1 %>: <% opt.test %>
|
||||
<! end !>
|
Loading…
Reference in New Issue