diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-25 18:48:31 +0200 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-04-25 18:48:31 +0200 |
| commit | 7ce1e3fdeb407b4485e44da811086a388e6a17b3 (patch) | |
| tree | e732d95f741c40d8390bb54f9ac0aae5d66b0d8b /src | |
| parent | b53f5e086c3fa41083a3a17d73ffa3e8d4fa3872 (diff) | |
feat(lib): returns document instead of html
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/Title.zig | 2 | ||||
| -rw-r--r-- | src/eval/paragraph.zig | 1 | ||||
| -rw-r--r-- | src/parser.zig | 56 | ||||
| -rw-r--r-- | src/root.zig | 13 |
4 files changed, 52 insertions, 20 deletions
diff --git a/src/eval/Title.zig b/src/eval/Title.zig index 56524ad..730d512 100644 --- a/src/eval/Title.zig +++ b/src/eval/Title.zig @@ -38,7 +38,7 @@ fn html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { 5 => "h5", 6 => "h6", else => unreachable, - }); + }); errdefer el.deinit(); try el.appendContent(try self.content.html(alloc)); return el; diff --git a/src/eval/paragraph.zig b/src/eval/paragraph.zig index e56b7ff..468b92e 100644 --- a/src/eval/paragraph.zig +++ b/src/eval/paragraph.zig @@ -9,7 +9,6 @@ pub const Bold = Element.Simple("b"); pub const Italic = Element.Simple("em"); pub const Code = Element.Simple("code"); - pub const Link = struct { link: []const u8, content: Element, diff --git a/src/parser.zig b/src/parser.zig index 163c5a8..b2c0f95 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -11,24 +11,43 @@ pub const Error = error{ FeatureNotSupported, } || Lexer.Error || paragraph.Error || title.Error || link.Error || Allocator.Error; -pub fn parseReader(parent: Allocator, r: *std.io.Reader) ![]const u8 { +pub const Document = struct { + arena: std.heap.ArenaAllocator, + root: []Element, + + pub fn renderHTML(self: @This(), alloc: Allocator) Element.HTML.Error![]const u8 { + var content = try std.ArrayList(u8).initCapacity(alloc, self.root.len * 6); + errdefer content.deinit(alloc); + for (self.root) |it| { + const v = try it.renderHTML(alloc); + defer alloc.free(v); + try content.appendSlice(alloc, v); + } + return content.toOwnedSlice(alloc); + } + + pub fn deinit(self: @This()) void { + self.arena.deinit(); + } +}; + +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![]const u8 { +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![]const u8 { +fn gen(parent: Allocator, l: *Lexer) Error!Document { var arena = std.heap.ArenaAllocator.init(parent); - defer arena.deinit(); const alloc = arena.allocator(); + errdefer arena.deinit(); var elements = try std.ArrayList(Element).initCapacity(alloc, 2); - base: while (l.peek()) |it| { try elements.append(alloc, switch (it.kind) { // block paragraph @@ -42,20 +61,16 @@ fn gen(parent: Allocator, l: *Lexer) Error![]const u8 { else => return Error.FeatureNotSupported, }); } - - var res = try std.ArrayList(u8).initCapacity(parent, elements.items.len); - errdefer res.deinit(parent); - for (elements.items) |it| { - try res.appendSlice(parent, try it.renderHTML(alloc)); - } - return res.toOwnedSlice(parent); + return .{ .root = try elements.toOwnedSlice(alloc), .arena = arena }; } fn doTest(alloc: Allocator, t: []const u8, v: []const u8) !void { const g = try parse(alloc, t); - defer alloc.free(g); - std.testing.expect(std.mem.eql(u8, g, v)) catch |err| { - std.debug.print("{s}\n", .{g}); + defer g.deinit(); + const res = try g.renderHTML(alloc); + defer alloc.free(res); + std.testing.expect(std.mem.eql(u8, res, v)) catch |err| { + std.debug.print("{s}\n", .{res}); return err; }; } @@ -78,3 +93,14 @@ test "parse multilines" { \\hehe , "<h1>title</h1><p>hello world ;3</p><h2>subtitle</h2><p>hehe</p>"); } + +test "multiple render doc" { + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + const alloc = arena.allocator(); + + const g = try parse(alloc, "hello *world*"); + const a = try g.renderHTML(alloc); + const b = try g.renderHTML(alloc); + try std.testing.expect(std.mem.eql(u8, a, b)); +} diff --git a/src/root.zig b/src/root.zig index 32f48ef..185e6b6 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,6 +1,8 @@ const std = @import("std"); const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; const parser = @import("parser.zig"); +pub const Document = parser.Document; pub const Error = parser.Error; inline fn getErrorCode(err: Error) u8 { @@ -45,7 +47,12 @@ var default_alloc: std.mem.Allocator = /// Use typdown_getErrorString to retrieve the string linked with the error code. /// Use parse if you are in Zig. export fn typdown_parse(content: [*:0]const u8, code: *u8) ?[*:0]const u8 { - const res = parse(default_alloc, std.mem.span(content)) catch |err| { + const doc = parse(default_alloc, std.mem.span(content)) catch |err| { + code.* = getErrorCode(err); + return null; + }; + defer doc.deinit(); + const res = doc.renderHTML(default_alloc) catch |err| { code.* = getErrorCode(err); return null; }; @@ -60,11 +67,11 @@ 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![]const u8 { +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)![]const u8 { +pub fn parseReader(alloc: std.mem.Allocator, r: *std.io.Reader) (Error || std.io.Reader.Error)!Document { return parser.parseReader(alloc, r); } |
