Unknown state [2025-08-04]
This commit is contained in:
156
md/src/main.zig
Normal file
156
md/src/main.zig
Normal file
@@ -0,0 +1,156 @@
|
||||
//! Markdown Parser.
|
||||
//!
|
||||
//! Leaf blocks
|
||||
//! Thematic Breaks
|
||||
//! ATX headings
|
||||
//! Setext headings
|
||||
//! Indented code blocks
|
||||
//! Fenced code blocks
|
||||
//! HTML blocks
|
||||
//! Link reference definitions
|
||||
//! Paragraphs
|
||||
//! Blank lines
|
||||
//!
|
||||
//! Container blocks
|
||||
//! Block Quotes
|
||||
//! List Items
|
||||
//! Lists
|
||||
//!
|
||||
//! Inlines
|
||||
//! Code spans
|
||||
//! emph, strong
|
||||
//! Links
|
||||
//! Images
|
||||
//! Autolinks
|
||||
//! Raw HTML
|
||||
//! Hard line breaks
|
||||
//! Soft line breaks
|
||||
//! Text
|
||||
|
||||
const BlockType = enum {
|
||||
// Leaf Blocks
|
||||
Break,
|
||||
Heading,
|
||||
Code,
|
||||
HTML,
|
||||
LinkDefinition,
|
||||
Paragraph,
|
||||
Blank,
|
||||
|
||||
// Container Blocks
|
||||
Quote,
|
||||
List,
|
||||
// ListItem,
|
||||
};
|
||||
|
||||
const Block = struct {
|
||||
type: BlockType,
|
||||
text: []const u8,
|
||||
depth: u8 = 0,
|
||||
};
|
||||
|
||||
const std = @import("std");
|
||||
const md = @import("md");
|
||||
|
||||
pub fn render(src: []const u8, alloc: std.mem.Allocator) !void {
|
||||
var it = std.mem.splitScalar(u8, src, '\n');
|
||||
|
||||
var blocks = std.ArrayList(Block).init(alloc);
|
||||
defer blocks.deinit();
|
||||
|
||||
line_search: while (it.next()) |line| {
|
||||
// early check for blank line
|
||||
for (line) |ch| {
|
||||
if (!std.ascii.isWhitespace(ch)) break;
|
||||
} else {
|
||||
try blocks.append(.{ .type = .Blank, .text = line });
|
||||
continue :line_search;
|
||||
}
|
||||
|
||||
// figure out indentation
|
||||
var depth: u8 = 0;
|
||||
var start: u8 = 0;
|
||||
|
||||
for (line) |ch| {
|
||||
switch (ch) {
|
||||
' ' => depth += 1,
|
||||
'\t' => depth += 4,
|
||||
else => break,
|
||||
}
|
||||
start += 1;
|
||||
}
|
||||
// at this point, start will not be line.len because we checked
|
||||
// it is not blank
|
||||
|
||||
if (depth >= 4) {
|
||||
// todo something special has to happen. lists and code blocks.
|
||||
}
|
||||
|
||||
// test thematic break
|
||||
switch (line[start]) {
|
||||
'*', '-', '_' => |kind| {
|
||||
var count: u8 = 0;
|
||||
for (line[start..]) |ch| {
|
||||
if (ch == kind) {
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (std.ascii.isWhitespace(ch)) continue;
|
||||
|
||||
break;
|
||||
} else {
|
||||
if (count >= 3) {
|
||||
try blocks.append(.{ .type = .Break, .text = line });
|
||||
continue :line_search;
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
for (blocks.items) |block| {
|
||||
std.debug.print("{any}\n", .{block});
|
||||
}
|
||||
|
||||
// TODO split into paragraphs, list elements
|
||||
//
|
||||
|
||||
// const State = enum {
|
||||
// Block,
|
||||
// };
|
||||
|
||||
// mach: switch (State.Block) {
|
||||
// .Block => {
|
||||
// continue :mach .Block;
|
||||
// },
|
||||
// }
|
||||
|
||||
// while (it.next()) |line| {}
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
const args = try std.process.argsAlloc(alloc);
|
||||
defer std.process.argsFree(alloc, args);
|
||||
|
||||
const buf = try alloc.alloc(u8, 1 << 20);
|
||||
defer alloc.free(buf);
|
||||
|
||||
for (args[1..]) |path| {
|
||||
const file = try std.fs.cwd().openFile(path, .{ .mode = .read_only });
|
||||
defer file.close();
|
||||
const stat = try file.stat();
|
||||
if (stat.size > buf.len)
|
||||
return error.FileTooBig;
|
||||
|
||||
const n = try file.readAll(buf);
|
||||
const content = buf[0..n];
|
||||
|
||||
try render(content, alloc);
|
||||
}
|
||||
}
|
54
md/src/root.zig
Normal file
54
md/src/root.zig
Normal file
@@ -0,0 +1,54 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn parse(src: []const u8, alloc: std.mem.Allocator) ![][]const u8 {
|
||||
var it = std.mem.splitScalar(u8, src, '\n');
|
||||
|
||||
var lines = std.ArrayList([]const u8).init(alloc);
|
||||
|
||||
while (it.next()) |line| {
|
||||
try lines.append(line);
|
||||
}
|
||||
|
||||
return try lines.toOwnedSlice();
|
||||
}
|
||||
|
||||
// pub fn render(src: []const u8, alloc: std.mem.Allocator) ![]const u8 {
|
||||
// // std.mem.tokenizeScalar(comptime T: type, buffer: []const T, delimiter: T)
|
||||
// }
|
||||
|
||||
test "split-lines" {
|
||||
const alloc = std.testing.allocator;
|
||||
|
||||
const src =
|
||||
\\hello world!
|
||||
\\
|
||||
\\this is some content!
|
||||
\\
|
||||
;
|
||||
|
||||
const lines = try parse(src, alloc);
|
||||
defer alloc.free(lines);
|
||||
|
||||
const expect: []const []const u8 = &.{
|
||||
"hello world!",
|
||||
"",
|
||||
"this is some content!",
|
||||
"",
|
||||
};
|
||||
|
||||
try std.testing.expectEqualDeep(
|
||||
expect,
|
||||
lines,
|
||||
);
|
||||
}
|
||||
|
||||
test "2.2 Tabs" {
|
||||
// const alloc = std.testing.allocator;
|
||||
//
|
||||
// const md = "\tfoo\tbaz\t\tbim";
|
||||
|
||||
// const expect = "<pre><code>foo\tbaz\t\tbim</code></pre>";
|
||||
// const actual = try render(md, alloc);
|
||||
// defer alloc.free(actual);
|
||||
// std.testing.expectEqualStrings(expect, actual);
|
||||
}
|
Reference in New Issue
Block a user