en Custom Syntax - chiba233/yumeDSL GitHub Wiki

Custom Syntax

API Reference | Custom Tag Name Characters

Default syntax is $$tag(content)$$. Want @@tag(content)@@ or <<tag[content]>>? Change the syntax option.


Syntax tokens overview

Inline:   $$tag(content)$$
          ↑↑   ↑       ↑↑
      tagPrefix tagOpen endTag

          $$tag(arg | content)$$
                    ↑
               tagDivider

Raw:      $$tag(arg)%
                   ↑   raw content (no parsing)
              rawOpen
          %end$$
          ↑
          rawClose

Block:    $$tag(arg)*
                   ↑   block content (recursive parsing)
             blockOpen
          *end$$
          ↑
          blockClose

Escape:   \)  \\  \|
          ↑
          escapeChar

Two approaches

Want to customize syntax?
    │
    ├─ Most cases → createEasySyntax (recommended)
    │              Change base tokens, compounds auto-derive
    │
    └─ Full manual → createSyntax
                     Plain shallow merge, you manage everything

createEasySyntax (recommended)

function createEasySyntax(
  overrides?: Partial<SyntaxInput> & { closeMiddle?: string }
): SyntaxConfig

You only change the easy-layer base inputs, compound tokens derive automatically:

Base token Default What you change
tagPrefix $$ Tag prefix
tagOpen ( Argument opener
tagClose ) Argument closer
tagDivider | Pipe separator
escapeChar \ Escape character
closeMiddle end Shared middle literal inside rawClose / blockClose

escapeChar gotcha: The default is "\\" in JavaScript source — but that is a single \ character. The length of the string is what the user types in the DSL text. escapeChar: "\\" → user types \| to get a literal | (one keystroke). escapeChar: "~~" → user types ~~| to get a literal | (two keystrokes). If you set a multi-character escape, users must type the full sequence every time they escape.

Compound token Derivation rule Default result
endTag tagClose + tagPrefix )$$
rawOpen tagClose + % )%
blockOpen tagClose + * )*
rawClose % + end + tagPrefix %end$$
blockClose * + end + tagPrefix *end$$

Examples

Change prefix — compounds follow:

createEasySyntax({ tagPrefix: "@@" });
// endTag → ")@@"    rawClose → "%end@@"    blockClose → "*end@@"

Change prefix + closer:

createEasySyntax({ tagPrefix: "@@", tagClose: "]" });
// endTag → "]@@"    rawOpen → "]%"    blockOpen → "]*"

Change the shared close middle:

createEasySyntax({ tagPrefix: "@@", closeMiddle: "fin" });
// rawClose → "%fin@@"    blockClose → "*fin@@"

Just the divider:

createEasySyntax({ tagDivider: ";" });
// $$tag(arg1;arg2)$$

Explicit compound override wins over derivation:

createEasySyntax({
    tagPrefix: "@@",
    rawClose: "<<ENDRAW>>",  // explicit, derivation skipped
});
// endTag → ")@@" (derived)    rawClose → "<<ENDRAW>>" (your override)

createSyntax (low-level)

function createSyntax(overrides?: Partial<SyntaxInput>): SyntaxConfig

Plain shallow merge with DEFAULT_SYNTAX. No auto-derivation, no validation. For fully irregular protocols.

const syntax = createSyntax({
    tagPrefix: "<<",
    tagOpen: "[",
    tagClose: "]",
    tagDivider: ";",
    endTag: "]>>",
    rawOpen: "]#",
    blockOpen: "]*",
    rawClose: "#end>>",
    blockClose: "*end>>",
    escapeChar: "~",
});
// <<bold[hello]>>   <<code[ts]#\nconst x = 1;\n#end>>

Warning: If you only override tagPrefix without updating endTag, inline tags won't close — endTag is still )$$ but tags open with the new prefix.


Token dependencies

Hard constraint in the parser — break these and tags stop working:

Core rule: endTag, rawOpen, blockOpen must start with tagClose.

Why? The parser balances tagOpen/tagClose nesting in the argument section, lands on tagClose, then looks ahead to determine the tag form. If compound tokens don't start with tagClose, the lookahead never matches.

Token Constraint
endTag Must start with tagClose
rawOpen Must start with tagClose
blockOpen Must start with tagClose
tagOpen/tagClose Must pair
Others Independent, change freely

Using createEasySyntax guarantees these constraints automatically. closeMiddle only affects easy derivation; it does not relax these parser requirements.


DEFAULT_SYNTAX quick reference

DEFAULT_SYNTAX.tagPrefix   === "$$"
DEFAULT_SYNTAX.tagOpen     === "("
DEFAULT_SYNTAX.tagClose    === ")"
DEFAULT_SYNTAX.tagDivider  === "|"
DEFAULT_SYNTAX.endTag      === ")$$"
DEFAULT_SYNTAX.rawOpen     === ")%"
DEFAULT_SYNTAX.blockOpen   === ")*"
DEFAULT_SYNTAX.rawClose    === "%end$$"
DEFAULT_SYNTAX.blockClose  === "*end$$"
DEFAULT_SYNTAX.escapeChar  === "\\"
⚠️ **GitHub.com Fallback** ⚠️