diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/Element.zig | 1 | ||||
| -rw-r--r-- | src/eval/blocks.zig | 41 | ||||
| -rw-r--r-- | src/lexer/Token.zig | 2 | ||||
| -rw-r--r-- | src/paragraph.zig | 2 | ||||
| -rw-r--r-- | src/parser.zig | 5 | ||||
| -rw-r--r-- | src/quote.zig | 58 |
6 files changed, 106 insertions, 3 deletions
diff --git a/src/eval/Element.zig b/src/eval/Element.zig index 4600abf..73dfb94 100644 --- a/src/eval/Element.zig +++ b/src/eval/Element.zig @@ -10,6 +10,7 @@ const blocks = @import("blocks.zig"); pub const Code = blocks.Code; pub const Figure = blocks.Figure; pub const Callout = blocks.Callout; +pub const Quote = blocks.Quote; pub const Node = struct { ptr: *anyopaque, diff --git a/src/eval/blocks.zig b/src/eval/blocks.zig index cbb95b1..32f2de3 100644 --- a/src/eval/blocks.zig +++ b/src/eval/blocks.zig @@ -138,3 +138,44 @@ pub const Callout = struct { return el.element(); } }; + +pub const Quote = struct { + content: Element, + attribution: ?Element = null, + node: Node = .{ + .ptr = undefined, + .vtable = .{ .element = fromNode }, + }, + + const Self = @This(); + + pub fn init(alloc: Allocator, content: Element) !*Self { + const v = try alloc.create(Self); + v.* = .{ .content = content }; + v.node.ptr = v; + return v; + } + + pub fn element(self: *Self) Element { + return .{ .ptr = self, .vtable = .{ .html = html, .node = getNode } }; + } + + 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 html(context: *anyopaque, alloc: Allocator) HTML.Error!HTML { + const self: *Self = @ptrCast(@alignCast(context)); + const quote = try Element.Simple("blockquote").init(alloc); + quote.content = self.content; + var el = try Figure.init(alloc, quote.element()); + el.caption = self.attribution; + return try el.element().html(alloc); + } +}; diff --git a/src/lexer/Token.zig b/src/lexer/Token.zig index 3162869..8ac5d34 100644 --- a/src/lexer/Token.zig +++ b/src/lexer/Token.zig @@ -28,7 +28,7 @@ pub const Kind = enum { }; } - pub inline fn isPar(self: @This()) bool { + pub inline fn isInParagraph(self: @This()) bool { return switch (self) { .literal, .link, .code, .math, .bold, .italic, .ref => true, else => false, diff --git a/src/paragraph.zig b/src/paragraph.zig index f8c3727..2e5383a 100644 --- a/src/paragraph.zig +++ b/src/paragraph.zig @@ -22,7 +22,7 @@ pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { .weak_delimiter => { l.consume(); const future = l.peek() orelse return el.element(); - if (!future.kind.isPar()) return el.element(); + if (!future.kind.isInParagraph()) return el.element(); root.append((try Element.Literal.init(alloc, " ")).element()); }, else => root.append(try parseLine(alloc, l)), diff --git a/src/parser.zig b/src/parser.zig index c8f1c81..e7a3aff 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -9,6 +9,7 @@ const link = @import("link.zig"); const list = @import("list.zig"); const code = @import("code.zig"); const callout = @import("callout.zig"); +const quote = @import("quote.zig"); pub const Error = error{FeatureNotSupported} || Lexer.Error || @@ -19,6 +20,7 @@ pub const Error = error{FeatureNotSupported} || link.ImageError || code.Error || callout.Error || + quote.Error || Allocator.Error; pub const Document = Element.Root; @@ -46,13 +48,14 @@ fn gen(parent: Allocator, l: *Lexer) Error!*Document { .list_unordored => try list.parseUnordored(alloc, l), .image => try link.parseImage(alloc, l), .code_block => try code.parse(alloc, l), + .quote => try quote.parse(alloc, l), .weak_delimiter, .strong_delimiter => { l.consume(); continue :base; }, else => // block paragraph - if (it.kind.isPar()) + if (it.kind.isInParagraph()) try paragraph.parse(alloc, l) else return Error.FeatureNotSupported, diff --git a/src/quote.zig b/src/quote.zig new file mode 100644 index 0000000..8c24c7e --- /dev/null +++ b/src/quote.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const Token = @import("lexer/Token.zig"); +const Lexer = @import("lexer/Lexer.zig"); +const Element = @import("eval/Element.zig"); +const paragraph = @import("paragraph.zig"); +const testing = @import("testing.zig"); +const doTest = testing.do; +const doTestError = testing.doError; + +pub const Error = paragraph.Error || Allocator.Error; + +pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { + const root = try Element.Root.init(alloc); + while (l.peek()) |next| switch (next.kind) { + .quote => { + l.consume(); + continue; + }, + .weak_delimiter => { + l.consume(); + if (l.peek()) |it| if (it.kind != .quote) break; + root.append((try Element.Literal.init(alloc, " ")).element()); + continue; + }, + .strong_delimiter => break, + else => root.append(try paragraph.parseLine(alloc, l)), + }; + const el = try Element.Quote.init(alloc, root.element()); + const v = l.peek() orelse return el.element(); + if (v.kind == .strong_delimiter) { + l.consume(); + return el.element(); + } + const attr = try paragraph.parse(alloc, l); + const p_el: *Element.paragraph.Block = @ptrCast(@alignCast(attr.ptr)); + el.attribution = (try p_el.toRoot(alloc)).element(); + return el.element(); +} + +test { + const alloc = std.testing.allocator; + + try doTest(parse, alloc, "> hello world", "<figure><blockquote>hello world</blockquote></figure>"); + try doTest(parse, alloc, ">hello world", "<figure><blockquote>hello world</blockquote></figure>"); + try doTest(parse, alloc, "> hello world", "<figure><blockquote>hello world</blockquote></figure>"); + + try doTest(parse, alloc, + \\> hello + \\>world + , "<figure><blockquote>hello world</blockquote></figure>"); + try doTest(parse, alloc, + \\> hello + \\>world + \\attribution sur + \\plusieurs lignes + , "<figure><blockquote>hello world</blockquote><figcaption>attribution sur plusieurs lignes</figcaption></figure>"); +} |
