wip
This commit is contained in:
@@ -26,12 +26,22 @@ const DocumentStyle = struct {
|
|||||||
|
|
||||||
heading_char: u8 = '#',
|
heading_char: u8 = '#',
|
||||||
heading_levels: usize = 6,
|
heading_levels: usize = 6,
|
||||||
unordered_chars: []const u8 = "-++*",
|
unordered_chars: []const u8 = "-=+*",
|
||||||
|
check_box_chars: []const u8 = " x/~",
|
||||||
|
horizontal_rule_chars: []const u8 = "-=*.&^",
|
||||||
|
comment_id: []const u8 = "//",
|
||||||
|
escape_char: u8 = '\\',
|
||||||
|
code_block_id: []const u8 = "```",
|
||||||
|
|
||||||
pub const default = Self{
|
pub const default = Self{
|
||||||
.heading_char = '#',
|
.heading_char = '#',
|
||||||
.heading_levels = 6,
|
.heading_levels = 6,
|
||||||
.unordered_chars = "-++*",
|
.unordered_chars = "-=+*",
|
||||||
|
.check_box_chars = " x/~",
|
||||||
|
.horizontal_rule_chars = "-=*.&^",
|
||||||
|
.comment_id = "// ",
|
||||||
|
.escape_char = '\\',
|
||||||
|
.code_block_id = "```",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,7 +65,7 @@ pub const Document = struct {
|
|||||||
const StartLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
const StartLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
||||||
const InlineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
const InlineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
||||||
const EndLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
const EndLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line: *[]const u8) anyerror!FoundToken;
|
||||||
const MultiLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line_itter: *std.mem.SplitIterator(u8)) anyerror!void;
|
const MultiLineParserFn = *const fn (self: *Self, alloc: std.mem.Allocator, line_itter: *std.mem.SplitIterator(u8, .sequence)) anyerror!FoundToken;
|
||||||
|
|
||||||
tokens: std.ArrayList(Token) = .empty,
|
tokens: std.ArrayList(Token) = .empty,
|
||||||
tab_width: usize = 4,
|
tab_width: usize = 4,
|
||||||
@@ -64,27 +74,31 @@ pub const Document = struct {
|
|||||||
document_style: DocumentStyle = .default,
|
document_style: DocumentStyle = .default,
|
||||||
|
|
||||||
// each function will
|
// each function will
|
||||||
start_line_parsers: std.ArrayList(StartLineParserFn) = .empty,
|
start_line_parsers: []const StartLineParserFn = &[_]StartLineParserFn{
|
||||||
inline_parsers: std.ArrayList(InlineParserFn) = .empty,
|
&headingParser,
|
||||||
end_line_parsers: std.ArrayList(EndLineParserFn) = .empty,
|
&tickBoxParser,
|
||||||
|
&listOrderedParser,
|
||||||
|
&listUnorderedParser,
|
||||||
|
&horizontalRuleParser,
|
||||||
|
},
|
||||||
|
inline_parsers: []const InlineParserFn = &[_]InlineParserFn{
|
||||||
|
&baseLineParser,
|
||||||
|
},
|
||||||
|
|
||||||
pub fn initDefault(alloc: std.mem.Allocator) !Self {
|
end_line_parsers: []const EndLineParserFn = &[_]EndLineParserFn{
|
||||||
var doc = Self{
|
&commentParser,
|
||||||
|
},
|
||||||
|
|
||||||
|
multi_line_parsers: []const MultiLineParserFn = &[_]MultiLineParserFn{
|
||||||
|
&codeBlockParser,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn initDefault() Self {
|
||||||
|
return Self{
|
||||||
.tab_width = 4,
|
.tab_width = 4,
|
||||||
.newline_char = .newline,
|
.newline_char = .newline,
|
||||||
.document_style = .default,
|
.document_style = .default,
|
||||||
};
|
};
|
||||||
|
|
||||||
try doc.appendStartLineParsers(alloc, &[_]StartLineParserFn{
|
|
||||||
&headingParser,
|
|
||||||
&listOrderedParser,
|
|
||||||
});
|
|
||||||
|
|
||||||
try doc.appendInlineParsers(alloc, &[_]InlineParserFn{
|
|
||||||
&baseLineParser,
|
|
||||||
});
|
|
||||||
|
|
||||||
return doc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn appendStartLineParsers(self: *Self, alloc: std.mem.Allocator, parser_fns: []const StartLineParserFn) !void {
|
pub fn appendStartLineParsers(self: *Self, alloc: std.mem.Allocator, parser_fns: []const StartLineParserFn) !void {
|
||||||
@@ -99,9 +113,9 @@ pub const Document = struct {
|
|||||||
|
|
||||||
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
pub fn deinit(self: *Self, alloc: std.mem.Allocator) void {
|
||||||
self.tokens.deinit(alloc);
|
self.tokens.deinit(alloc);
|
||||||
self.start_line_parsers.deinit(alloc);
|
// self.start_line_parsers.deinit(alloc);
|
||||||
self.end_line_parsers.deinit(alloc);
|
// self.end_line_parsers.deinit(alloc);
|
||||||
self.inline_parsers.deinit(alloc);
|
// self.inline_parsers.deinit(alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consumeChars(line: *[]const u8, amount_of_chars_to_consume: usize) error{too_many_chars_to_consume}!void {
|
fn consumeChars(line: *[]const u8, amount_of_chars_to_consume: usize) error{too_many_chars_to_consume}!void {
|
||||||
@@ -109,6 +123,11 @@ pub const Document = struct {
|
|||||||
line.* = line.*[amount_of_chars_to_consume..];
|
line.* = line.*[amount_of_chars_to_consume..];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn consumeCharsFromEnd(line: *[]const u8, amount_of_chars_to_consume: usize) error{too_many_chars_to_consume}!void {
|
||||||
|
if (line.len < amount_of_chars_to_consume) return error.too_many_chars_to_consume;
|
||||||
|
line.* = line.*[0 .. line.len - amount_of_chars_to_consume];
|
||||||
|
}
|
||||||
|
|
||||||
fn baseLineParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
fn baseLineParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
try self.tokens.append(alloc, Token{
|
try self.tokens.append(alloc, Token{
|
||||||
.data = stripWhiteSpace(line.*),
|
.data = stripWhiteSpace(line.*),
|
||||||
@@ -122,7 +141,7 @@ pub const Document = struct {
|
|||||||
fn inlineParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !void {
|
fn inlineParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !void {
|
||||||
if (line.len == 0) return;
|
if (line.len == 0) return;
|
||||||
|
|
||||||
for (self.inline_parsers.items) |parser| {
|
for (self.inline_parsers) |parser| {
|
||||||
_ = try parser(self, alloc, line);
|
_ = try parser(self, alloc, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,11 +157,30 @@ pub const Document = struct {
|
|||||||
pub fn parse(self: *Self, alloc: std.mem.Allocator, document: []const u8) !void {
|
pub fn parse(self: *Self, alloc: std.mem.Allocator, document: []const u8) !void {
|
||||||
var line_iter = std.mem.splitSequence(u8, document, self.newline_char.chars);
|
var line_iter = std.mem.splitSequence(u8, document, self.newline_char.chars);
|
||||||
|
|
||||||
while (line_iter.next()) |line| {
|
line_iterator: while (line_iter.next()) |line| {
|
||||||
var current_line = line;
|
var current_line = line;
|
||||||
|
std.debug.print("acutal current line : {s}\n", .{line});
|
||||||
|
var end_token: ?Token = null;
|
||||||
|
|
||||||
for (self.start_line_parsers.items) |parser| {
|
for (self.multi_line_parsers) |parser| {
|
||||||
std.debug.print("line : \"{s}\"\n", .{line});
|
// std.debug.print("line : \"{s}\"\n", .{line});
|
||||||
|
const token_found = try parser(self, alloc, &line_iter);
|
||||||
|
|
||||||
|
if (token_found) continue :line_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.end_line_parsers) |parser| {
|
||||||
|
// std.debug.print("line : \"{s}\"\n", .{line});
|
||||||
|
const token_found = try parser(self, alloc, ¤t_line);
|
||||||
|
|
||||||
|
if (token_found) {
|
||||||
|
end_token = self.tokens.pop();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.start_line_parsers) |parser| {
|
||||||
|
// std.debug.print("line : \"{s}\"\n", .{line});
|
||||||
const token_found = try parser(self, alloc, ¤t_line);
|
const token_found = try parser(self, alloc, ¤t_line);
|
||||||
|
|
||||||
if (token_found) break;
|
if (token_found) break;
|
||||||
@@ -150,6 +188,9 @@ pub const Document = struct {
|
|||||||
|
|
||||||
try self.inlineParser(alloc, ¤t_line);
|
try self.inlineParser(alloc, ¤t_line);
|
||||||
|
|
||||||
|
if (end_token) |t| {
|
||||||
|
try self.tokens.append(alloc, t);
|
||||||
|
}
|
||||||
try self.appendNewLineToken(alloc);
|
try self.appendNewLineToken(alloc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,6 +225,7 @@ pub const Document = struct {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [TODO] make this generic
|
||||||
pub fn listOrderedParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
pub fn listOrderedParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
const clean_line = stripWhiteSpace(line.*);
|
const clean_line = stripWhiteSpace(line.*);
|
||||||
const whitespace_chars_removed = countWhiteSpace(line.*, self.tab_width);
|
const whitespace_chars_removed = countWhiteSpace(line.*, self.tab_width);
|
||||||
@@ -235,31 +277,191 @@ pub const Document = struct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try consumeChars(line, whitespace_chars_removed + number.len + 2);
|
try consumeChars(line, whitespace_chars_removed + number.len + 2);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn listUnorderedParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
pub fn listUnorderedParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
const clean_line = stripWhiteSpace(line);
|
const clean_line = stripWhiteSpace(line.*);
|
||||||
const whitespace_chars_removed = @intFromPtr(clean_line.ptr) - @intFromPtr(line.ptr);
|
const whitespace_chars_removed = countWhiteSpace(line.*, self.tab_width);
|
||||||
// check for the min length of chars
|
// check for the min length of chars
|
||||||
if (clean_line.len < 2) return null;
|
if (clean_line.len < 2) return false;
|
||||||
|
|
||||||
// check there is a space seperating the char and the text
|
// check there is a space seperating the char and the text
|
||||||
if (clean_line[1] != ' ') return null;
|
if (clean_line[1] != ' ') return false;
|
||||||
|
|
||||||
if (clean_line[0] == '-' or clean_line[0] == '=' or clean_line[0] == '+' or clean_line[0] == '*') {
|
if (std.mem.containsAtLeastScalar(u8, self.document_style.unordered_chars, 1, clean_line[0])) {
|
||||||
return LineParserReturn{
|
try self.tokens.append(alloc, Token{
|
||||||
// +2 to remove "-. "
|
|
||||||
.pointer_offset = whitespace_chars_removed + 2,
|
|
||||||
.token = .{
|
|
||||||
.data = clean_line[0..1],
|
.data = clean_line[0..1],
|
||||||
|
.padding = whitespace_chars_removed,
|
||||||
.type = .unordered_list,
|
.type = .unordered_list,
|
||||||
},
|
});
|
||||||
};
|
|
||||||
|
try consumeChars(line, whitespace_chars_removed + 2);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tickBoxParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
|
const clean_line = stripWhiteSpace(line.*);
|
||||||
|
const whitespace_chars_removed = countWhiteSpace(line.*, self.tab_width);
|
||||||
|
|
||||||
|
if (!std.mem.startsWith(u8, clean_line, "- [")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// not long enough for a tick box
|
||||||
|
if (clean_line.len < 6) return false;
|
||||||
|
|
||||||
|
// - [
|
||||||
|
|
||||||
|
if (std.mem.containsAtLeastScalar(u8, self.document_style.check_box_chars, 1, clean_line[3]) and
|
||||||
|
clean_line[4] == ']' and clean_line[5] == ' ')
|
||||||
|
{
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = clean_line[0..5],
|
||||||
|
.padding = whitespace_chars_removed,
|
||||||
|
.type = .tickbox,
|
||||||
|
});
|
||||||
|
try consumeChars(line, 5);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseRuleLength(rule_char: u8, line: []const u8) usize {
|
||||||
|
for (line, 0..) |char, i| {
|
||||||
|
if (char == rule_char or char == ' ' or char == '\t')
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return line.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn horizontalRuleParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
|
const clean_line = stripWhiteSpace(line.*);
|
||||||
|
const whitespace_chars_removed = countWhiteSpace(line.*, self.tab_width);
|
||||||
|
|
||||||
|
for (self.document_style.horizontal_rule_chars) |char| {
|
||||||
|
const rule_length = parseRuleLength(char, clean_line);
|
||||||
|
|
||||||
|
if (rule_length != 0) {
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = clean_line[0..rule_length],
|
||||||
|
.padding = whitespace_chars_removed,
|
||||||
|
.type = .rule,
|
||||||
|
});
|
||||||
|
try consumeChars(line, rule_length + whitespace_chars_removed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commentParser(self: *Self, alloc: std.mem.Allocator, line: *[]const u8) !FoundToken {
|
||||||
|
|
||||||
|
// find the index of the comment
|
||||||
|
const index_of_commnet = blk: {
|
||||||
|
if (std.mem.indexOf(u8, line.*, self.document_style.comment_id)) |index| {
|
||||||
|
if (index == 0)
|
||||||
|
break :blk index;
|
||||||
|
|
||||||
|
if (line.*[index - 1] != self.document_style.escape_char) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk index;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = line.*[index_of_commnet .. index_of_commnet + self.document_style.comment_id.len],
|
||||||
|
.padding = 0,
|
||||||
|
.type = .comment,
|
||||||
|
});
|
||||||
|
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = line.*[index_of_commnet + self.document_style.comment_id.len .. line.len],
|
||||||
|
.padding = 0,
|
||||||
|
.type = .raw_text,
|
||||||
|
});
|
||||||
|
|
||||||
|
line.* = line.*[0..index_of_commnet];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn codeBlockParser(self: *Self, alloc: std.mem.Allocator, line_itter: *std.mem.SplitIterator(u8, .sequence)) !FoundToken {
|
||||||
|
const current_line = line_itter.peek() orelse return false;
|
||||||
|
const clean_line = stripWhiteSpace(current_line);
|
||||||
|
const whitespace_chars_removed = countWhiteSpace(current_line, self.tab_width);
|
||||||
|
std.debug.print("current_line : {s}\n", .{current_line});
|
||||||
|
|
||||||
|
if (!std.mem.startsWith(u8, clean_line, self.document_style.code_block_id)) {
|
||||||
|
// std.debug.print("line didnt start with ```\n", .{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = clean_line[0..self.document_style.code_block_id.len],
|
||||||
|
.padding = whitespace_chars_removed,
|
||||||
|
.type = .code_block,
|
||||||
|
});
|
||||||
|
|
||||||
|
std.debug.print("clean_line[0..self.document_style.code_block_id.len] : {s}\n", .{clean_line[0..self.document_style.code_block_id.len]});
|
||||||
|
|
||||||
|
const lang_name = current_line[self.document_style.code_block_id.len..]; // may have white space
|
||||||
|
std.debug.print("lang_name : {s}\n", .{current_line});
|
||||||
|
|
||||||
|
// std.debug.print("lang_name = {}\n", .{lang_name.len});
|
||||||
|
if (lang_name.len != 0) {
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = lang_name,
|
||||||
|
.padding = 0,
|
||||||
|
.type = .raw_text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.appendNewLineToken(alloc);
|
||||||
|
|
||||||
|
// std.debug.print("first values\n", .{});
|
||||||
|
// +1 to remove the newline
|
||||||
|
const raw_text_start_index =
|
||||||
|
@intFromPtr(current_line.ptr) - @intFromPtr(line_itter.buffer.ptr) + lang_name.len + self.document_style.code_block_id.len + 1;
|
||||||
|
std.debug.print("{} - {} + {} + {} + 1;", .{ @intFromPtr(current_line.ptr), @intFromPtr(line_itter.buffer.ptr), lang_name.len, self.document_style.code_block_id.len });
|
||||||
|
|
||||||
|
const ending_line = find_end: {
|
||||||
|
while (line_itter.next()) |line| {
|
||||||
|
if (std.mem.startsWith(u8, stripWhiteSpace(line), self.document_style.code_block_id))
|
||||||
|
break :find_end line;
|
||||||
|
} else {
|
||||||
|
return error.no_closing_tag;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const raw_text_end_index = @intFromPtr(ending_line.ptr) - @intFromPtr(line_itter.buffer.ptr) - 1;
|
||||||
|
std.debug.print("line_itter.buffer[{}..{}] : {s}\n", .{ raw_text_start_index, raw_text_end_index, line_itter.buffer });
|
||||||
|
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = line_itter.buffer[raw_text_start_index..raw_text_end_index],
|
||||||
|
.padding = 0,
|
||||||
|
.type = .raw_text,
|
||||||
|
});
|
||||||
|
|
||||||
|
try self.tokens.append(alloc, Token{
|
||||||
|
.data = ending_line[0..self.document_style.code_block_id.len],
|
||||||
|
.padding = countWhiteSpace(ending_line, 4),
|
||||||
|
.type = .code_block,
|
||||||
|
});
|
||||||
|
|
||||||
|
try self.appendNewLineToken(alloc);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
1214
src/tests.zig
1214
src/tests.zig
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,11 @@ const Io = std.Io;
|
|||||||
|
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
pub const colour_set_string_fg_fmt = "\x1B[38;2;{d};{d};{d}m";
|
||||||
|
pub const colour_set_string_bg_fmt = "\x1B[48;2;{d};{d};{d}m";
|
||||||
|
pub const colour_set_string_fmt = colour_set_string_fg_fmt ++ colour_set_string_bg_fmt;
|
||||||
|
pub const colour_end_string_fmt = "\x1B[0m";
|
||||||
|
|
||||||
pub fn main(init: std.process.Init) !void {
|
pub fn main(init: std.process.Init) !void {
|
||||||
const io = init.io;
|
const io = init.io;
|
||||||
|
|
||||||
@@ -10,12 +15,16 @@ pub fn main(init: std.process.Init) !void {
|
|||||||
var stdout_file_writer: Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
|
var stdout_file_writer: Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
|
||||||
const stdout = &stdout_file_writer.interface;
|
const stdout = &stdout_file_writer.interface;
|
||||||
|
|
||||||
|
var passed: usize = 0;
|
||||||
for (builtin.test_functions) |t| {
|
for (builtin.test_functions) |t| {
|
||||||
t.func() catch |err| {
|
t.func() catch |err| {
|
||||||
try stdout.print("{s} fail: {}\n", .{ t.name, err });
|
try stdout.print("[\x1B[38;2;255;0;0mfail\x1B[0m] {s} : {}\n", .{ t.name, err });
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
try stdout.print("{s} passed\n", .{t.name});
|
passed += 1;
|
||||||
|
try stdout.print("[\x1B[38;2;0;255;0mpassed\x1B[0m] {s} \n", .{t.name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try stdout.print("[{}/{}] test passed\n", .{ passed, builtin.test_functions.len });
|
||||||
try stdout.flush(); // Don't forget to flush!
|
try stdout.flush(); // Don't forget to flush!
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user