Whenever I develop a grammar I find it useful to pretty print the parse tree. I did not find anything for this in the API, so I ported some code I have used in other parser generators:
import * as _inspect from "browser-util-inspect"
import { Input, stringInput, Tree } from "lezer-tree"
function inspect(arg: any): string {
return _inspect(arg, {
depth: null,
colors: true,
})
}
function printTree(tree: Tree, input: Input | string, from = 0, to = input.length): string {
if (typeof input === "string") input = stringInput(input)
let out = ""
const c = tree.cursor()
const childPrefixes: string[] = []
for (;;) {
const { type } = c
const cfrom = c.from
const cto = c.to
let leave = false
if (cfrom <= to && cto >= from) {
if (!type.isAnonymous) {
leave = true
if (!type.isTop) {
out += "\n" + childPrefixes.join("")
if (c.nextSibling() && c.prevSibling()) {
out += " ├─ "
childPrefixes.push(" │ ")
} else {
out += " └─ "
childPrefixes.push(" ")
}
}
out += type.name
}
const isLeaf = !c.firstChild()
if (!type.isAnonymous) {
const hasRange = cfrom !== cto
out += ` ${hasRange ? `[${inspect(cfrom)}..${inspect(cto)}]` : inspect(cfrom)}`
if (isLeaf && hasRange) {
out += `: ${inspect(input.read(cfrom, cto))}`
}
}
if (!isLeaf || type.isTop) continue
}
for (;;) {
if (leave) childPrefixes.pop()
leave = c.type.isAnonymous
if (c.nextSibling()) break
if (!c.parent()) return out
leave = true
}
}
}
console.log(printTree(tree, input))
Example output would be:
Contents [0..8]
├─ TagName [0..1]: 't'
├─ Tag [1..7]
│ ├─ TagStart [1..2]: '{'
│ ├─ TagName [2..3]: 'a'
│ ├─ ContentsStart [3..4]: ':'
│ ├─ TagName [4..6]: 'es'
│ └─ TagEnd [6..7]: '}'
└─ TagName [7..8]: 't'