A fully CommonMark-compliant markdown parser written in Dyalog APL.
Status: 651/651 spec tests passing (100%), 1 skipped (Unicode case folding limitation)
⍝ Convert Markdown to HTML
Markdown.ToHTML '# Hello World'
⍝ → '<h1>Hello World</h1>'
Markdown.ToHTML 'This is **bold** and *italic*'
⍝ → '<p>This is <strong>bold</strong> and <em>italic</em></p>'
Markdown.ToHTML '- item 1',NL,'- item 2'
⍝ → '<ul><li>item 1</li><li>item 2</li></ul>'
⍝ Parse to AST (for custom processing)
ast ← Markdown.Parse '# Heading'
ast.type ⍝ → 'document'
(⊃ast.children).type ⍝ → 'heading'
(⊃ast.children).level ⍝ → 1
⍝ Render AST to HTML
html ← Markdown.Render ast⍝ Link the library
]link.create aplmus /path/to/APLSource
⍝ Try the demo (parses a file, navigates AST, renders HTML)
aplmus.Examples.ReadmeDemo.Run '/path/to/README.md'
⍝ Or open rendered HTML in browser
aplmus.Examples.ReadmeDemo.Open '/path/to/README.md'⍝ Link the library
]link.create aplmus /path/to/APLSource
⍝ Run all 652 spec tests
aplmus.Tests.Run.All '/path/to/spec.json'
⍝ Run specific example(s)
aplmus.Tests.Run.Example 280
aplmus.Tests.Run.Example 280 281 282
⍝ Run by section
aplmus.Tests.Run.Section 'Lists'APLSource/
├── Markdown/
│ ├── API.apln # Entry points: Parse, ToHTML, Render
│ ├── AST.apln # AST node constructors
│ ├── Block.apln # Block parsing (lists, blockquotes, headings, code)
│ ├── Inline.apln # Inline parsing (emphasis, links, code spans)
│ └── Render.apln # HTML rendering
├── Examples/
│ └── ReadmeDemo.apln # Demo: parse markdown, navigate AST, render HTML
└── Tests/
└── Run.apln # Test runner
spec.json # CommonMark 0.31.2 spec (652 tests)
spec.meta.json # Test configuration (skipped tests)
Input → API.Parse → Block.ParseBlocks → ParseLinesLoop
│
┌───────────────────────┼───────────────────────┐
↓ ↓ ↓
ParseBlockquote ParseBulletList ParseParagraph
│ │ │
↓ ↓ ↓
(recursive) ParseListItem (inline parsing)
│
↓
(recursive)
- Block parsing: Line-by-line with lookahead for container structures
- Lazy continuation: Content can continue paragraphs without matching container prefixes
- Emphasis: Delimiter stack algorithm per CommonMark Appendix A
- Links: Bracket matching with code span/HTML tag skipping
- Tight/loose lists: Track blank lines at correct nesting depth
Targets CommonMark 0.31.2.
| Feature | Status |
|---|---|
| ATX/Setext headings | ✓ |
| Paragraphs | ✓ |
| Thematic breaks | ✓ |
| Block quotes | ✓ |
| Lists (bullet/ordered) | ✓ |
| Tight/loose lists | ✓ |
| Indented code | ✓ |
| Fenced code | ✓ |
| HTML blocks | ✓ |
| Link reference definitions | ✓ |
| Emphasis (*/_) | ✓ |
| Links/Images | ✓ |
| Code spans | ✓ |
| Autolinks | ✓ |
| Raw HTML | ✓ |
| Hard/soft breaks | ✓ |
| Backslash escapes | ✓ |
| Entity references | ✓ |
| Lazy continuation | ✓ |
| Unicode case folding | Partial* |
*Test 540 skipped: ẞ→SS case folding requires one-to-many character mapping not supported by Dyalog ⎕C.
- Unicode case folding: German capital sharp S (ẞ) to "SS" requires character expansion not available in Dyalog APL's
⎕Cfunction.
MIT