aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eval/Title.zig2
-rw-r--r--src/eval/paragraph.zig1
-rw-r--r--src/parser.zig56
-rw-r--r--src/root.zig13
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);
}