aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/data/test_block_1.typ3
-rw-r--r--src/data/test_block_2.typ3
-rw-r--r--src/data/test_block_3.typ3
-rw-r--r--src/math.zig56
-rw-r--r--src/paragraph.zig31
-rw-r--r--src/parser.zig3
-rw-r--r--src/root.zig2
-rw-r--r--src/testing.zig32
8 files changed, 103 insertions, 30 deletions
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;
+ });
+}