Skip to content

Instantly share code, notes, and snippets.

@biogeo
Created March 23, 2017 17:55
Show Gist options
  • Save biogeo/586d660a74d1f59d667763a1cdd7309b to your computer and use it in GitHub Desktop.
Save biogeo/586d660a74d1f59d667763a1cdd7309b to your computer and use it in GitHub Desktop.
"Offside rule" indentation parsing with PEG.js
// Parse a document using "offside rule" indentation (as in Python) into lines
// grouped by indentation level, using PEG.js.
// Attempts to segregate the "stateful" rules from the other production/parsing
// rules by "disallowing" indentation-level-sensitive rules from consuming any
// text.
{ var margin_stack = [""]; }
Document
= content: Element+
BlankLine*
{ return content; }
Element
= BlankLine*
content: (CurrentBlockElement / IndentedBlock)
{ return content; }
CurrentBlockElement
= &MAINTAIN_MARGIN content:Line
{ return content; }
IndentedBlock
= &INCREASE_MARGIN
first: CurrentBlockElement
content: Element*
&DECREASE_MARGIN
{ return [first].concat(content)]; }
Line
= BlankSpace?
content: $(!LineEnding .)+ LineEnding
{ return content; }
INCREASE_MARGIN
= indent: $BlankSpace?
&{ return (indent !== margin_stack[0])
&& indent.startsWith(margin_stack[0]); }
{ margin_stack.unshift(indent); }
MAINTAIN_MARGIN
= indent: $BlankSpace?
&{ return indent === margin_stack[0]; }
DECREASE_MARGIN
= EOF
/ indent: $BlankSpace?
&{ return margin_stack.slice(1).includes(indent); }
{ margin_stack.shift(); }
BlankLine
= BlankSpace? EOL
/ BlankSpace EOF
LineEnding
= BlankSpace? (EOL / EOF)
BlankSpace
= [ \t]+
EOL = "\r\n" / "\n" / "\r"
EOF = !.
@disarticulate
Copy link

IndentedBlock
  = &INCREASE_MARGIN
    first: CurrentBlockElement
    content: Element*
    &DECREASE_MARGIN
    { return [first].concat(content); }

typo fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment