aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2026-04-15 14:41:33 +0200
committerAnhgelus Morhtuuzh <william@herges.fr>2026-04-15 14:41:33 +0200
commite053ba532f1e58bb54f286b950e3be7166ba3dcd (patch)
treef71d1efdacfdd0599938d8e5175b2f0e9b887033
parent4a423b2f637143ff0135045733e0428cc9560597 (diff)
feat(lexer): supports link and image
-rw-r--r--grammar.ebnf2
-rw-r--r--src/lexer/lexed.zig2
-rw-r--r--src/lexer/lexer.zig26
3 files changed, 25 insertions, 5 deletions
diff --git a/grammar.ebnf b/grammar.ebnf
index d0259a1..664f68b 100644
--- a/grammar.ebnf
+++ b/grammar.ebnf
@@ -6,7 +6,7 @@ strong-delimiter = "\n\n", { "\n" };
(* blocks *)
block = title | paragraph | quote | callout | code-block | math-block | image | footnotes;
-title = ? #{1,6} ?, content;
+title = ? #{1,6} ?, " ", content;
paragraph = content, { weak-delimiter, content };
quote = ">", content, { weak-delimiter, ">", content }, [ paragraph ];
callout = ":::", [ ? [a-z]+ ? ], { delimiter, content }, weak-delimiter, ":::";
diff --git a/src/lexer/lexed.zig b/src/lexer/lexed.zig
index a572c2a..59b6e31 100644
--- a/src/lexer/lexed.zig
+++ b/src/lexer/lexed.zig
@@ -13,6 +13,8 @@ pub const Kind = enum {
bold,
italic,
ref,
+ list_ordored,
+ list_unordored,
};
pub const Lexed = struct {
diff --git a/src/lexer/lexer.zig b/src/lexer/lexer.zig
index a29384e..b76cd50 100644
--- a/src/lexer/lexer.zig
+++ b/src/lexer/lexer.zig
@@ -30,14 +30,14 @@ pub const Lexer = struct {
self.force_lit = true;
current_kind = .literal;
} else {
- current_kind = self.getCurrentKind(rune, acc.items);
+ current_kind = self.getCurrentKind(current_kind, rune, acc.items);
self.force_lit = false;
try acc.appendSlice(alloc, rune);
}
// conds here to avoid creating complex condition in while
const next_rune = self.iter.peek(1);
if (next_rune.len > 0) {
- if (self.getCurrentKind(next_rune, acc.items) != current_kind.?) {
+ if (self.getCurrentKind(current_kind, next_rune, acc.items) != current_kind.?) {
if (!requiresSpace(current_kind.?)) break;
if (std.mem.eql(u8, next_rune, " ")) {
// consume next space
@@ -55,13 +55,15 @@ pub const Lexer = struct {
return lexed.Lexed.init(alloc, kind, acc);
}
- fn getCurrentKind(self: *Self, rune: []const u8, acc: []const u8) lexed.Kind {
+ fn getCurrentKind(self: *Self, before: ?lexed.Kind, rune: []const u8, acc: []const u8) lexed.Kind {
if (self.force_lit) return .literal;
if (std.mem.eql(u8, rune, ">")) return .quote;
if (std.mem.eql(u8, rune, "\n")) return .delimiter;
+ if (std.mem.eql(u8, rune, "!")) return .image;
if (is('#', 6, rune, acc)) return .title;
if (is('`', 3, rune, acc)) return .code;
if (is('$', 3, rune, acc)) return .math;
+ if (isIn(links, before, .link, rune, acc)) return .link;
return .literal;
}
};
@@ -72,6 +74,21 @@ fn is(v: u8, maxLen: usize, rune: []const u8, acc: []const u8) bool {
return std.mem.eql(u8, rune, &[_]u8{v});
}
+const links = &[_][]const u8{ "[", "](", ")" };
+
+fn isIn(ops: []const []const u8, before: ?lexed.Kind, now: lexed.Kind, rune: []const u8, p: []const u8) bool {
+ var acc = p;
+ if (before) |b| {
+ if (now != b) acc = &[_]u8{};
+ }
+ for (ops) |op| {
+ const ln = acc.len + rune.len;
+ if (op.len >= ln and std.mem.eql(u8, acc, op[0..acc.len]) and std.mem.eql(u8, rune, op[acc.len..ln]))
+ return true;
+ }
+ return false;
+}
+
fn requiresSpace(k: lexed.Kind) bool {
return switch (k) {
.title => true,
@@ -98,7 +115,8 @@ test "lexer common" {
var l = try Lexer.init("# hello world :)");
try doTest(alloc, &l, .title, "#");
- try doTest(alloc, &l, .literal, "hello world :)");
+ try doTest(alloc, &l, .literal, "hello world :");
+ try doTest(alloc, &l, .link, ")");
try expect(try l.next(alloc) == null);
}