UNPKG

cm-tarnation

Version:

An alternative parser for CodeMirror 6

142 lines 5.57 kB
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Tree } from "@lezer/common"; import { COMPILER_ARRAY_INTERVAL, FINISH_INCOMPLETE_NODES, NodeID } from "../constants"; import { ChunkBuffer } from "./buffer"; import { CompileStack } from "./stack"; export class Compiler { constructor(language, buffer) { this.language = language; this.stack = new CompileStack(); this.buffer = buffer || new ChunkBuffer(); this.compiled = new Int32Array(COMPILER_ARRAY_INTERVAL); this.size = 0; this.reused = []; this.index = 0; } get done() { return this.index >= this.buffer.chunks.length; } emit(type, from, to, children) { const idx = this.size * 4; // we may need to resize the array if (idx + 4 > this.compiled.length) { const old = this.compiled; this.compiled = new Int32Array(old.length + COMPILER_ARRAY_INTERVAL); this.compiled.set(old); } this.compiled[idx] = type; this.compiled[idx + 1] = from; this.compiled[idx + 2] = to; this.compiled[idx + 3] = children; this.size++; this.stack.increment(); } parse(chunk) { const from = chunk.from; const to = chunk.to; if (chunk.tryForTree(this.language.nodeSet)) { const tree = chunk.tree; this.emit(this.reused.length, from, to, -1); this.reused.push(tree); return; } // add open nodes to stack // this doesn't affect the buffer at all, but now we can watch for // when another node closes one of the open nodes we added if (chunk.open) { for (let i = 0; i < chunk.open.length; i++) { this.stack.push(chunk.open[i], from, 0); } } if (chunk.size) { for (let i = 0; i < chunk.size * 3; i += 3) { this.emit(chunk.tokens[i], from + chunk.tokens[i + 1], from + chunk.tokens[i + 2], 4); } } // pop close nodes from the stack, if they can be paired with an open node if (chunk.close) { for (let i = 0; i < chunk.close.length; i++) { const id = chunk.close[i]; const idx = this.stack.last(id); if (idx !== null) { // cut off anything past the closing element // i.e. inside nodes won't persist outside their parent if they // never closed before their parent did this.stack.close(idx); // finally pop the node const s = this.stack.pop(); const node = s[0]; const pos = s[1]; const children = s[2]; this.emit(node, pos, to, children * 4 + 4); } } } } step(force = false) { if (this.index < this.buffer.chunks.length - +force) { const chunk = this.buffer.chunks[this.index]; this.parse(chunk); this.index++; return true; } return false; } advanceFully(force = false) { if (!this.done) while (this.step(force)) { } } compile(start, length) { if (!this.buffer.chunks.length) { return new Tree(this.language.topNode, [], [], length); } this.advanceFully(); if (FINISH_INCOMPLETE_NODES) { while (this.stack.length > 0) { // emit an error token this.emit(NodeID.ERROR_INCOMPLETE, length, length, 4); // finish the last element in the stack const s = this.stack.pop(); const node = s[0]; const pos = s[1]; const children = s[2]; this.emit(node, pos, length, children * 4 + 4); } } const topID = NodeID.TOP; const reused = this.reused; const buffer = new ArrayBufferCursor(this.compiled, this.size * 4); const nodeSet = this.language.nodeSet; // build tree from buffer const tree = Tree.build({ topID, buffer, reused, nodeSet, start, length }); // bit of a hack (private properties) // this is so that we don't need to build another tree const props = Object.create(null); // @ts-ignore props[this.language.stateProp.id] = this.buffer; // @ts-ignore tree.props = props; return tree; } } // prettier-ignore /** Cursor that the `Tree.buildData` function uses to read a buffer. */ export class ArrayBufferCursor { buffer; pos; constructor(buffer, pos = buffer.length) { this.buffer = buffer; this.pos = pos; } // weirdly enough, using getters here is _faster_. // I don't understand why, lol get id() { return this.buffer[this.pos - 4]; } get start() { return this.buffer[this.pos - 3]; } get end() { return this.buffer[this.pos - 2]; } get size() { return this.buffer[this.pos - 1]; } next() { this.pos -= 4; } fork() { return new ArrayBufferCursor(this.buffer, this.pos); } } //# sourceMappingURL=compiler.js.map