diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/Element.zig | 6 | ||||
| -rw-r--r-- | src/eval/Root.zig | 5 | ||||
| -rw-r--r-- | src/eval/Title.zig | 2 | ||||
| -rw-r--r-- | src/eval/blocks.zig | 21 | ||||
| -rw-r--r-- | src/eval/html/Content.zig | 31 | ||||
| -rw-r--r-- | src/eval/html/Element.zig | 55 | ||||
| -rw-r--r-- | src/eval/html/Literal.zig | 19 | ||||
| -rw-r--r-- | src/eval/html/Root.zig | 44 | ||||
| -rw-r--r-- | src/eval/html/Void.zig | 18 | ||||
| -rw-r--r-- | src/eval/list.zig | 10 | ||||
| -rw-r--r-- | src/eval/paragraph.zig | 2 | ||||
| -rw-r--r-- | src/link.zig | 4 | ||||
| -rw-r--r-- | src/parser.zig | 11 | ||||
| -rw-r--r-- | src/root.zig | 13 |
14 files changed, 153 insertions, 88 deletions
diff --git a/src/eval/Element.zig b/src/eval/Element.zig index f93dc1a..d804a0c 100644 --- a/src/eval/Element.zig +++ b/src/eval/Element.zig @@ -48,7 +48,7 @@ pub const Empty = struct { const self: *Self = @ptrCast(@alignCast(context)); var el = try HTML.Root.init(alloc); errdefer el.deinit(); - for (self.content.items) |it| try el.append(try it.html(el.allocator())); + for (self.content.items) |it| el.append(try it.html(el.allocator())); return el.element(); } }; @@ -111,7 +111,9 @@ pub fn Simple(comptime tag: []const u8) type { fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { const self: *Self = @ptrCast(@alignCast(context)); var el = try HTML.Content.init(alloc, tag); - for (self.content.items) |it| try el.append(try it.html(alloc)); + var root = try HTML.Root.init(alloc); + el.content = root.element(); + for (self.content.items) |it| root.append(try it.html(root.allocator())); return el.element(); } }; diff --git a/src/eval/Root.zig b/src/eval/Root.zig index 1834dd0..fc59c1d 100644 --- a/src/eval/Root.zig +++ b/src/eval/Root.zig @@ -44,10 +44,7 @@ pub fn renderHTML(self: *Self, alloc: Allocator) HTML.Error![]const u8 { fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { const self: *Self = @ptrCast(@alignCast(context)); const el = try HTML.Root.init(alloc); - errdefer el.deinit(); if (self.content.items.len == 0) return el.element(); - for (self.content.items) |it| { - try el.append(try it.html(el.allocator())); - } + for (self.content.items) |it| el.append(try it.html(el.allocator())); return el.element(); } diff --git a/src/eval/Title.zig b/src/eval/Title.zig index ebb7fa2..dbb5d38 100644 --- a/src/eval/Title.zig +++ b/src/eval/Title.zig @@ -29,6 +29,6 @@ fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { 6 => "h6", else => unreachable, }); - try el.append(try self.content.html(alloc)); + el.content = try self.content.html(alloc); return el.element(); } diff --git a/src/eval/blocks.zig b/src/eval/blocks.zig index 78e28f6..eb583c7 100644 --- a/src/eval/blocks.zig +++ b/src/eval/blocks.zig @@ -24,8 +24,10 @@ pub const Code = struct { var el = try HTML.Content.init(alloc, "pre"); if (self.attribute) |attr| try el.base.setAttribute("data-code", attr); var code = try HTML.Content.init(alloc, "code"); - for (self.content.items) |it| try code.append(try it.html(alloc)); - try el.append(code.element()); + var root = try HTML.Root.init(alloc); + for (self.content.items) |it| root.append(try it.html(root.allocator())); + code.content = root.element(); + el.content = code.element(); return el.element(); } }; @@ -46,14 +48,17 @@ pub const Figure = struct { return .{ .ptr = self, .vtable = .{ .html = Self.html } }; } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { + fn html(context: *anyopaque, parent: Allocator) HTML.Error!HTML { const self: *Self = @ptrCast(@alignCast(context)); - var el = try HTML.Content.init(alloc, "figure"); - try el.append(try self.content.html(alloc)); + var el = try HTML.Content.init(parent, "figure"); + var root = try HTML.Root.init(parent); + const alloc = root.allocator(); + root.append(try self.content.html(alloc)); + el.content = root.element(); const caption = self.caption orelse return el.element(); var figcap = try HTML.Content.init(alloc, "figcaption"); - try figcap.append(try caption.html(alloc)); - try el.append(figcap.element()); + figcap.content = try caption.html(alloc); + root.append(figcap.element()); return el.element(); } }; @@ -80,7 +85,7 @@ pub const Callout = struct { var el = try HTML.Content.init(alloc, "div"); try el.base.appendClass("callout"); if (self.kind) |kind| try el.base.setAttribute("data-callout", kind); - try el.append(try self.content.html(alloc)); + el.content = try self.content.html(alloc); return el.element(); } }; diff --git a/src/eval/html/Content.zig b/src/eval/html/Content.zig index f21bcf3..aed3a0e 100644 --- a/src/eval/html/Content.zig +++ b/src/eval/html/Content.zig @@ -1,11 +1,15 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const List = std.ArrayList; const Element = @import("Element.zig"); +const Node = Element.Node; const Error = Element.Error; base: Element.Void, -content: List(Element), +content: ?Element = null, +node: Node = .{ + .ptr = undefined, + .vtable = .{ .element = fromNode }, +}, pub const Self = @This(); @@ -18,17 +22,23 @@ pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { .attributes = .init(alloc), .class_list = .init(alloc), }, - .content = try .initCapacity(alloc, 2), }; + v.node.ptr = v; return v; } pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; + return .{ .vtable = .{ .render = render, .node = getNode }, .ptr = self }; } -pub fn append(self: *Self, content: Element) Error!void { - return self.content.append(self.base.alloc, content); +fn getNode(context: *anyopaque) *Node { + const self: *Self = @ptrCast(@alignCast(context)); + return &self.node; +} + +fn fromNode(context: *anyopaque) Element { + const self: *Self = @ptrCast(@alignCast(context)); + return self.element(); } fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { @@ -36,14 +46,15 @@ fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { var base = self.base; const b = try base.element().render(alloc); defer alloc.free(b); - var acc = try List(u8).initCapacity(alloc, b.len + self.content.items.len); + var acc = try std.ArrayList(u8).initCapacity(alloc, b.len * 2); try acc.appendSlice(alloc, b); - for (self.content.items) |it| { - var v = it; - const sub = try v.render(alloc); + + if (self.content) |it| { + const sub = try it.render(alloc); defer alloc.free(sub); try acc.appendSlice(alloc, sub); } + try acc.appendSlice(alloc, "</"); try acc.appendSlice(alloc, base.tag); try acc.append(alloc, '>'); diff --git a/src/eval/html/Element.zig b/src/eval/html/Element.zig index 6bccc0f..6073e6a 100644 --- a/src/eval/html/Element.zig +++ b/src/eval/html/Element.zig @@ -2,28 +2,47 @@ const std = @import("std"); const Arena = std.heap.ArenaAllocator; const Allocator = std.mem.Allocator; const eql = std.mem.eql; -const List = std.ArrayList; const html = @import("html.zig"); pub const Void = @import("Void.zig"); -pub const Content = @import("Content.zig"); +pub const Content = @import("Content.zig"); pub const Literal = @import("Literal.zig"); pub const Root = @import("Root.zig"); pub const Error = html.Error || Allocator.Error; -const Element = @This(); +pub const Node = struct { + ptr: *anyopaque, + vtable: struct { element: *const fn (*anyopaque) Self }, + node: std.DoublyLinkedList.Node = .{}, + + pub fn from(n: *std.DoublyLinkedList.Node) *Node { + const v: *Node = @fieldParentPtr("node", n); + return v; + } + + pub fn element(self: Node) Self { + return self.vtable.element(self.ptr); + } +}; + +const Self = @This(); vtable: struct { render: *const fn (self: *anyopaque, alloc: Allocator) Error![]const u8, + node: *const fn (self: *anyopaque) *Node, }, ptr: *anyopaque, -pub fn render(self: Element, alloc: Allocator) Error![]const u8 { +pub fn render(self: Self, alloc: Allocator) Error![]const u8 { return self.vtable.render(self.ptr, alloc); } -fn doTest(alloc: Allocator, el: Element, exp: []const u8) !void { +pub fn node(self: Self) *Node { + return self.vtable.node(self.ptr); +} + +fn doTest(alloc: Allocator, el: Self, exp: []const u8) !void { const got = try el.render(alloc); defer alloc.free(got); std.testing.expect(eql(u8, got, exp)) catch |err| { @@ -54,33 +73,21 @@ test "content" { const alloc = arena.allocator(); var p = try Content.init(alloc, "p"); + var root = try Root.init(alloc); + p.content = root.element(); var content = try Literal.init(alloc, "hello world"); - try p.append(content.element()); + root.append(content.element()); try doTest(alloc, content.element(), "hello world"); try doTest(alloc, p.element(), "<p>hello world</p>"); var div = try Content.init(alloc, "div"); + var rootDiv = try Root.init(alloc); + div.content = rootDiv.element(); try div.base.appendClass("foo-bar"); - try div.append(p.element()); - try div.append((try Void.init(alloc, "br")).element()); + rootDiv.append(p.element()); + rootDiv.append((try Void.init(alloc, "br")).element()); try doTest(alloc, div.element(), "<div class=\"foo-bar\"><p>hello world</p><br></div>"); } - -test "root" { - const root = try Root.init(std.testing.allocator); - defer root.deinit(); - const alloc = root.allocator(); - - var p = try Content.init(alloc, "p"); - var content = try Literal.init(alloc, "hello world"); - try p.append(content.element()); - try root.append(p.element()); - - var br = try Void.init(alloc, "br"); - try root.append(br.element()); - - try doTest(alloc, root.element(), "<p>hello world</p><br>"); -} diff --git a/src/eval/html/Literal.zig b/src/eval/html/Literal.zig index d3b697f..ccad004 100644 --- a/src/eval/html/Literal.zig +++ b/src/eval/html/Literal.zig @@ -1,22 +1,37 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const List = std.ArrayList; const html = @import("html.zig"); const Element = @import("Element.zig"); +const Node = Element.Node; const Error = Element.Error; literal: []const u8, +node: Node = .{ + .ptr = undefined, + .vtable = .{ .element = fromNode }, +}, const Self = @This(); pub fn init(alloc: Allocator, literal: []const u8) Error!*Element.Literal { const v = try alloc.create(Self); v.* = .{ .literal = try html.escape(alloc, literal) }; + v.node.ptr = v; return v; } pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; + return .{ .vtable = .{ .render = render, .node = getNode }, .ptr = self }; +} + +fn getNode(context: *anyopaque) *Node { + const self: *Self = @ptrCast(@alignCast(context)); + return &self.node; +} + +fn fromNode(context: *anyopaque) Element { + const self: *Self = @ptrCast(@alignCast(context)); + return self.element(); } fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { diff --git a/src/eval/html/Root.zig b/src/eval/html/Root.zig index 80c556a..440667a 100644 --- a/src/eval/html/Root.zig +++ b/src/eval/html/Root.zig @@ -1,24 +1,25 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const Arena = std.heap.ArenaAllocator; -const List = std.ArrayList; const Element = @import("Element.zig"); +const Node = Element.Node; const Error = Element.Error; -content: List(Element), +content: std.DoublyLinkedList = .{}, arena: Arena, +node: Node = .{ + .ptr = undefined, + .vtable = .{ .element = fromNode }, +}, const Self = @This(); pub fn init(parent: Allocator) Error!*Self { - var s = Self{ - .content = undefined, - .arena = .init(parent), - }; + var s = Self{ .arena = .init(parent) }; var alloc = s.arena.allocator(); - s.content = try .initCapacity(alloc, 2); const v = try alloc.create(Self); v.* = s; + v.node.ptr = v; return v; } @@ -27,28 +28,39 @@ pub fn deinit(self: *Self) void { } pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render, }, .ptr = self }; + return .{ .vtable = .{ + .render = render, + .node = getNode, + }, .ptr = self }; } pub fn allocator(self: *Self) Allocator { return self.arena.allocator(); } -pub fn append(self: *Self, el: Element) Error!void { - try self.content.append(self.allocator(), el); +pub fn append(self: *Self, el: Element) void { + self.content.append(&el.node().node); +} + +fn getNode(context: *anyopaque) *Node { + const self: *Self = @ptrCast(@alignCast(context)); + return &self.node; +} + +fn fromNode(context: *anyopaque) Element { + const self: *Self = @ptrCast(@alignCast(context)); + return self.element(); } fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { const self: *Self = @ptrCast(@alignCast(context)); - if (self.content.items.len == 0) return ""; - var acc = try List(u8).initCapacity(alloc, self.content.items.len); + if (self.content.first == null) return ""; + var acc = try std.ArrayList(u8).initCapacity(alloc, 8); errdefer acc.deinit(alloc); var arena = Arena.init(alloc); defer arena.deinit(); - for (self.content.items) |it| { - const res = try it.render(arena.allocator()); - try acc.appendSlice(alloc, res); - } + var v = self.content.first; + while (v) |it| : (v = it.next) try acc.appendSlice(alloc, try Node.from(it).element().render(arena.allocator())); return acc.toOwnedSlice(alloc); } diff --git a/src/eval/html/Void.zig b/src/eval/html/Void.zig index ec35fc7..99bc923 100644 --- a/src/eval/html/Void.zig +++ b/src/eval/html/Void.zig @@ -3,12 +3,17 @@ const Allocator = std.mem.Allocator; const List = std.ArrayList; const html = @import("html.zig"); const Element = @import("Element.zig"); +const Node = Element.Node; const Error = Element.Error; alloc: Allocator, tag: []const u8, attributes: std.StringArrayHashMap([]const u8), class_list: std.BufSet, +node: Node = .{ + .ptr = undefined, + .vtable = .{ .element = fromNode }, +}, pub const Self = @This(); @@ -20,11 +25,12 @@ pub fn init(alloc: Allocator, tag: []const u8) Error!*Self { .attributes = .init(alloc), .class_list = .init(alloc), }; + v.node.ptr = v; return v; } pub fn element(self: *Self) Element { - return .{ .vtable = .{ .render = Self.render }, .ptr = self }; + return .{ .vtable = .{ .render = render, .node = getNode }, .ptr = self }; } pub fn setAttribute(self: *Self, k: []const u8, v: []const u8) Error!void { @@ -51,6 +57,16 @@ pub fn removeClass(self: *Self, v: []const u8) void { self.class_list.remove(v); } +fn getNode(context: *anyopaque) *Node { + const self: *Self = @ptrCast(@alignCast(context)); + return &self.node; +} + +fn fromNode(context: *anyopaque) Element { + const self: *Self = @ptrCast(@alignCast(context)); + return self.element(); +} + fn render(context: *anyopaque, alloc: Allocator) Error![]const u8 { const self: *Self = @ptrCast(@alignCast(context)); const attr = try renderAttribute(alloc, &self.attributes, &self.class_list); diff --git a/src/eval/list.zig b/src/eval/list.zig index 06b7af7..2ba136a 100644 --- a/src/eval/list.zig +++ b/src/eval/list.zig @@ -21,13 +21,15 @@ fn List(comptime tag: []const u8) type { return .{ .ptr = self, .vtable = .{ .html = html } }; } - fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { + fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { const self: *Self = @ptrCast(@alignCast(context)); var el = try HTML.Content.init(alloc, tag); + var root = try HTML.Root.init(alloc); + el.content = root.element(); for (self.content.items) |it| { - var li = try HTML.Content.init(alloc, "li"); - try li.append(try it.html(alloc)); - try el.append(li.element()); + var li = try HTML.Content.init(root.allocator(), "li"); + li.content = try it.html(root.allocator()); + root.append(li.element()); } return el.element(); } diff --git a/src/eval/paragraph.zig b/src/eval/paragraph.zig index e57f995..8609e79 100644 --- a/src/eval/paragraph.zig +++ b/src/eval/paragraph.zig @@ -32,7 +32,7 @@ pub const Link = struct { fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { const self: *Self = @ptrCast(@alignCast(context)); var el = try HTML.Content.init(alloc, "a"); - try el.append(try self.content.html(alloc)); + el.content = try self.content.html(alloc); try el.base.setAttribute("href", self.link); if (self.target) |target| try el.base.setAttribute("target", target); return el.element(); diff --git a/src/link.zig b/src/link.zig index 07cbf95..612ee8c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -34,9 +34,9 @@ pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { if (href.kind != .literal) return Error.InvalidLink; const finisher = l.next() orelse return Error.InvalidLink; if (!finisher.equals(.link, ")")) return Error.InvalidLink; - const in: Element = if (el.content.items.len > 0) + const in: Element = if (el.content.items.len > 0) el.element() - else + else (try Element.Literal.init(alloc, href.content)).element(); return (try Link.init(alloc, in, href.content)).element(); } diff --git a/src/parser.zig b/src/parser.zig index db91641..72b803f 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -21,21 +21,21 @@ pub const Error = error{FeatureNotSupported} || callout.Error || Allocator.Error; -pub const Document = *Element.Root; +pub const Document = Element.Root; -pub fn parseReader(parent: Allocator, r: *std.io.Reader) !Document { +pub fn parseReader(parent: Allocator, r: *std.io.Reader) !*Document { var l = try Lexer.initReader(parent, r); defer parent.free(l.iter.bytes); return gen(parent, &l); } -pub fn parse(parent: Allocator, content: []const u8) Error!Document { +pub fn parse(parent: Allocator, content: []const u8) Error!*Document { var l = try Lexer.init(content); return gen(parent, &l); } -fn gen(parent: Allocator, l: *Lexer) Error!Document { - var root = try Element.Root.init(parent); +fn gen(parent: Allocator, l: *Lexer) Error!*Document { + var root = try Document.init(parent); errdefer root.deinit(); const alloc = root.allocator(); base: while (l.peek()) |it| { @@ -75,6 +75,7 @@ fn doTest(alloc: Allocator, t: []const u8, v: []const u8) !void { test "parse multilines" { const alloc = std.testing.allocator; + try doTest(alloc, "hello world", "<p>hello world</p>"); try doTest(alloc, \\hello \\world diff --git a/src/root.zig b/src/root.zig index 919c3ee..6fd15f5 100644 --- a/src/root.zig +++ b/src/root.zig @@ -4,6 +4,10 @@ const Allocator = std.mem.Allocator; const parser = @import("parser.zig"); pub const Document = parser.Document; pub const Error = parser.Error; +/// Parse the content. +/// +/// Use typdown_parse if you are not in Zig. +pub const parse = parser.parse; inline fn getErrorCode(err: Error) u8 { return switch (err) { @@ -70,14 +74,7 @@ export fn typdown_parse(content: [*:0]const u8, code: *u8) ?[*:0]const u8 { }; } -/// Parse the content. -/// -/// Use parse if you are not in Zig. -pub fn parse(alloc: std.mem.Allocator, content: []const u8) Error!Document { - return parser.parse(alloc, content); -} - -pub fn parseReader(alloc: std.mem.Allocator, r: *std.io.Reader) (Error || std.io.Reader.Error)!Document { +pub fn parseReader(alloc: std.mem.Allocator, r: *std.io.Reader) (Error || std.io.Reader.Error)!*Document { return parser.parseReader(alloc, r); } |
