UNPKG

cm-tarnation

Version:

An alternative parser for CodeMirror 6

125 lines 4.89 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 { Wrapping } from "../constants"; import { re } from "../util"; import { Matched } from "./matched"; import { Node } from "./node"; import { Repository } from "./repository"; import { State } from "./rules/state"; import { GrammarStack, GrammarState } from "./state"; /** Grammar/dumb-tokenizer for a {@link TarnationLanguage}. */ export class Grammar { def; variables; /** * @param def - The definition grammar to compile. * @param variables - {@link Variable}s to pass to the compiled grammar. */ constructor(def, variables = {}) { // process language data this.def = def; this.variables = variables; this.data = {}; if (def.comments) this.data.commentTokens = def.comments; if (def.closeBrackets) this.data.closeBrackets = def.closeBrackets; if (def.wordChars) this.data.wordChars = def.wordChars; if (def.indentOnInput) { const regex = re(def.indentOnInput); if (!regex) throw new Error(`Invalid indentOnInput: ${def.indentOnInput}`); this.data.indentOnInput = regex; } // populate variable table with repository patterns if (def.repository) { for (const name in def.repository) { const value = def.repository[name]; if (typeof value === "string") variables[name] = value; else if ("match" in value) variables[name] = value.match; } } // setup repository, add rules, etc. this.repository = new Repository(this, variables, def.ignoreCase); if (def.default) this.default = this.repository.add(def.default); if (def.repository) { for (const name in def.repository) { this.repository.add(def.repository[name], name); } } this.root = this.repository.inside(def.root); if (def.global) this.global = this.repository.inside(def.global); } /** Returns a {@link GrammarState} setup for this grammar's default state. */ startState() { return new GrammarState(this.variables, {}, new GrammarStack([{ node: Node.None, rules: this.root, end: null }])); } /** * Runs a match against a string (starting from a given position). * * @param state - The {@link GrammarState} to run the match with. * @param str - The string to match. * @param pos - The position to start matching at. * @param offset - The offset to apply to the resulting {@link Matched}'s * `from` position. */ match(state, str, pos, offset = 0) { // check stack end state first before running match if (state.stack.end) { if (state.stack.end instanceof State) { let result = state.stack.end.close(state, str, pos); if (result) { if (offset !== pos) result.offset(offset); return result; } } else { let result = state.stack.end.match(state, str, pos); if (result) { result = result.wrap(state.stack.node, Wrapping.END); result.state.stack.pop(); if (offset !== pos) result.offset(offset); return result; } } } // normal matching const rules = state.stack.rules; for (let i = 0; i < rules.length; i++) { const rule = rules[i]; const result = rule.match(state, str, pos); if (result) { if (offset !== pos) result.offset(offset); return result; } } // global matching if (this.global) { for (let i = 0; i < this.global.length; i++) { const rule = this.global[i]; const result = rule.match(state, str, pos); if (result) { if (offset !== pos) result.offset(offset); return result; } } } if (this.default) { const result = new Matched(state, this.default, str.slice(pos, pos + 1), offset); if (result) return result; } return null; } } //# sourceMappingURL=grammar.js.map