Skip to content

cursork/aplmus

Repository files navigation

Dyalog APL CommonMark Parser

A fully CommonMark-compliant markdown parser written in Dyalog APL.

Status: 651/651 spec tests passing (100%), 1 skipped (Unicode case folding limitation)

Usage

 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

Getting Started

 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'

Running Tests

 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'

Project Structure

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)

Architecture

Parsing Flow

Input → API.Parse → Block.ParseBlocks → ParseLinesLoop
                                            │
                    ┌───────────────────────┼───────────────────────┐
                    ↓                       ↓                       ↓
             ParseBlockquote          ParseBulletList         ParseParagraph
                    │                       │                       │
                    ↓                       ↓                       ↓
              (recursive)            ParseListItem            (inline parsing)
                                           │
                                           ↓
                                     (recursive)

Key Algorithms

  • 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

CommonMark Compliance

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.

Known Limitations

  1. Unicode case folding: German capital sharp S (ẞ) to "SS" requires character expansion not available in Dyalog APL's ⎕C function.

License

MIT

About

aplmus... Apfelmus... get it?

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages