diff --git a/README.md b/README.md index e69de29..fdb4ea4 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,63 @@ +# YAMD + +Indentation-based markup language that compiles to HTML via Ruby codegen. + +## Syntax + +Structure is defined by indentation (tabs). Lines at a given indent level belong to the enclosing directive. + +### Directives (line-start only) + +| Syntax | Description | +|--------|-------------| +| `#% ` | Metadata / Ruby statement. e.g. `#% meta "date", "2025 01 01"` | +| `#! ` | Block wrapper. Evaluates `expr` as a Ruby method call, content is passed as a block. e.g. `#! tag('h1')` | +| `#$ ` | Raw block. Like `#!` but inner lines are passed as a raw string (no YAMD parsing). Used for code blocks. e.g. `#$ code` | +| `#. ` | List item. Must appear inside a `#! list(...)` block | + +### Inline constructs + +| Syntax | Description | +|--------|-------------| +| `` #`...` `` | Inline code | +| `#{expr}` | Ruby expression interpolation (result converted to string) | +| `#(expr)` | Inline math (AsciiMath, rendered to MathML) | +| `#name(args)` | Method call. e.g. `#link("url")` | +| `#name(args)#{ content }#` | Method call with content block. e.g. `#link("url")#{ click here }#` | +| `##` | Escaped literal `#` | +| `}#` | Closes a `#{` content block | + +### Comments + +Lines matching `#` followed by nothing or a space (i.e. `#` or `# ...`) are comments and are skipped. + +### Available renderer methods + +These are defined by `HTMLRenderer` in `exe/yamd`: + +| Method | Usage | +|--------|-------| +| `tag(name, **attrs)` | HTML element. `#! tag('h2')` | +| `list(numbering)` | List. numbering: `"-"` (ul), `"1"`, `"a"`, `"A"`, `"i"`, `"I"` (ol). Dot-separated for nested: `"-.1"` | +| `link(url)` | Anchor tag. `#link("https://example.com")#{ text }#` | +| `code(lang: Lang::DEFAULT)` | Code block (used via `#$`). Optional `lang:` for highlighting rules | +| `meta(name, value)` | HTML `` tag. Used via `#%` | +| `inline_code(text)` | Inline ``. Used via `` #`...` `` | +| `inline_math(text)` | AsciiMath to MathML. Used via `#(...)` | + +### Example + +``` +#% meta "date", "2025 01 01" + +#! tag('h1') + Title + +Paragraph text with #`inline code` and #link("https://example.com")#{ a link }#. + +#! list('-') + #. First item + #. Second item with sub-content + #$ code + x = 1 +``` diff --git a/exe/yamd b/exe/yamd index f43a35a..e160705 100755 --- a/exe/yamd +++ b/exe/yamd @@ -91,6 +91,23 @@ class HTMLRenderer < YAMD::Renderer } end + def single_tag(name, **attrs) + @buf << "<#{name}" + attrs.each { |k, v| + @buf << " " + if k.kind_of? Symbol + k = k.to_s + end + @buf << k + @buf << "=" << v.dump + } + @buf << "/>" + end + + def meta(name, value) + single_tag('meta', name: name, content: value) + end + def tag(name, **attrs) @buf << "<#{name}" attrs.each { |k, v| @@ -110,16 +127,16 @@ class HTMLRenderer < YAMD::Renderer tag('a', href: to, &blk) end - def code(&blk) + def code(lang: YAMD::Code::Lang::DEFAULT, &blk) tag('pre', class: "language-any") { tag('code', class: "language-any") { - @buf << YAMD::Code::highlight(blk.()) + @buf << YAMD::Code::highlight(blk.(), lang) } } end def inline_code(text) - tag('code') { + tag('code', class: "code-inline") { @buf << YAMD::Code::highlight(text) } end diff --git a/lib/yamd/code.rb b/lib/yamd/code.rb index 73e666e..ea7854f 100644 --- a/lib/yamd/code.rb +++ b/lib/yamd/code.rb @@ -21,7 +21,7 @@ module YAMD::Code DEFAULT = { /\b(?:[A-Z][a-zA-Z\-_0-9]*)\b*/ => 'type', /#(\s|$)[^\n]*/ => 'comment', - /\b(?:open|final|class|fn|let|var|if|else|while|loop|return|namespace|new)\b/ => 'kw', + /\b(?:open|final|class|struct|enum|fn|let|var|if|else|while|loop|return|namespace|new)\b/ => 'kw', /\b(?:def|import|from)\b/ => 'kw', /\b(?:null|undefined|true|false|this|self)\b/i => 'const', /\b(?:__[a-zA-Z0-9_]+__)\b/i => 'const', @@ -30,6 +30,19 @@ module YAMD::Code /"(?:\\.|[^\\"])*"/ => 'str', /'(?:\\.|[^\\'])*'/ => 'str', } + + GD_SCRIPT = { + /\b(?:[A-Z_]+[A-Z_0-9]*)\b/ => 'const', + /\b(?:[A-Z][a-zA-Z\-_0-9]*)\b*/ => 'type', + /#(\s|$)[^\n]*/ => 'comment', + /\b(?:class|struct|enum|var|func|const|if|else|while|return)\b/ => 'kw', + /\b(?:class_name|extends)\b/ => 'kw', + /\b(?:null|true|false|self)\b/i => 'const', + /[:,;()\[\]{}<>]/ => 'punct', + /0|(0x[0-9a-fA-F]+)|([1-9][0-9]*(\.[0-9]+)?)/ => 'number', + /"(?:\\.|[^\\"])*"/ => 'str', + /'(?:\\.|[^\\'])*'/ => 'str', + } end def self.highlight(txt, highlights=Lang::DEFAULT)