Add 'ziglue/' from commit '323816262704dbee1fc1b2aa08204d55d4386b16'
git-subtree-dir: ziglue git-subtree-mainline:d955ccf17d
git-subtree-split:3238162627
This commit is contained in:
3
ziglue/.envrc
Normal file
3
ziglue/.envrc
Normal file
@@ -0,0 +1,3 @@
|
||||
PATH_add zig-out/bin
|
||||
source .venv/bin/activate
|
||||
|
5
ziglue/.gitignore
vendored
Normal file
5
ziglue/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.zig-cache/
|
||||
zig-out/
|
||||
|
||||
.spec/
|
||||
|
1
ziglue/.tool-versions
Normal file
1
ziglue/.tool-versions
Normal file
@@ -0,0 +1 @@
|
||||
zig master
|
0
ziglue/README.md
Normal file
0
ziglue/README.md
Normal file
46
ziglue/build.zig
Normal file
46
ziglue/build.zig
Normal file
@@ -0,0 +1,46 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
const exe = b.addExecutable(.{
|
||||
.name = "ziglue",
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
|
||||
const run_step = b.step("run", "Run the app");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
const exe_unit_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||
|
||||
const example_tests = b.addTest(.{
|
||||
.root_source_file = b.path("src/examples.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
const run_example_tests = b.addRunArtifact(example_tests);
|
||||
|
||||
const test_step = b.step("test", "Run unit tests");
|
||||
test_step.dependOn(&run_exe_unit_tests.step);
|
||||
test_step.dependOn(&run_example_tests.step);
|
||||
}
|
10
ziglue/build.zig.zon
Normal file
10
ziglue/build.zig.zon
Normal file
@@ -0,0 +1,10 @@
|
||||
.{
|
||||
.name = "ziglue",
|
||||
.version = "0.0.0",
|
||||
|
||||
.paths = .{
|
||||
"build.zig",
|
||||
"build.zig.zon",
|
||||
"src",
|
||||
},
|
||||
}
|
61
ziglue/examples.py
Normal file
61
ziglue/examples.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import parsel
|
||||
import httpx
|
||||
from pathlib import Path
|
||||
from textwrap import indent
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
VERSION = '0.31.2'
|
||||
|
||||
CACHE = Path('.spec', VERSION)
|
||||
URL = f'https://spec.commonmark.org/{VERSION}/'
|
||||
|
||||
if CACHE.exists():
|
||||
sel = parsel.Selector(CACHE.read_text())
|
||||
else:
|
||||
response = httpx.get('https://spec.commonmark.org/0.31.2/')
|
||||
response.raise_for_status()
|
||||
CACHE.parent.mkdir(parents=True, exist_ok=True)
|
||||
CACHE.write_text(response.text)
|
||||
sel = parsel.Selector(response.text)
|
||||
|
||||
|
||||
formatter = Popen(['zig', 'fmt', '--stdin'], stdin=PIPE, encoding='utf-8')
|
||||
assert formatter.stdin
|
||||
|
||||
write = formatter.stdin.write
|
||||
|
||||
write(f'''
|
||||
//! Example cases from Commonmark spec {VERSION}
|
||||
//! {URL}
|
||||
|
||||
const std = @import("std");
|
||||
const convert = @import("main.zig").convert;
|
||||
''')
|
||||
|
||||
for example in sel.css('.example'):
|
||||
name = example.css('.examplenum > a::text').get()
|
||||
|
||||
md = ''.join(example.css('.language-markdown *::text').getall())
|
||||
html = ''.join(example.css('.language-html *::text').getall())
|
||||
|
||||
md = md.replace('→', ' ')
|
||||
html = html.replace('→', ' ')
|
||||
|
||||
assert name is not None
|
||||
|
||||
write(f'''
|
||||
test "{name}" {{
|
||||
const output = try convert(std.testing.allocator,
|
||||
{indent(md, r'\\', lambda _: True) if md else '""'}
|
||||
);
|
||||
defer std.testing.allocator.free(output);
|
||||
|
||||
try std.testing.expectEqualStrings(
|
||||
{indent(html, r'\\', lambda _: True) if html else '""'}
|
||||
,
|
||||
output
|
||||
);
|
||||
}}
|
||||
''')
|
||||
|
||||
formatter.stdin.close()
|
37
ziglue/notes.md
Normal file
37
ziglue/notes.md
Normal file
@@ -0,0 +1,37 @@
|
||||
The spec is organized into "content blocks", "leaf blocks", and inline content. I should take this as a hint to do the same.
|
||||
|
||||
So the first task in the parser should be to parse the block structure.
|
||||
|
||||
<https://spec.commonmark.org/0.31.2/>
|
||||
|
||||
- Blocks
|
||||
- Leaf
|
||||
- Thematic break
|
||||
- ATX heading
|
||||
- Setext heading
|
||||
- Indented chunk
|
||||
+ Indented code block is a sequence of indented chunks.
|
||||
+ Preserve count of blank lines.
|
||||
- Fenced code block
|
||||
- HTML blocks
|
||||
- Link reference definition
|
||||
- Paragraph
|
||||
- Blank lines
|
||||
+ These are part of the document, but they are not rendered.
|
||||
- Container
|
||||
- Blockquote
|
||||
- List Item
|
||||
+ List is a sequence of list items of the same type.
|
||||
|
||||
- Inline
|
||||
- Inline code
|
||||
- Strong, emph
|
||||
- Links
|
||||
- Inline
|
||||
- Reference
|
||||
- Images
|
||||
- Auto
|
||||
- HTML
|
||||
- Text
|
||||
|
||||
<!--vim: ts=2 sw=2 et linebreak :-->
|
112
ziglue/pyparse.py
Normal file
112
ziglue/pyparse.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import sys
|
||||
from enum import Enum, auto
|
||||
from idlelib.configdialog import is_int
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Block:
|
||||
def __init__(self, *parts):
|
||||
self.tag = type(self).__name__
|
||||
self.data = list(parts)
|
||||
|
||||
def extend(self, *parts):
|
||||
self.data.extend(parts)
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.tag}:: {''.join(self.data)!r}'
|
||||
|
||||
|
||||
class Break(Block): pass
|
||||
|
||||
|
||||
class ATXHeading(Block): pass
|
||||
|
||||
|
||||
class SetextHeading(Block): pass
|
||||
|
||||
|
||||
class IndentedChunk(Block): pass
|
||||
|
||||
|
||||
class Fence(Block):
|
||||
def __init__(self, meta, *data):
|
||||
super().__init__(*data)
|
||||
self.meta = meta
|
||||
self.complete = False
|
||||
|
||||
def __repr__(self):
|
||||
return f'{self.tag}:{self.meta}:: {''.join(self.data)!r}'
|
||||
|
||||
|
||||
class HTML(Block): pass
|
||||
|
||||
|
||||
class Definition(Block): pass
|
||||
|
||||
|
||||
class Paragraph(Block): pass
|
||||
|
||||
|
||||
class Blank(Block): pass
|
||||
|
||||
|
||||
def convert(md: str):
|
||||
blocks: list[Block] = []
|
||||
|
||||
cur_fence: Optional[Fence] = None
|
||||
|
||||
def get(idx):
|
||||
try:
|
||||
return blocks[idx]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
for line in md.splitlines(keepends=True):
|
||||
if cur_fence:
|
||||
if line.lstrip(' ').startswith('```'):
|
||||
blocks.append(cur_fence)
|
||||
cur_fence = None
|
||||
else:
|
||||
cur_fence.extend(line)
|
||||
else:
|
||||
if line.isspace():
|
||||
if len(blocks) >= 1 and isinstance(blocks[-1], Blank):
|
||||
blocks[-1].extend(line)
|
||||
else:
|
||||
blocks.append(Blank(line))
|
||||
|
||||
elif line.startswith(' ') or line.startswith('\t'):
|
||||
if len(blocks) >= 1 and isinstance(blocks[-1], IndentedChunk):
|
||||
blocks[-1].extend(line)
|
||||
elif len(blocks) >= 2 and isinstance(blocks[-1], Blank) and isinstance(blocks[-2], IndentedChunk):
|
||||
blocks[-2].extend(*blocks[-1].data, line)
|
||||
blocks.pop(-1)
|
||||
else:
|
||||
blocks.append(IndentedChunk(line))
|
||||
|
||||
elif line.lstrip(' ').startswith('```'):
|
||||
meta = line.strip().removeprefix('```')
|
||||
cur_fence = Fence(meta)
|
||||
else:
|
||||
if len(blocks) >= 1 and isinstance(blocks[-1], Paragraph):
|
||||
blocks[-1].extend(line)
|
||||
else:
|
||||
blocks.append(Paragraph(line))
|
||||
|
||||
pprint(blocks)
|
||||
|
||||
|
||||
def main():
|
||||
for arg in sys.argv[1:]:
|
||||
md = Path(arg).read_text()
|
||||
html = convert(md)
|
||||
|
||||
print('=' * 80)
|
||||
print(html)
|
||||
print('=' * 80)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
2
ziglue/requirements.txt
Normal file
2
ziglue/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
httpx
|
||||
parsel
|
9146
ziglue/src/examples.zig
Normal file
9146
ziglue/src/examples.zig
Normal file
File diff suppressed because it is too large
Load Diff
65
ziglue/src/main.zig
Normal file
65
ziglue/src/main.zig
Normal file
@@ -0,0 +1,65 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn find_blocks(src: []const u8, blocks: *std.ArrayList([]const u8)) !void {
|
||||
var idx: usize = 0;
|
||||
var blk: usize = 0;
|
||||
|
||||
while (idx < src.len) {
|
||||
const end = std.mem.indexOfAnyPos(u8, src, idx, "\n") orelse src.len;
|
||||
const line = src[idx..end];
|
||||
if (std.mem.indexOfNone(u8, line, " \t\r\n") == null) {
|
||||
// the line is blank; a block has ended.
|
||||
const block = src[blk..end];
|
||||
|
||||
std.debug.print("BLOCK:\n{s}", .{block});
|
||||
|
||||
try blocks.append(block);
|
||||
blk = end + 1;
|
||||
}
|
||||
idx = end + 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert(alloc: std.mem.Allocator, md: []const u8) ![]const u8 {
|
||||
return try alloc.dupe(u8, md);
|
||||
|
||||
// _ = alloc;
|
||||
// _ = md;
|
||||
// return error.UhOh;
|
||||
// return error.NotImplemented;
|
||||
|
||||
// return alloc.dupe(u8, "Hello World!");
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
const alloc = gpa.allocator();
|
||||
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
var args = try std.process.argsWithAllocator(alloc);
|
||||
defer args.deinit();
|
||||
std.debug.assert(args.skip());
|
||||
|
||||
var blocks = std.ArrayList([]const u8).init(alloc);
|
||||
defer blocks.deinit();
|
||||
|
||||
while (args.next()) |fname| {
|
||||
const src = load: {
|
||||
const file = try std.fs.cwd().openFile(fname, .{ .mode = .read_only });
|
||||
defer file.close();
|
||||
break :load try file.readToEndAlloc(alloc, std.math.maxInt(u32));
|
||||
};
|
||||
defer alloc.free(src);
|
||||
|
||||
std.log.debug("arg '{s}' {d} bytes", .{ fname, src.len });
|
||||
|
||||
blocks.clearRetainingCapacity();
|
||||
try find_blocks(src, &blocks);
|
||||
|
||||
// try parse(alloc, src);
|
||||
|
||||
// const tokens = try parse(alloc, src);
|
||||
// defer alloc.free(tokens);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user