aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/content.zig15
-rw-r--r--src/data/.gitignore1
-rw-r--r--src/data/test_content_1.typ12
-rw-r--r--src/data/test_content_2.typ12
-rw-r--r--src/eval/Element.zig5
-rw-r--r--src/eval/html/Literal.zig7
-rw-r--r--src/eval/math.zig108
-rw-r--r--src/eval/template_math_block.typ12
-rw-r--r--src/eval/template_math_content.typ12
-rw-r--r--src/paragraph.zig3
-rw-r--r--src/quote.zig4
-rw-r--r--src/root.zig1
-rw-r--r--src/typst.zig11
13 files changed, 189 insertions, 14 deletions
diff --git a/src/content.zig b/src/content.zig
index fe1b844..067c44f 100644
--- a/src/content.zig
+++ b/src/content.zig
@@ -22,6 +22,7 @@ pub fn parse(alloc: Allocator, l: *Lexer) Error!Element {
.bold => content.append(try parseModifier(alloc, l, .bold, "b")),
.italic => content.append(try parseModifier(alloc, l, .italic, "em")),
.code => content.append(try parseModifier(alloc, l, .code, "code")),
+ .math => content.append(try parseMath(alloc, l)),
else => return Error.IllegalPlacement,
}
return content.element();
@@ -42,3 +43,17 @@ fn parseModifier(alloc: Allocator, l: *Lexer, knd: Token.Kind, comptime tag: []c
}
return Error.ModifierNotClosed;
}
+
+fn parseMath(alloc: Allocator, l: *Lexer) Error!Element {
+ const el = try Element.Math.Content.init(alloc);
+ var acc = try std.ArrayList(u8).initCapacity(alloc, 2);
+ while (l.next()) |it| {
+ if (it.kind == .math) {
+ el.content = try acc.toOwnedSlice(alloc);
+ return el.element();
+ }
+ if (it.kind.isDelimiter()) return Error.ModifierNotClosed;
+ try acc.appendSlice(alloc, it.content);
+ }
+ return Error.ModifierNotClosed;
+}
diff --git a/src/data/.gitignore b/src/data/.gitignore
new file mode 100644
index 0000000..756b22f
--- /dev/null
+++ b/src/data/.gitignore
@@ -0,0 +1 @@
+*.svg
diff --git a/src/data/test_content_1.typ b/src/data/test_content_1.typ
new file mode 100644
index 0000000..3adf59a
--- /dev/null
+++ b/src/data/test_content_1.typ
@@ -0,0 +1,12 @@
+#set page(
+ fill: none,
+ margin: 2pt,
+);
+
+#let display(body) = context {
+ let m = measure(body)
+ set page(width: m.width + page.margin.length*2, height: m.height + page.margin.length*2)
+ body
+}
+
+#display()[$x$]
diff --git a/src/data/test_content_2.typ b/src/data/test_content_2.typ
new file mode 100644
index 0000000..db02163
--- /dev/null
+++ b/src/data/test_content_2.typ
@@ -0,0 +1,12 @@
+#set page(
+ fill: none,
+ margin: 2pt,
+);
+
+#let display(body) = context {
+ let m = measure(body)
+ set page(width: m.width + page.margin.length*2, height: m.height + page.margin.length*2)
+ body
+}
+
+#display()[$x^2$]
diff --git a/src/eval/Element.zig b/src/eval/Element.zig
index 73dfb94..16905b4 100644
--- a/src/eval/Element.zig
+++ b/src/eval/Element.zig
@@ -11,6 +11,11 @@ pub const Code = blocks.Code;
pub const Figure = blocks.Figure;
pub const Callout = blocks.Callout;
pub const Quote = blocks.Quote;
+pub const Math = @import("math.zig");
+
+comptime {
+ _ = Math;
+}
pub const Node = struct {
ptr: *anyopaque,
diff --git a/src/eval/html/Literal.zig b/src/eval/html/Literal.zig
index ccad004..a75b16c 100644
--- a/src/eval/html/Literal.zig
+++ b/src/eval/html/Literal.zig
@@ -20,6 +20,13 @@ pub fn init(alloc: Allocator, literal: []const u8) Error!*Element.Literal {
return v;
}
+pub fn initNoEscape(alloc: Allocator, literal: []const u8) Error!*Element.Literal {
+ const v = try alloc.create(Self);
+ v.* = .{ .literal = try alloc.dupe(u8, literal) };
+ v.node.ptr = v;
+ return v;
+}
+
pub fn element(self: *Self) Element {
return .{ .vtable = .{ .render = render, .node = getNode }, .ptr = self };
}
diff --git a/src/eval/math.zig b/src/eval/math.zig
new file mode 100644
index 0000000..20d3fec
--- /dev/null
+++ b/src/eval/math.zig
@@ -0,0 +1,108 @@
+const std = @import("std");
+const typst = @cImport(@cInclude("typdown_typst.h"));
+const Allocator = std.mem.Allocator;
+const HTML = Element.HTML;
+const Element = @import("Element.zig");
+const Node = Element.Node;
+
+const content_template = @embedFile("template_math_content.typ");
+const block_template = @embedFile("template_math_block.typ");
+
+pub const Error = error{InvalidTypstTemplate} || Allocator.Error;
+
+fn typstInterop(alloc: Allocator, comptime f: fn ([*c]const u8) callconv(.c) [*c]const u8, content: []const u8) ![]const u8 {
+ const source = try alloc.dupeZ(u8, content);
+ defer alloc.free(source);
+ const raw_res = f(source);
+ const res = try alloc.dupe(u8, std.mem.span(raw_res));
+ defer typst.typst_freeString(raw_res);
+ return res;
+}
+
+fn generateSVG(alloc: Allocator, content: []const u8) ![]const u8 {
+ return try typstInterop(alloc, typst.typst_generateSVG, content);
+}
+
+fn escape(alloc: Allocator, content: []const u8) ![]const u8 {
+ return try typstInterop(alloc, typst.typst_escapeMath, content);
+}
+
+fn generateFile(alloc: Allocator, template: []const u8, content: []const u8) Error![]const u8 {
+ var iter = std.mem.splitSequence(u8, template, "!!");
+ const beg = iter.next() orelse return Error.InvalidTypstTemplate;
+ const end = iter.next() orelse return Error.InvalidTypstTemplate;
+ if (iter.next() != null) return Error.InvalidTypstTemplate;
+
+ var acc = try std.ArrayList(u8).initCapacity(alloc, beg.len + end.len + content.len);
+ try acc.appendSlice(alloc, beg);
+ try acc.appendSlice(alloc, content);
+ try acc.appendSlice(alloc, end);
+ return try acc.toOwnedSlice(alloc);
+}
+
+fn Math(comptime template: []const u8) type {
+ return struct {
+ content: ?[]const u8 = null,
+ node: Node,
+
+ const Self = @This();
+
+ pub fn init(alloc: Allocator) !*Self {
+ const v = try alloc.create(Self);
+ v.node = .{ .ptr = v, .vtable = .{ .element = fromNode } };
+ 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 content = self.content orelse return (try HTML.Literal.init(alloc, "")).element();
+
+ var arena = std.heap.ArenaAllocator.init(alloc);
+ defer arena.deinit();
+ const escaped = try escape(arena.allocator(), content);
+ const file = generateFile(arena.allocator(), template, escaped) catch |err| switch (err) {
+ Error.InvalidTypstTemplate => @panic("invalid template"),
+ Error.OutOfMemory => return Error.OutOfMemory,
+ };
+ const svg = try generateSVG(arena.allocator(), file);
+ return (try HTML.Literal.initNoEscape(alloc, svg)).element();
+ }
+ };
+}
+
+pub const Content = Math(content_template);
+pub const Block = Math(block_template);
+
+fn doTest(alloc: Allocator, v: []const u8, r: []const u8) !void {
+ const escaped = try escape(alloc, v);
+ defer alloc.free(escaped);
+ std.testing.expect(std.mem.eql(u8, escaped, r)) catch |err| {
+ std.debug.print("{s}\n", .{escaped});
+ return err;
+ };
+}
+
+test "escape math" {
+ const alloc = std.testing.allocator;
+
+ try doTest(alloc, "hello", "hello");
+ try doTest(alloc, "hello $ world", "hello \\$ world");
+ try doTest(alloc,
+ \\hello
+ \\world
+ , "hello\\ world");
+}
diff --git a/src/eval/template_math_block.typ b/src/eval/template_math_block.typ
new file mode 100644
index 0000000..3d5891a
--- /dev/null
+++ b/src/eval/template_math_block.typ
@@ -0,0 +1,12 @@
+#set page(
+ fill: none,
+ margin: 2pt,
+);
+
+#let display(body) = context {
+ let m = measure(body)
+ set page(width: m.width + page.margin.length*2, height: m.height + page.margin.length*2)
+ body
+}
+
+#display()[$ !! $]
diff --git a/src/eval/template_math_content.typ b/src/eval/template_math_content.typ
new file mode 100644
index 0000000..03732af
--- /dev/null
+++ b/src/eval/template_math_content.typ
@@ -0,0 +1,12 @@
+#set page(
+ fill: none,
+ margin: 2pt,
+);
+
+#let display(body) = context {
+ let m = measure(body)
+ set page(width: m.width + page.margin.length*2, height: m.height + page.margin.length*2)
+ body
+}
+
+#display()[$!!$]
diff --git a/src/paragraph.zig b/src/paragraph.zig
index 2e5383a..45af712 100644
--- a/src/paragraph.zig
+++ b/src/paragraph.zig
@@ -50,9 +50,12 @@ 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 doTest(parse, alloc, "$x$", "<p>" ++ @embedFile("data/test_content_1.svg") ++ "</p>");
+ try doTest(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);
try doTestError(parse, alloc, "*hell*o *wo_rld*", Error.ModifierNotClosed);
+ try doTestError(parse, alloc, "hello wo$rld", Error.ModifierNotClosed);
try doTestError(parse, alloc, "hello ::: world", Error.IllegalPlacement);
}
diff --git a/src/quote.zig b/src/quote.zig
index 5456d5b..ad5a156 100644
--- a/src/quote.zig
+++ b/src/quote.zig
@@ -46,11 +46,11 @@ test {
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,
+ try doTest(parse, alloc,
\\> hello
\\>world
, "<figure><blockquote>hello world</blockquote></figure>");
- try doTest(parse, alloc,
+ try doTest(parse, alloc,
\\> hello
\\>world
\\attribution sur
diff --git a/src/root.zig b/src/root.zig
index 648fb77..6fd15f5 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -2,7 +2,6 @@ const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const parser = @import("parser.zig");
-const typst = @import("typst.zig");
pub const Document = parser.Document;
pub const Error = parser.Error;
/// Parse the content.
diff --git a/src/typst.zig b/src/typst.zig
deleted file mode 100644
index 9608af5..0000000
--- a/src/typst.zig
+++ /dev/null
@@ -1,11 +0,0 @@
-const std = @import("std");
-const typst = @cImport(@cInclude("typdown_typst.h"));
-
-pub fn generateSVG(alloc: std.mem.Allocator, content: []const u8) ![]const u8 {
- const source = try alloc.dupeZ(u8, content);
- defer alloc.free(source);
- const raw_res = typst.typst_generateSVG(source);
- const res = try alloc.dupe(u8, std.mem.span(raw_res));
- defer typst.typst_freeString(raw_res);
- return res;
-}