diff options
| -rw-r--r-- | build.zig | 2 | ||||
| -rw-r--r-- | src/data/test_block_1.typ | 3 | ||||
| -rw-r--r-- | src/data/test_block_2.typ | 3 | ||||
| -rw-r--r-- | src/data/test_block_3.typ | 3 | ||||
| -rw-r--r-- | src/math.zig | 56 | ||||
| -rw-r--r-- | src/paragraph.zig | 31 | ||||
| -rw-r--r-- | src/parser.zig | 3 | ||||
| -rw-r--r-- | src/root.zig | 2 | ||||
| -rw-r--r-- | src/testing.zig | 32 |
9 files changed, 104 insertions, 31 deletions
@@ -93,7 +93,7 @@ pub fn build(b: *std.Build) void { } fn generateSVG(b: *std.Build, step: *std.Build.Step) !void { - var dir = try std.fs.cwd().openDir(b.build_root.path.?, .{ .iterate = true }); + var dir = try b.build_root.handle.openDir("src/data", .{ .iterate = true }); defer dir.close(); var iter = dir.iterate(); while (try iter.next()) |it| { diff --git a/src/data/test_block_1.typ b/src/data/test_block_1.typ new file mode 100644 index 0000000..582ab60 --- /dev/null +++ b/src/data/test_block_1.typ @@ -0,0 +1,3 @@ +#import "_base.typ": * + +#display[$ x $] diff --git a/src/data/test_block_2.typ b/src/data/test_block_2.typ new file mode 100644 index 0000000..cce19c2 --- /dev/null +++ b/src/data/test_block_2.typ @@ -0,0 +1,3 @@ +#import "_base.typ": * + +#display[$ x^2 $] diff --git a/src/data/test_block_3.typ b/src/data/test_block_3.typ new file mode 100644 index 0000000..04dae08 --- /dev/null +++ b/src/data/test_block_3.typ @@ -0,0 +1,3 @@ +#import "_base.typ": * + +#display[$ forall x in RR, quad f(x) = x^2 $] diff --git a/src/math.zig b/src/math.zig new file mode 100644 index 0000000..e28b3b9 --- /dev/null +++ b/src/math.zig @@ -0,0 +1,56 @@ +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 testing = @import("testing.zig"); +const doTest = testing.doMath; +const doTestError = testing.doError; + +pub const Error = error{InvalidMathBlock} || Allocator.Error; + +pub fn parse(alloc: Allocator, l: *Lexer) Error!Element { + _ = l.next(); + const beg = l.next() orelse return Error.InvalidMathBlock; + if (!beg.kind.isDelimiter()) return Error.InvalidMathBlock; + const math = try Element.Math.Block.init(alloc); + var acc = try std.ArrayList(u8).initCapacity(alloc, 2); + while (l.next()) |it| { + if (it.kind == .math_block) return Error.InvalidMathBlock; + try acc.appendSlice(alloc, it.content); + // restore modifications done by the lexer + if (it.kind.requiresSpace()) + try acc.append(alloc, ' '); + if (it.kind.isDelimiter()) { + const next = l.peek() orelse return Error.InvalidMathBlock; + if (next.kind == .math_block) break; + } + } + var end = l.next() orelse return Error.InvalidMathBlock; + if (end.kind != .math_block) return Error.InvalidMathBlock; + const el = try Element.Figure.init(alloc, math.element()); + math.content = try acc.toOwnedSlice(alloc); + end = l.next() orelse return el.element(); + if (!end.kind.isDelimiter()) return Error.InvalidMathBlock; + return el.element(); +} + +test { + const alloc = std.testing.allocator; + + try doTest(parse, alloc, + \\$$$ + \\x + \\$$$ + , "<figure>" ++ @embedFile("data/test_block_1.svg") ++ "</figure>"); + try doTest(parse, alloc, + \\$$$ + \\x^2 + \\$$$ + , "<figure>" ++ @embedFile("data/test_block_2.svg") ++ "</figure>"); + try doTest(parse, alloc, + \\$$$ + \\forall x in RR, quad f(x) = x^2 + \\$$$ + , "<figure>" ++ @embedFile("data/test_block_3.svg") ++ "</figure>"); +} diff --git a/src/paragraph.zig b/src/paragraph.zig index f4a6619..5395082 100644 --- a/src/paragraph.zig +++ b/src/paragraph.zig @@ -9,6 +9,7 @@ const link = @import("link.zig"); const content = @import("content.zig"); const testing = @import("testing.zig"); const doTest = testing.do; +const doTestMath = testing.doMath; const doTestError = testing.doError; pub const Error = content.Error || link.Error || Allocator.Error; @@ -40,32 +41,6 @@ pub fn parseLine(alloc: Allocator, l: *Lexer) Error!Element { return line.element(); } -fn doTestMath(parent: Allocator, t: []const u8, v: []const u8) !void { - if (@import("config").short) return; - var arena = std.heap.ArenaAllocator.init(parent); - defer arena.deinit(); - var alloc = arena.allocator(); - - var l = try Lexer.init(t); - var p = try parse(alloc, &l); - const g = try p.renderHTML(alloc); - defer alloc.free(g); - try std.testing.expect(blk: { - var g_iter = std.mem.splitSequence(u8, g, " "); - var v_iter = std.mem.splitSequence(u8, v, " "); - while (g_iter.next()) |g_it| { - const v_it = v_iter.next() orelse break :blk false; - if ((std.mem.startsWith(u8, g_it, "xlink:href=") and std.mem.startsWith(u8, g_it, "xlink:href")) or - (std.mem.startsWith(u8, g_it, "id=") and std.mem.startsWith(u8, v_it, "id="))) continue; - if (!std.mem.eql(u8, g_it, v_it)) { - std.debug.print("not the same: {s} vs {s}", .{ g_it, v_it }); - break :blk false; - } - } - break :blk v_iter.next() == null; - }); -} - test "parse paragraphs" { const alloc = std.testing.allocator; @@ -76,8 +51,8 @@ test "parse paragraphs" { try doTest(parse, alloc, "[](bar)", "<p><a href=\"bar\">bar</a></p>"); try doTest(parse, alloc, "[foo](bar)", "<p><a href=\"bar\">foo</a></p>"); try doTest(parse, alloc, "hello [foo](bar) world", "<p>hello <a href=\"bar\">foo</a> world</p>"); - try doTestMath(alloc, "$x$", "<p>" ++ @embedFile("data/test_content_1.svg") ++ "</p>"); - try doTestMath(alloc, "$x^2$", "<p>" ++ @embedFile("data/test_content_2.svg") ++ "</p>"); + try doTestMath(parse, alloc, "$x$", "<p>" ++ @embedFile("data/test_content_1.svg") ++ "</p>"); + try doTestMath(parse, alloc, "$x^2$", "<p>" ++ @embedFile("data/test_content_2.svg") ++ "</p>"); try doTestError(parse, alloc, "hello *world", Error.ModifierNotClosed); try doTestError(parse, alloc, "hello *wo_rld*", Error.ModifierNotClosed); diff --git a/src/parser.zig b/src/parser.zig index e7a3aff..3dba456 100644 --- a/src/parser.zig +++ b/src/parser.zig @@ -10,6 +10,7 @@ const list = @import("list.zig"); const code = @import("code.zig"); const callout = @import("callout.zig"); const quote = @import("quote.zig"); +const math = @import("math.zig"); pub const Error = error{FeatureNotSupported} || Lexer.Error || @@ -21,6 +22,7 @@ pub const Error = error{FeatureNotSupported} || code.Error || callout.Error || quote.Error || + math.Error || Allocator.Error; pub const Document = Element.Root; @@ -49,6 +51,7 @@ fn gen(parent: Allocator, l: *Lexer) Error!*Document { .image => try link.parseImage(alloc, l), .code_block => try code.parse(alloc, l), .quote => try quote.parse(alloc, l), + .math_block => try math.parse(alloc, l), .weak_delimiter, .strong_delimiter => { l.consume(); continue :base; diff --git a/src/root.zig b/src/root.zig index 6fd15f5..f7899a7 100644 --- a/src/root.zig +++ b/src/root.zig @@ -21,6 +21,7 @@ inline fn getErrorCode(err: Error) u8 { Error.InvalidImage => 8, Error.InvalidCodeBlock => 9, Error.InvalidCallout => 10, + Error.InvalidMathBlock => 11, }; } @@ -37,6 +38,7 @@ export fn typdown_getErrorString(code: u8) [*:0]const u8 { 8 => "invalid image", 9 => "invalid code block", 10 => "invalid callout", + 11 => "invalid math block", else => unreachable, }; } diff --git a/src/testing.zig b/src/testing.zig index 21fb4fd..143d6ca 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -4,7 +4,9 @@ const Lexer = @import("lexer/Lexer.zig"); const Element = @import("eval/Element.zig"); const parser = @import("parser.zig"); -pub fn do(comptime parse: fn (Allocator, *Lexer) parser.Error!Element, parent: Allocator, t: []const u8, v: []const u8) !void { +const ParserFn = fn (Allocator, *Lexer) parser.Error!Element; + +pub fn do(comptime parse: ParserFn, parent: Allocator, t: []const u8, v: []const u8) !void { var arena = std.heap.ArenaAllocator.init(parent); defer arena.deinit(); var alloc = arena.allocator(); @@ -19,7 +21,7 @@ pub fn do(comptime parse: fn (Allocator, *Lexer) parser.Error!Element, parent: A }; } -pub fn doError(comptime parse: fn (Allocator, *Lexer) parser.Error!Element, parent: Allocator, t: []const u8, err: parser.Error) !void { +pub fn doError(comptime parse: ParserFn, parent: Allocator, t: []const u8, err: parser.Error) !void { var arena = std.heap.ArenaAllocator.init(parent); defer arena.deinit(); @@ -32,3 +34,29 @@ pub fn doError(comptime parse: fn (Allocator, *Lexer) parser.Error!Element, pare }; return error.ExpectingError; } + +pub fn doMath(comptime parse: ParserFn, parent: Allocator, t: []const u8, v: []const u8) !void { + if (@import("config").short) return; + var arena = std.heap.ArenaAllocator.init(parent); + defer arena.deinit(); + var alloc = arena.allocator(); + + var l = try Lexer.init(t); + var p = try parse(alloc, &l); + const g = try p.renderHTML(alloc); + defer alloc.free(g); + try std.testing.expect(blk: { + var g_iter = std.mem.splitSequence(u8, g, " "); + var v_iter = std.mem.splitSequence(u8, v, " "); + while (g_iter.next()) |g_it| { + const v_it = v_iter.next() orelse break :blk false; + if ((std.mem.startsWith(u8, g_it, "xlink:href=") and std.mem.startsWith(u8, g_it, "xlink:href")) or + (std.mem.startsWith(u8, g_it, "id=") and std.mem.startsWith(u8, v_it, "id="))) continue; + if (!std.mem.eql(u8, g_it, v_it)) { + std.debug.print("not the same: {s} vs {s}", .{ g_it, v_it }); + break :blk false; + } + } + break :blk v_iter.next() == null; + }); +} |
