From cf6f08d1602841f8c337dc64365e2dcde1e4c528 Mon Sep 17 00:00:00 2001 From: Anhgelus Morhtuuzh Date: Sun, 19 Apr 2026 17:55:42 +0200 Subject: fix(ast): error if title contains modifier --- examples/main.c | 1 + src/paragraph.zig | 26 ++++++++++++++++++++++---- src/parser.zig | 3 +++ src/root.zig | 2 ++ src/title.zig | 5 ++++- 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/examples/main.c b/examples/main.c index 1409bd5..11534b3 100644 --- a/examples/main.c +++ b/examples/main.c @@ -20,6 +20,7 @@ int main() { // invalid foo("hello *world"); + foo("hello world :::"); foo("# hello :::"); return 0; } diff --git a/src/paragraph.zig b/src/paragraph.zig index 8cdaeb0..222f376 100644 --- a/src/paragraph.zig +++ b/src/paragraph.zig @@ -5,10 +5,11 @@ const Lexer = @import("lexer/Lexer.zig"); const Element = @import("dom/Element.zig"); const parser = @import("parser.zig"); -pub const Error = error{ModifierNotClosed} || Lexer.Error; +pub const Error = error{ModifierNotClosed, IllegalPlacement} || Lexer.Error; pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { var el = try Element.init(alloc, .content, "p"); + errdefer el.deinit(); while (l.nextKind()) |kind| { switch (kind) { // because nextKind returns only an hint for the next rune @@ -22,14 +23,30 @@ pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { else => return el, } }, - else => try el.appendContent(try parseContent(alloc, l)), + else => try el.appendContent(try parseLine(alloc, l)), } } return el; } -pub fn parseContent(alloc: Allocator, l: *Lexer) Error!Element { +pub fn parseLine(alloc: Allocator, l: *Lexer) Error!Element { var content = Element.initEmpty(alloc); + errdefer content.deinit(); + while (l.nextKind()) |kind| { + switch (kind) { + .weak_delimiter, .strong_delimiter => return content, + else => { + const el = try parseContent(alloc, l); + try content.appendContent(el); + }, + } + } + return content; +} + +fn parseContent(alloc: Allocator, l: *Lexer) Error!Element { + var content = Element.initEmpty(alloc); + errdefer content.deinit(); const v = (try l.next(alloc)).?; switch (v.kind) { .literal => { @@ -39,13 +56,14 @@ pub fn parseContent(alloc: Allocator, l: *Lexer) Error!Element { .bold => try content.appendContent(try parseModifier(alloc, l, .bold, "b")), .italic => try content.appendContent(try parseModifier(alloc, l, .italic, "em")), .code => try content.appendContent(try parseModifier(alloc, l, .code, "code")), - else => unreachable, + else => return Error.IllegalPlacement, } return content; } fn parseModifier(alloc: Allocator, l: *Lexer, knd: Lexed.Kind, tag: []const u8) Error!Element { var el = try Element.init(alloc, .content, tag); + errdefer el.deinit(); while (l.nextKind()) |it| { if (it == knd) { // consuming the finisher diff --git a/src/parser.zig b/src/parser.zig index af571d4..442d823 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -74,6 +74,7 @@ test "parse paragraphs" { try doTestError(alloc, "hello *world", Error.ModifierNotClosed); try doTestError(alloc, "hello *wo_rld*", Error.ModifierNotClosed); try doTestError(alloc, "*hell*o *wo_rld*", Error.ModifierNotClosed); + try doTestError(alloc, "hello ::: world", Error.IllegalPlacement); } test "parse title" { @@ -85,6 +86,8 @@ test "parse title" { try doTest(alloc, "## hey", "

hey

"); try doTest(alloc, "### hey", "

hey

"); + try doTest(alloc, "# hello *world*", "

hello world

"); + try doTest(alloc, \\# title \\hello world ;3 diff --git a/src/root.zig b/src/root.zig index bd183ff..830ba1c 100644 --- a/src/root.zig +++ b/src/root.zig @@ -9,6 +9,7 @@ fn getErrorCode(err: Error) u8 { Error.FeatureNotSupported => 3, Error.ModifierNotClosed => 4, Error.InvalidTitleContent => 5, + Error.IllegalPlacement => 6, }; } @@ -20,6 +21,7 @@ export fn getErrorString(code: u8) [*:0]const u8 { 3 => "feature not supported", 4 => "modifier not closed", 5 => "invalid title content", + 6 => "illegal placement", else => unreachable, }; } diff --git a/src/title.zig b/src/title.zig index d9b5612..de88c67 100644 --- a/src/title.zig +++ b/src/title.zig @@ -18,7 +18,10 @@ pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { 6 => "h6", else => unreachable, }); - try el.appendContent(try paragraph.parseContent(alloc, l)); + try el.appendContent(paragraph.parseLine(alloc, l) catch |err| switch (err) { + paragraph.Error.IllegalPlacement => return Error.InvalidTitleContent, + else => return err, + }); v = (try l.next(alloc)) orelse return el; if (!v.kind.isDelimiter()) return Error.InvalidTitleContent; v.deinit(); -- cgit v1.2.3