diff --git a/dm-lezer/src/context.js b/dm-lezer/src/context.js new file mode 100644 index 0000000..32f61f3 --- /dev/null +++ b/dm-lezer/src/context.js @@ -0,0 +1,25 @@ +import { ContextTracker } from "@lezer/lr"; +import { dedent, indent } from "./parser.terms.js"; + +class IndentLevel { + constructor(parent, depth) { + this.parent = parent; + this.depth = depth; + this.hash = + (parent ? (parent.hash + parent.hash) << 8 : 0) + depth + (depth << 4); + } +} + +export const ctx = new ContextTracker({ + start: { indent: new IndentLevel(null, 0) }, + shift(context, term, stack, input) { + if (term === indent) + return { + ...context, + indent: new IndentLevel(context, stack.pos - input.pos), + }; + if (term === dedent) return { ...context, indent: context.parent }; + return context; + }, + hash: (context) => context.hash, +}); diff --git a/dm-lezer/src/dm.grammar b/dm-lezer/src/dm.grammar index aeebf66..9730704 100644 --- a/dm-lezer/src/dm.grammar +++ b/dm-lezer/src/dm.grammar @@ -1,3 +1,10 @@ +@context ctx from "./context" +@external tokens indentation from "./tokens.js" { + indent + dedent + blankLineStart +} + @top File { ( preprocessor | diff --git a/dm-lezer/src/tokens.js b/dm-lezer/src/tokens.js new file mode 100644 index 0000000..57a05a4 --- /dev/null +++ b/dm-lezer/src/tokens.js @@ -0,0 +1,32 @@ +import { ExternalTokenizer } from "@lezer/lr"; +import { blankLineStart, dedent, indent } from "./parser.terms"; + +const newline = "\n".charCodeAt(0), + space = " ".charCodeAt(0), + tab = "\t".charCodeAt(0), + hash = "#".charCodeAt(0), + slash = "/".charCodeAt(0), + star = "*".charCodeAt(0); + +export const indentation = new ExternalTokenizer((input, stack) => { + let prev = input.peek(-1); + if (prev !== -1 && prev !== newline) return; + let spaces = 0; + while (input.next === space || input.next === tab) { + input.advance(); + spaces++; + } + if ( + input.next === newline || + input.next === hash || + (input.next === slash && + (input.peek(1) === slash || input.peek(1) === star)) + ) { + if (stack.canShift(blankLineStart)) + input.acceptToken(blankLineStart, -spaces); + } else if (spaces > stack.context.indent.depth) { + input.acceptToken(indent); + } else if (spaces < stack.context.indent.depth) { + input.acceptToken(dedent, -spaces); + } +});