srctree

Gregory Mullen parent 2accc0c8 f1e8a712
add arrays to template For Loop exact=count

inlinesplit
src/template.zig added: 233, removed: 87, total 146
@@ -1,13 +1,3 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const endsWith = std.mem.endsWith;
const indexOfScalar = std.mem.indexOfScalar;
const allocPrint = std.fmt.allocPrint;
const log = std.log.scoped(.Verse);
 
const build_mode = @import("builtin").mode;
 
pub const Structs = @import("comptime_structs");
pub const Directive = @import("template/directive.zig");
pub const Template = @import("template/Template.zig");
@@ -26,6 +16,7 @@ pub var dynamic = &template_data.dynamic;
 
const initDynamic = template_data.initDynamic;
const makeStructName = template_data.makeStructName;
const makeFieldName = template_data.makeFieldName;
pub const findTemplate = template_data.findTemplate;
 
pub fn raze(a: Allocator) void {
@@ -77,6 +68,83 @@ fn testPrint(comptime fmt: []const u8, args: anytype) void {
}
}
 
fn comptimeCountNames(text: []const u8) usize {
var last: usize = 0;
var count: usize = 0;
while (std.mem.indexOfScalarPos(u8, text, last, '<')) |idx| {
last = idx + 1;
if (last >= text.len) break;
if (std.ascii.isUpper(text[last])) count += 1;
if (Directive.init(text[last - 1 ..])) |drct| switch (drct.verb) {
.variable => {},
else => last += drct.tag_block.len,
};
}
return count;
}
 
fn comptimeFields(text: []const u8) [comptimeCountNames(text)]std.builtin.Type.StructField {
var fields: [comptimeCountNames(text)]std.builtin.Type.StructField = undefined;
var last: usize = 0;
for (&fields) |*field| {
while (std.mem.indexOfScalarPos(u8, text, last, '<')) |idx| {
last = idx + 1;
if (last >= text.len) unreachable;
if (Directive.init(text[last - 1 ..])) |drct| switch (drct.verb) {
.variable => {
const ws = std.mem.indexOfAnyPos(u8, text, last, " />") orelse unreachable;
const name = text[last..ws];
var lower: [name.len + 8:0]u8 = @splat(0);
const llen = makeFieldName(name, &lower);
lower[llen] = 0;
const lname: [:0]const u8 = @as([:0]u8, lower[0..llen :0]);
 
field.* = .{
.name = lname,
.type = []const u8,
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf([]const u8),
};
break;
},
.foreach => {
var lower: [drct.noun.len + 8:0]u8 = @splat(0);
const llen = makeFieldName(drct.noun, &lower);
lower[llen] = 0;
const lname: [:0]const u8 = @as([:0]u8, lower[0..llen :0]);
 
const body_type = comptimeStruct(drct.tag_block_body.?);
 
field.* = .{
.name = lname,
.type = switch (drct.otherwise) {
.exact => |ex| [ex]body_type,
else => []const body_type,
},
.default_value_ptr = null,
.is_comptime = false,
.alignment = @alignOf([]const u8),
};
break;
},
else => unreachable,
};
}
}
return fields;
}
 
fn comptimeStruct(text: []const u8) type {
@setEvalBranchQuota(10000);
return @Type(.{ .@"struct" = .{
.layout = .auto,
.fields = &comptimeFields(text),
.decls = &.{},
.is_tuple = false,
} });
}
 
test findPageType {
// Copied from the example template html to create this test, the example
// html is the cannon definition.
@@ -228,23 +296,26 @@ test "directive nothing" {
}
 
test "directive nothing new" {
//const a = std.testing.allocator;
//const t = Template{
// //.path = "/dev/null",
// .name = "test",
// .blob = "<Nothing>",
//};
// TODO fix test
if (true) return error.SkipZigTest;
 
//const ctx = .{};
const a = std.testing.allocator;
const t = Template{
//.path = "/dev/null",
.name = "test",
.blob = "<Nothing>",
};
 
//// TODO is this still the expected behavior
////const p = Page(t, @TypeOf(ctx)).init(.{});
////try std.testing.expectError(error.VariableMissing, p);
const ctx = .{};
 
//const pg = Page(t, @TypeOf(ctx)).init(.{});
//const p = try allocPrint(a, "{}", .{pg});
//defer a.free(p);
//try std.testing.expectEqualStrings("<Nothing>", p);
// TODO is this still the expected behavior
//const p = Page(t, @TypeOf(ctx)).init(.{});
//try std.testing.expectError(error.VariableMissing, p);
 
const pg = Page(t, @TypeOf(ctx)).init(.{});
const p = try allocPrint(a, "{}", .{pg});
defer a.free(p);
try std.testing.expectEqualStrings("<Nothing>", p);
}
 
test "directive ORELSE" {
@@ -748,3 +819,56 @@ test "comment tags" {
 
try std.testing.expectEqualStrings(expected, page);
}
 
test "For exact" {
var a = std.testing.allocator;
 
// 4 chosen by a fair dice roll!
const blob =
\\<For Loop exact="4">
\\ <span><Name></span>
\\</For>
;
 
const expected: []const u8 =
\\<span>first</span>
\\<span>second</span>
\\<span>third</span>
\\<span>forth</span>
\\
;
 
const t = Template{
//.path = "/dev/null",
.name = "test",
.blob = blob,
};
 
const PgType = comptimeStruct(blob);
//@compileLog(@typeInfo(PgType));
//@compileLog(@typeInfo(PgType).@"struct".fields);
 
const ctx = PgType{
.loop = .{
.{ .name = "first" },
.{ .name = "second" },
.{ .name = "third" },
.{ .name = "forth" },
},
};
const pg = Page(t, PgType).init(ctx);
 
const p = try allocPrint(a, "{}", .{pg});
defer a.free(p);
try std.testing.expectEqualStrings(expected, p);
}
 
const std = @import("std");
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const endsWith = std.mem.endsWith;
const indexOfScalar = std.mem.indexOfScalar;
const allocPrint = std.fmt.allocPrint;
const log = std.log.scoped(.Verse);
 
const build_mode = @import("builtin").mode;
 
src/template/directive.zig added: 233, removed: 87, total 146
@@ -13,6 +13,7 @@ pub const Otherwise = union(enum) {
delete: void,
default: []const u8,
template: Template,
exact: usize,
//page: type,
};
 
@@ -40,8 +41,7 @@ pub const KnownType = enum {
pub fn init(str: []const u8) ?Directive {
if (str.len < 2) return null;
if (!isUpper(str[1]) and str[1] != '_') return null;
const end = findTag(str) catch return null;
const tag = str[0..end];
const tag = findTag(str) catch return null;
const verb = tag[1 .. indexOfAnyPos(u8, tag, 1, " /") orelse tag.len - 1];
 
if (verb.len == tag.len - 2) {
@@ -51,14 +51,8 @@ pub fn init(str: []const u8) ?Directive {
}
 
const noun = tag[verb.len + 1 .. tag.len - 1];
if (initVerb(verb, noun, str)) |kind| {
return kind;
}
 
if (initNoun(verb, tag)) |kind| {
return kind;
}
return null;
return initVerb(verb, noun, str) orelse initNoun(verb, tag);
}
 
fn initNoun(noun: []const u8, tag: []const u8) ?Directive {
@@ -119,6 +113,7 @@ fn initNoun(noun: []const u8, tag: []const u8) ?Directive {
pub fn initVerb(verb: []const u8, noun: []const u8, blob: []const u8) ?Directive {
var end: ?usize = null;
var word: Verb = undefined;
 
if (eql(u8, verb, "For")) {
end = calcBody("For", noun, blob) orelse return null;
word = .foreach;
@@ -161,32 +156,43 @@ pub fn initVerb(verb: []const u8, noun: []const u8, blob: []const u8) ?Directive
}
} else return null;
 
// TODO convert to while
//inline for (Word) |tag_name| {
// if (eql(u8, noun, @tagName(tag_name))) {
// pos = calcPos(@tagName(tag_name), blob, verb) orelse return null;
// word = tag_name;
// break;
// }
//} else return null;
var name_end = (indexOfAnyPos(u8, noun, 1, " >") orelse noun.len);
if (noun[name_end - 1] == '/') name_end -= 1;
const name = noun[1..name_end];
 
var exact: ?usize = null;
 
var rem_attr: []const u8 = noun[1 + name.len ..];
while (indexOfScalar(u8, rem_attr, '=') != null) {
if (findAttribute(rem_attr)) |attr| {
if (eql(u8, attr.name, "exact")) {
exact = std.fmt.parseInt(usize, attr.value, 10) catch null;
} else {
std.debug.print("attr {s}\n", .{attr.name});
unreachable;
}
rem_attr = rem_attr[attr.len..];
} else |err| switch (err) {
error.AttrInvalid => break,
else => unreachable,
}
}
 
var end2 = (indexOf(u8, noun, ">") orelse noun.len);
if (noun[end2 - 1] == '/') end2 -= 1;
const body_start = 1 + (indexOfPosLinear(u8, blob, 0, ">") orelse return null);
const body_end: usize = end.? - @as(usize, if (word == .foreach) 6 else if (word == .with) 7 else 0);
const tag_block_body = blob[body_start..body_end];
return .{
.verb = word,
.noun = noun[1..end2],
.otherwise = .required,
.noun = name,
.otherwise = if (exact) |e| .{ .exact = e } else .required,
.tag_block = blob[0..end.?],
.tag_block_body = tag_block_body,
.tag_block_skip = body_start,
};
}
 
fn findTag(blob: []const u8) !usize {
return 1 + (indexOf(u8, blob, ">") orelse return error.TagInvalid);
fn findTag(blob: []const u8) ![]const u8 {
return blob[0 .. 1 + (indexOf(u8, blob, ">") orelse return error.TagInvalid)];
}
 
const TAttr = struct {
@@ -473,6 +479,7 @@ pub fn formatTyped(d: Directive, comptime T: type, ctx: T, out: anytype) !void {
}
}
},
.exact => unreachable,
//inline for (std.meta.fields(T)) |field| {
// if (eql(u8, field.name, noun)) {
// const subdata = @field(ctx, field.name);
 
src/template/page.zig added: 233, removed: 87, total 146
@@ -160,6 +160,7 @@ fn baseType(T: type, name: []const u8) type {
.optional => |opt| return opt.child,
.@"struct" => return f.type,
.int => return f.type,
.array => |ar| return ar.child,
else => @compileError("Unexpected kind " ++ f.name),
},
}
@@ -527,6 +528,9 @@ pub fn Page(comptime template: Template, comptime PageDataType: type) type {
if (opt.child == []const u8) unreachable;
try offsetOptionalItem(opt.child, data, ofs, html, out);
},
.array => |arr| {
for (data) |each| try formatDirective(arr.child, each, ofs, html, out);
},
else => {
std.debug.print("unexpected type {s}\n", .{@typeName(T)});
unreachable;
 
src/template/struct-emit.zig added: 233, removed: 87, total 146
@@ -1,13 +1,3 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const bufPrint = std.fmt.bufPrint;
const indexOf = std.mem.indexOf;
const indexOfPos = std.mem.indexOfPos;
const compiled = @import("comptime_templates");
const Directive = @import("directive.zig");
const Page = @import("page.zig");
 
const AbstTree = struct {
pub const Member = struct {
name: []u8,
@@ -20,16 +10,16 @@ const AbstTree = struct {
}
};
 
parent: ?*AbstTree,
//parent: ?*AbstTree,
alloc: Allocator,
name: []u8,
children: []Member,
child_cap: usize = 0,
 
pub fn init(a: Allocator, name: []const u8, parent: ?*AbstTree) !*AbstTree {
pub fn init(a: Allocator, name: []const u8, _: ?*AbstTree) !*AbstTree {
const self = try a.create(AbstTree);
self.* = .{
.parent = parent,
//.parent = parent,
.alloc = a,
.name = try a.dupe(u8, name),
.children = try a.alloc(Member, 50),
@@ -53,11 +43,11 @@ const AbstTree = struct {
if (std.mem.eql(u8, child.name, name)) {
if (!std.mem.eql(u8, child.kind, kind)) {
std.debug.print("Error: kind mismatch while building ", .{});
var par = self.parent;
while (par != null) {
par = par.?.parent;
std.debug.print("{s}.", .{par.?.name});
}
//var par = self.parent;
//while (par != null) {
// par = par.?.parent;
// std.debug.print("{s}.", .{par.?.name});
//}
 
std.debug.print(
"{s}.{s}\n {s} != {s}\n",
@@ -91,7 +81,7 @@ const AbstTree = struct {
}
};
 
var tree: std.StringHashMap(*AbstTree) = undefined;
var root_tree: std.StringHashMapUnmanaged(*AbstTree) = .{};
 
pub fn main() !void {
var args = std.process.args();
@@ -113,8 +103,6 @@ pub fn main() !void {
);
var wout = wfile.writer();
 
tree = std.StringHashMap(*AbstTree).init(a);
 
for (compiled.data) |tplt| {
const fdata = std.fs.cwd().readFileAlloc(a, tplt.path, 0xffff) catch |err| br: {
if (err != error.FileNotFound) {
@@ -129,26 +117,33 @@ pub fn main() !void {
 
const name = makeStructName(tplt.path);
const this = try AbstTree.init(a, name, null);
const gop = try tree.getOrPut(this.name);
const gop = try root_tree.getOrPut(a, this.name);
if (!gop.found_existing) {
gop.value_ptr.* = this;
}
try emitVars(a, fdata, this);
try emitSourceVars(a, fdata, this);
}
 
var itr = tree.iterator();
var itr = root_tree.iterator();
while (itr.next()) |each| {
//std.debug.print("tree: {}\n", .{each.value_ptr.*});
try wout.print("{}\n", .{each.value_ptr.*});
}
}
 
fn emitVars(a: Allocator, fdata: []const u8, current: *AbstTree) !void {
fn genType(d: Directive) type {
std.debug.print("dir {any}\n", .{d});
 
return @Type(.{});
}
 
fn emitSourceVars(a: Allocator, fdata: []const u8, root: *AbstTree) !void {
var data = fdata;
while (data.len > 0) {
if (indexOf(u8, data, "<")) |offset| {
data = data[offset..];
if (Directive.init(data)) |drct| {
//_ = genType(drct);
data = data[drct.tag_block.len..];
const s_name = makeStructName(drct.noun);
var f_name = makeFieldName(drct.noun);
@@ -159,6 +154,7 @@ fn emitVars(a: Allocator, fdata: []const u8, current: *AbstTree) !void {
 
switch (drct.otherwise) {
.required => {},
.exact => unreachable,
.default => |str| {
kind = try bufPrint(&buffer, ": []const u8 = \"{s}\",\n", .{str});
},
@@ -173,11 +169,11 @@ fn emitVars(a: Allocator, fdata: []const u8, current: *AbstTree) !void {
if (drct.known_type) |kt| {
kind = try bufPrint(&buffer, ": {s},\n", .{@tagName(kt)});
}
try current.append(f_name, kind);
try root.append(f_name, kind);
},
else => |verb| {
var this = try AbstTree.init(a, s_name, current);
const gop = try tree.getOrPut(this.name);
var this = try AbstTree.init(a, s_name, root);
const gop = try root_tree.getOrPut(a, this.name);
if (!gop.found_existing) {
gop.value_ptr.* = this;
} else {
@@ -188,27 +184,32 @@ fn emitVars(a: Allocator, fdata: []const u8, current: *AbstTree) !void {
.variable => unreachable,
.foreach => {
var buffer: [0xFF]u8 = undefined;
const kind = try bufPrint(&buffer, ": []const {s},\n", .{s_name});
try current.append(f_name, kind);
try emitVars(a, drct.tag_block_body.?, this);
if (drct.otherwise == .exact) {
const kind = try bufPrint(&buffer, ": [{}]{s},\n", .{ drct.otherwise.exact, s_name });
try root.append(f_name, kind);
try emitSourceVars(a, drct.tag_block_body.?, this);
} else {
const kind = try bufPrint(&buffer, ": []const {s},\n", .{s_name});
try root.append(f_name, kind);
try emitSourceVars(a, drct.tag_block_body.?, this);
}
},
.split => {
var buffer: [0xFF]u8 = undefined;
const kind = try bufPrint(&buffer, ": []const []const u8,\n", .{});
try current.append(f_name, kind);
try root.append(f_name, kind);
},
.with => {
var buffer: [0xFF]u8 = undefined;
const kind = try bufPrint(&buffer, ": ?{s},\n", .{s_name});
try current.append(f_name, kind);
try emitVars(a, drct.tag_block_body.?, this);
try root.append(f_name, kind);
try emitSourceVars(a, drct.tag_block_body.?, this);
},
.build => {
var buffer: [0xFF]u8 = undefined;
const tmpl_name = makeStructName(drct.otherwise.template.name);
const kind = try bufPrint(&buffer, ": {s},\n", .{tmpl_name});
try current.append(f_name, kind);
//try emitVars(a, drct.otherwise.template.blob, this);
try root.append(f_name, kind);
},
}
},
@@ -315,3 +316,13 @@ fn intToWord(in: u8) []const u8 {
else => unreachable,
};
}
 
const std = @import("std");
const Allocator = std.mem.Allocator;
const eql = std.mem.eql;
const bufPrint = std.fmt.bufPrint;
const indexOf = std.mem.indexOf;
const indexOfPos = std.mem.indexOfPos;
const compiled = @import("comptime_templates");
const Directive = @import("directive.zig");
const Page = @import("page.zig");