1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603 |
- import { Parser, NodeSet, NodeType, DefaultBufferLength, NodeProp, Tree, IterMode } from '@lezer/common';
- /// A parse stack. These are used internally by the parser to track
- /// parsing progress. They also provide some properties and methods
- /// that external code such as a tokenizer can use to get information
- /// about the parse state.
- class Stack {
- /// @internal
- constructor(
- /// The parse that this stack is part of @internal
- p,
- /// Holds state, input pos, buffer index triplets for all but the
- /// top state @internal
- stack,
- /// The current parse state @internal
- state,
- // The position at which the next reduce should take place. This
- // can be less than `this.pos` when skipped expressions have been
- // added to the stack (which should be moved outside of the next
- // reduction)
- /// @internal
- reducePos,
- /// The input position up to which this stack has parsed.
- pos,
- /// The dynamic score of the stack, including dynamic precedence
- /// and error-recovery penalties
- /// @internal
- score,
- // The output buffer. Holds (type, start, end, size) quads
- // representing nodes created by the parser, where `size` is
- // amount of buffer array entries covered by this node.
- /// @internal
- buffer,
- // The base offset of the buffer. When stacks are split, the split
- // instance shared the buffer history with its parent up to
- // `bufferBase`, which is the absolute offset (including the
- // offset of previous splits) into the buffer at which this stack
- // starts writing.
- /// @internal
- bufferBase,
- /// @internal
- curContext,
- /// @internal
- lookAhead = 0,
- // A parent stack from which this was split off, if any. This is
- // set up so that it always points to a stack that has some
- // additional buffer content, never to a stack with an equal
- // `bufferBase`.
- /// @internal
- parent) {
- this.p = p;
- this.stack = stack;
- this.state = state;
- this.reducePos = reducePos;
- this.pos = pos;
- this.score = score;
- this.buffer = buffer;
- this.bufferBase = bufferBase;
- this.curContext = curContext;
- this.lookAhead = lookAhead;
- this.parent = parent;
- }
- /// @internal
- toString() {
- return `[${this.stack.filter((_, i) => i % 3 == 0).concat(this.state)}]@${this.pos}${this.score ? "!" + this.score : ""}`;
- }
- // Start an empty stack
- /// @internal
- static start(p, state, pos = 0) {
- let cx = p.parser.context;
- return new Stack(p, [], state, pos, pos, 0, [], 0, cx ? new StackContext(cx, cx.start) : null, 0, null);
- }
- /// The stack's current [context](#lr.ContextTracker) value, if
- /// any. Its type will depend on the context tracker's type
- /// parameter, or it will be `null` if there is no context
- /// tracker.
- get context() { return this.curContext ? this.curContext.context : null; }
- // Push a state onto the stack, tracking its start position as well
- // as the buffer base at that point.
- /// @internal
- pushState(state, start) {
- this.stack.push(this.state, start, this.bufferBase + this.buffer.length);
- this.state = state;
- }
- // Apply a reduce action
- /// @internal
- reduce(action) {
- let depth = action >> 19 /* ReduceDepthShift */, type = action & 65535 /* ValueMask */;
- let { parser } = this.p;
- let dPrec = parser.dynamicPrecedence(type);
- if (dPrec)
- this.score += dPrec;
- if (depth == 0) {
- this.pushState(parser.getGoto(this.state, type, true), this.reducePos);
- // Zero-depth reductions are a special case—they add stuff to
- // the stack without popping anything off.
- if (type < parser.minRepeatTerm)
- this.storeNode(type, this.reducePos, this.reducePos, 4, true);
- this.reduceContext(type, this.reducePos);
- return;
- }
- // Find the base index into `this.stack`, content after which will
- // be dropped. Note that with `StayFlag` reductions we need to
- // consume two extra frames (the dummy parent node for the skipped
- // expression and the state that we'll be staying in, which should
- // be moved to `this.state`).
- let base = this.stack.length - ((depth - 1) * 3) - (action & 262144 /* StayFlag */ ? 6 : 0);
- let start = this.stack[base - 2];
- let bufferBase = this.stack[base - 1], count = this.bufferBase + this.buffer.length - bufferBase;
- // Store normal terms or `R -> R R` repeat reductions
- if (type < parser.minRepeatTerm || (action & 131072 /* RepeatFlag */)) {
- let pos = parser.stateFlag(this.state, 1 /* Skipped */) ? this.pos : this.reducePos;
- this.storeNode(type, start, pos, count + 4, true);
- }
- if (action & 262144 /* StayFlag */) {
- this.state = this.stack[base];
- }
- else {
- let baseStateID = this.stack[base - 3];
- this.state = parser.getGoto(baseStateID, type, true);
- }
- while (this.stack.length > base)
- this.stack.pop();
- this.reduceContext(type, start);
- }
- // Shift a value into the buffer
- /// @internal
- storeNode(term, start, end, size = 4, isReduce = false) {
- if (term == 0 /* Err */ &&
- (!this.stack.length || this.stack[this.stack.length - 1] < this.buffer.length + this.bufferBase)) {
- // Try to omit/merge adjacent error nodes
- let cur = this, top = this.buffer.length;
- if (top == 0 && cur.parent) {
- top = cur.bufferBase - cur.parent.bufferBase;
- cur = cur.parent;
- }
- if (top > 0 && cur.buffer[top - 4] == 0 /* Err */ && cur.buffer[top - 1] > -1) {
- if (start == end)
- return;
- if (cur.buffer[top - 2] >= start) {
- cur.buffer[top - 2] = end;
- return;
- }
- }
- }
- if (!isReduce || this.pos == end) { // Simple case, just append
- this.buffer.push(term, start, end, size);
- }
- else { // There may be skipped nodes that have to be moved forward
- let index = this.buffer.length;
- if (index > 0 && this.buffer[index - 4] != 0 /* Err */)
- while (index > 0 && this.buffer[index - 2] > end) {
- // Move this record forward
- this.buffer[index] = this.buffer[index - 4];
- this.buffer[index + 1] = this.buffer[index - 3];
- this.buffer[index + 2] = this.buffer[index - 2];
- this.buffer[index + 3] = this.buffer[index - 1];
- index -= 4;
- if (size > 4)
- size -= 4;
- }
- this.buffer[index] = term;
- this.buffer[index + 1] = start;
- this.buffer[index + 2] = end;
- this.buffer[index + 3] = size;
- }
- }
- // Apply a shift action
- /// @internal
- shift(action, next, nextEnd) {
- let start = this.pos;
- if (action & 131072 /* GotoFlag */) {
- this.pushState(action & 65535 /* ValueMask */, this.pos);
- }
- else if ((action & 262144 /* StayFlag */) == 0) { // Regular shift
- let nextState = action, { parser } = this.p;
- if (nextEnd > this.pos || next <= parser.maxNode) {
- this.pos = nextEnd;
- if (!parser.stateFlag(nextState, 1 /* Skipped */))
- this.reducePos = nextEnd;
- }
- this.pushState(nextState, start);
- this.shiftContext(next, start);
- if (next <= parser.maxNode)
- this.buffer.push(next, start, nextEnd, 4);
- }
- else { // Shift-and-stay, which means this is a skipped token
- this.pos = nextEnd;
- this.shiftContext(next, start);
- if (next <= this.p.parser.maxNode)
- this.buffer.push(next, start, nextEnd, 4);
- }
- }
- // Apply an action
- /// @internal
- apply(action, next, nextEnd) {
- if (action & 65536 /* ReduceFlag */)
- this.reduce(action);
- else
- this.shift(action, next, nextEnd);
- }
- // Add a prebuilt (reused) node into the buffer.
- /// @internal
- useNode(value, next) {
- let index = this.p.reused.length - 1;
- if (index < 0 || this.p.reused[index] != value) {
- this.p.reused.push(value);
- index++;
- }
- let start = this.pos;
- this.reducePos = this.pos = start + value.length;
- this.pushState(next, start);
- this.buffer.push(index, start, this.reducePos, -1 /* size == -1 means this is a reused value */);
- if (this.curContext)
- this.updateContext(this.curContext.tracker.reuse(this.curContext.context, value, this, this.p.stream.reset(this.pos - value.length)));
- }
- // Split the stack. Due to the buffer sharing and the fact
- // that `this.stack` tends to stay quite shallow, this isn't very
- // expensive.
- /// @internal
- split() {
- let parent = this;
- let off = parent.buffer.length;
- // Because the top of the buffer (after this.pos) may be mutated
- // to reorder reductions and skipped tokens, and shared buffers
- // should be immutable, this copies any outstanding skipped tokens
- // to the new buffer, and puts the base pointer before them.
- while (off > 0 && parent.buffer[off - 2] > parent.reducePos)
- off -= 4;
- let buffer = parent.buffer.slice(off), base = parent.bufferBase + off;
- // Make sure parent points to an actual parent with content, if there is such a parent.
- while (parent && base == parent.bufferBase)
- parent = parent.parent;
- return new Stack(this.p, this.stack.slice(), this.state, this.reducePos, this.pos, this.score, buffer, base, this.curContext, this.lookAhead, parent);
- }
- // Try to recover from an error by 'deleting' (ignoring) one token.
- /// @internal
- recoverByDelete(next, nextEnd) {
- let isNode = next <= this.p.parser.maxNode;
- if (isNode)
- this.storeNode(next, this.pos, nextEnd, 4);
- this.storeNode(0 /* Err */, this.pos, nextEnd, isNode ? 8 : 4);
- this.pos = this.reducePos = nextEnd;
- this.score -= 190 /* Delete */;
- }
- /// Check if the given term would be able to be shifted (optionally
- /// after some reductions) on this stack. This can be useful for
- /// external tokenizers that want to make sure they only provide a
- /// given token when it applies.
- canShift(term) {
- for (let sim = new SimulatedStack(this);;) {
- let action = this.p.parser.stateSlot(sim.state, 4 /* DefaultReduce */) || this.p.parser.hasAction(sim.state, term);
- if ((action & 65536 /* ReduceFlag */) == 0)
- return true;
- if (action == 0)
- return false;
- sim.reduce(action);
- }
- }
- // Apply up to Recover.MaxNext recovery actions that conceptually
- // inserts some missing token or rule.
- /// @internal
- recoverByInsert(next) {
- if (this.stack.length >= 300 /* MaxInsertStackDepth */)
- return [];
- let nextStates = this.p.parser.nextStates(this.state);
- if (nextStates.length > 4 /* MaxNext */ << 1 || this.stack.length >= 120 /* DampenInsertStackDepth */) {
- let best = [];
- for (let i = 0, s; i < nextStates.length; i += 2) {
- if ((s = nextStates[i + 1]) != this.state && this.p.parser.hasAction(s, next))
- best.push(nextStates[i], s);
- }
- if (this.stack.length < 120 /* DampenInsertStackDepth */)
- for (let i = 0; best.length < 4 /* MaxNext */ << 1 && i < nextStates.length; i += 2) {
- let s = nextStates[i + 1];
- if (!best.some((v, i) => (i & 1) && v == s))
- best.push(nextStates[i], s);
- }
- nextStates = best;
- }
- let result = [];
- for (let i = 0; i < nextStates.length && result.length < 4 /* MaxNext */; i += 2) {
- let s = nextStates[i + 1];
- if (s == this.state)
- continue;
- let stack = this.split();
- stack.pushState(s, this.pos);
- stack.storeNode(0 /* Err */, stack.pos, stack.pos, 4, true);
- stack.shiftContext(nextStates[i], this.pos);
- stack.score -= 200 /* Insert */;
- result.push(stack);
- }
- return result;
- }
- // Force a reduce, if possible. Return false if that can't
- // be done.
- /// @internal
- forceReduce() {
- let reduce = this.p.parser.stateSlot(this.state, 5 /* ForcedReduce */);
- if ((reduce & 65536 /* ReduceFlag */) == 0)
- return false;
- let { parser } = this.p;
- if (!parser.validAction(this.state, reduce)) {
- let depth = reduce >> 19 /* ReduceDepthShift */, term = reduce & 65535 /* ValueMask */;
- let target = this.stack.length - depth * 3;
- if (target < 0 || parser.getGoto(this.stack[target], term, false) < 0)
- return false;
- this.storeNode(0 /* Err */, this.reducePos, this.reducePos, 4, true);
- this.score -= 100 /* Reduce */;
- }
- this.reducePos = this.pos;
- this.reduce(reduce);
- return true;
- }
- /// @internal
- forceAll() {
- while (!this.p.parser.stateFlag(this.state, 2 /* Accepting */)) {
- if (!this.forceReduce()) {
- this.storeNode(0 /* Err */, this.pos, this.pos, 4, true);
- break;
- }
- }
- return this;
- }
- /// Check whether this state has no further actions (assumed to be a direct descendant of the
- /// top state, since any other states must be able to continue
- /// somehow). @internal
- get deadEnd() {
- if (this.stack.length != 3)
- return false;
- let { parser } = this.p;
- return parser.data[parser.stateSlot(this.state, 1 /* Actions */)] == 65535 /* End */ &&
- !parser.stateSlot(this.state, 4 /* DefaultReduce */);
- }
- /// Restart the stack (put it back in its start state). Only safe
- /// when this.stack.length == 3 (state is directly below the top
- /// state). @internal
- restart() {
- this.state = this.stack[0];
- this.stack.length = 0;
- }
- /// @internal
- sameState(other) {
- if (this.state != other.state || this.stack.length != other.stack.length)
- return false;
- for (let i = 0; i < this.stack.length; i += 3)
- if (this.stack[i] != other.stack[i])
- return false;
- return true;
- }
- /// Get the parser used by this stack.
- get parser() { return this.p.parser; }
- /// Test whether a given dialect (by numeric ID, as exported from
- /// the terms file) is enabled.
- dialectEnabled(dialectID) { return this.p.parser.dialect.flags[dialectID]; }
- shiftContext(term, start) {
- if (this.curContext)
- this.updateContext(this.curContext.tracker.shift(this.curContext.context, term, this, this.p.stream.reset(start)));
- }
- reduceContext(term, start) {
- if (this.curContext)
- this.updateContext(this.curContext.tracker.reduce(this.curContext.context, term, this, this.p.stream.reset(start)));
- }
- /// @internal
- emitContext() {
- let last = this.buffer.length - 1;
- if (last < 0 || this.buffer[last] != -3)
- this.buffer.push(this.curContext.hash, this.reducePos, this.reducePos, -3);
- }
- /// @internal
- emitLookAhead() {
- let last = this.buffer.length - 1;
- if (last < 0 || this.buffer[last] != -4)
- this.buffer.push(this.lookAhead, this.reducePos, this.reducePos, -4);
- }
- updateContext(context) {
- if (context != this.curContext.context) {
- let newCx = new StackContext(this.curContext.tracker, context);
- if (newCx.hash != this.curContext.hash)
- this.emitContext();
- this.curContext = newCx;
- }
- }
- /// @internal
- setLookAhead(lookAhead) {
- if (lookAhead > this.lookAhead) {
- this.emitLookAhead();
- this.lookAhead = lookAhead;
- }
- }
- /// @internal
- close() {
- if (this.curContext && this.curContext.tracker.strict)
- this.emitContext();
- if (this.lookAhead > 0)
- this.emitLookAhead();
- }
- }
- class StackContext {
- constructor(tracker, context) {
- this.tracker = tracker;
- this.context = context;
- this.hash = tracker.strict ? tracker.hash(context) : 0;
- }
- }
- var Recover;
- (function (Recover) {
- Recover[Recover["Insert"] = 200] = "Insert";
- Recover[Recover["Delete"] = 190] = "Delete";
- Recover[Recover["Reduce"] = 100] = "Reduce";
- Recover[Recover["MaxNext"] = 4] = "MaxNext";
- Recover[Recover["MaxInsertStackDepth"] = 300] = "MaxInsertStackDepth";
- Recover[Recover["DampenInsertStackDepth"] = 120] = "DampenInsertStackDepth";
- })(Recover || (Recover = {}));
- // Used to cheaply run some reductions to scan ahead without mutating
- // an entire stack
- class SimulatedStack {
- constructor(start) {
- this.start = start;
- this.state = start.state;
- this.stack = start.stack;
- this.base = this.stack.length;
- }
- reduce(action) {
- let term = action & 65535 /* ValueMask */, depth = action >> 19 /* ReduceDepthShift */;
- if (depth == 0) {
- if (this.stack == this.start.stack)
- this.stack = this.stack.slice();
- this.stack.push(this.state, 0, 0);
- this.base += 3;
- }
- else {
- this.base -= (depth - 1) * 3;
- }
- let goto = this.start.p.parser.getGoto(this.stack[this.base - 3], term, true);
- this.state = goto;
- }
- }
- // This is given to `Tree.build` to build a buffer, and encapsulates
- // the parent-stack-walking necessary to read the nodes.
- class StackBufferCursor {
- constructor(stack, pos, index) {
- this.stack = stack;
- this.pos = pos;
- this.index = index;
- this.buffer = stack.buffer;
- if (this.index == 0)
- this.maybeNext();
- }
- static create(stack, pos = stack.bufferBase + stack.buffer.length) {
- return new StackBufferCursor(stack, pos, pos - stack.bufferBase);
- }
- maybeNext() {
- let next = this.stack.parent;
- if (next != null) {
- this.index = this.stack.bufferBase - next.bufferBase;
- this.stack = next;
- this.buffer = next.buffer;
- }
- }
- get id() { return this.buffer[this.index - 4]; }
- get start() { return this.buffer[this.index - 3]; }
- get end() { return this.buffer[this.index - 2]; }
- get size() { return this.buffer[this.index - 1]; }
- next() {
- this.index -= 4;
- this.pos -= 4;
- if (this.index == 0)
- this.maybeNext();
- }
- fork() {
- return new StackBufferCursor(this.stack, this.pos, this.index);
- }
- }
- class CachedToken {
- constructor() {
- this.start = -1;
- this.value = -1;
- this.end = -1;
- this.extended = -1;
- this.lookAhead = 0;
- this.mask = 0;
- this.context = 0;
- }
- }
- const nullToken = new CachedToken;
- /// [Tokenizers](#lr.ExternalTokenizer) interact with the input
- /// through this interface. It presents the input as a stream of
- /// characters, tracking lookahead and hiding the complexity of
- /// [ranges](#common.Parser.parse^ranges) from tokenizer code.
- class InputStream {
- /// @internal
- constructor(
- /// @internal
- input,
- /// @internal
- ranges) {
- this.input = input;
- this.ranges = ranges;
- /// @internal
- this.chunk = "";
- /// @internal
- this.chunkOff = 0;
- /// Backup chunk
- this.chunk2 = "";
- this.chunk2Pos = 0;
- /// The character code of the next code unit in the input, or -1
- /// when the stream is at the end of the input.
- this.next = -1;
- /// @internal
- this.token = nullToken;
- this.rangeIndex = 0;
- this.pos = this.chunkPos = ranges[0].from;
- this.range = ranges[0];
- this.end = ranges[ranges.length - 1].to;
- this.readNext();
- }
- /// @internal
- resolveOffset(offset, assoc) {
- let range = this.range, index = this.rangeIndex;
- let pos = this.pos + offset;
- while (pos < range.from) {
- if (!index)
- return null;
- let next = this.ranges[--index];
- pos -= range.from - next.to;
- range = next;
- }
- while (assoc < 0 ? pos > range.to : pos >= range.to) {
- if (index == this.ranges.length - 1)
- return null;
- let next = this.ranges[++index];
- pos += next.from - range.to;
- range = next;
- }
- return pos;
- }
- /// Look at a code unit near the stream position. `.peek(0)` equals
- /// `.next`, `.peek(-1)` gives you the previous character, and so
- /// on.
- ///
- /// Note that looking around during tokenizing creates dependencies
- /// on potentially far-away content, which may reduce the
- /// effectiveness incremental parsing—when looking forward—or even
- /// cause invalid reparses when looking backward more than 25 code
- /// units, since the library does not track lookbehind.
- peek(offset) {
- let idx = this.chunkOff + offset, pos, result;
- if (idx >= 0 && idx < this.chunk.length) {
- pos = this.pos + offset;
- result = this.chunk.charCodeAt(idx);
- }
- else {
- let resolved = this.resolveOffset(offset, 1);
- if (resolved == null)
- return -1;
- pos = resolved;
- if (pos >= this.chunk2Pos && pos < this.chunk2Pos + this.chunk2.length) {
- result = this.chunk2.charCodeAt(pos - this.chunk2Pos);
- }
- else {
- let i = this.rangeIndex, range = this.range;
- while (range.to <= pos)
- range = this.ranges[++i];
- this.chunk2 = this.input.chunk(this.chunk2Pos = pos);
- if (pos + this.chunk2.length > range.to)
- this.chunk2 = this.chunk2.slice(0, range.to - pos);
- result = this.chunk2.charCodeAt(0);
- }
- }
- if (pos >= this.token.lookAhead)
- this.token.lookAhead = pos + 1;
- return result;
- }
- /// Accept a token. By default, the end of the token is set to the
- /// current stream position, but you can pass an offset (relative to
- /// the stream position) to change that.
- acceptToken(token, endOffset = 0) {
- let end = endOffset ? this.resolveOffset(endOffset, -1) : this.pos;
- if (end == null || end < this.token.start)
- throw new RangeError("Token end out of bounds");
- this.token.value = token;
- this.token.end = end;
- }
- getChunk() {
- if (this.pos >= this.chunk2Pos && this.pos < this.chunk2Pos + this.chunk2.length) {
- let { chunk, chunkPos } = this;
- this.chunk = this.chunk2;
- this.chunkPos = this.chunk2Pos;
- this.chunk2 = chunk;
- this.chunk2Pos = chunkPos;
- this.chunkOff = this.pos - this.chunkPos;
- }
- else {
- this.chunk2 = this.chunk;
- this.chunk2Pos = this.chunkPos;
- let nextChunk = this.input.chunk(this.pos);
- let end = this.pos + nextChunk.length;
- this.chunk = end > this.range.to ? nextChunk.slice(0, this.range.to - this.pos) : nextChunk;
- this.chunkPos = this.pos;
- this.chunkOff = 0;
- }
- }
- readNext() {
- if (this.chunkOff >= this.chunk.length) {
- this.getChunk();
- if (this.chunkOff == this.chunk.length)
- return this.next = -1;
- }
- return this.next = this.chunk.charCodeAt(this.chunkOff);
- }
- /// Move the stream forward N (defaults to 1) code units. Returns
- /// the new value of [`next`](#lr.InputStream.next).
- advance(n = 1) {
- this.chunkOff += n;
- while (this.pos + n >= this.range.to) {
- if (this.rangeIndex == this.ranges.length - 1)
- return this.setDone();
- n -= this.range.to - this.pos;
- this.range = this.ranges[++this.rangeIndex];
- this.pos = this.range.from;
- }
- this.pos += n;
- if (this.pos >= this.token.lookAhead)
- this.token.lookAhead = this.pos + 1;
- return this.readNext();
- }
- setDone() {
- this.pos = this.chunkPos = this.end;
- this.range = this.ranges[this.rangeIndex = this.ranges.length - 1];
- this.chunk = "";
- return this.next = -1;
- }
- /// @internal
- reset(pos, token) {
- if (token) {
- this.token = token;
- token.start = pos;
- token.lookAhead = pos + 1;
- token.value = token.extended = -1;
- }
- else {
- this.token = nullToken;
- }
- if (this.pos != pos) {
- this.pos = pos;
- if (pos == this.end) {
- this.setDone();
- return this;
- }
- while (pos < this.range.from)
- this.range = this.ranges[--this.rangeIndex];
- while (pos >= this.range.to)
- this.range = this.ranges[++this.rangeIndex];
- if (pos >= this.chunkPos && pos < this.chunkPos + this.chunk.length) {
- this.chunkOff = pos - this.chunkPos;
- }
- else {
- this.chunk = "";
- this.chunkOff = 0;
- }
- this.readNext();
- }
- return this;
- }
- /// @internal
- read(from, to) {
- if (from >= this.chunkPos && to <= this.chunkPos + this.chunk.length)
- return this.chunk.slice(from - this.chunkPos, to - this.chunkPos);
- if (from >= this.chunk2Pos && to <= this.chunk2Pos + this.chunk2.length)
- return this.chunk2.slice(from - this.chunk2Pos, to - this.chunk2Pos);
- if (from >= this.range.from && to <= this.range.to)
- return this.input.read(from, to);
- let result = "";
- for (let r of this.ranges) {
- if (r.from >= to)
- break;
- if (r.to > from)
- result += this.input.read(Math.max(r.from, from), Math.min(r.to, to));
- }
- return result;
- }
- }
- /// @internal
- class TokenGroup {
- constructor(data, id) {
- this.data = data;
- this.id = id;
- }
- token(input, stack) { readToken(this.data, input, stack, this.id); }
- }
- TokenGroup.prototype.contextual = TokenGroup.prototype.fallback = TokenGroup.prototype.extend = false;
- /// `@external tokens` declarations in the grammar should resolve to
- /// an instance of this class.
- class ExternalTokenizer {
- /// Create a tokenizer. The first argument is the function that,
- /// given an input stream, scans for the types of tokens it
- /// recognizes at the stream's position, and calls
- /// [`acceptToken`](#lr.InputStream.acceptToken) when it finds
- /// one.
- constructor(
- /// @internal
- token, options = {}) {
- this.token = token;
- this.contextual = !!options.contextual;
- this.fallback = !!options.fallback;
- this.extend = !!options.extend;
- }
- }
- // Tokenizer data is stored a big uint16 array containing, for each
- // state:
- //
- // - A group bitmask, indicating what token groups are reachable from
- // this state, so that paths that can only lead to tokens not in
- // any of the current groups can be cut off early.
- //
- // - The position of the end of the state's sequence of accepting
- // tokens
- //
- // - The number of outgoing edges for the state
- //
- // - The accepting tokens, as (token id, group mask) pairs
- //
- // - The outgoing edges, as (start character, end character, state
- // index) triples, with end character being exclusive
- //
- // This function interprets that data, running through a stream as
- // long as new states with the a matching group mask can be reached,
- // and updating `input.token` when it matches a token.
- function readToken(data, input, stack, group) {
- let state = 0, groupMask = 1 << group, { parser } = stack.p, { dialect } = parser;
- scan: for (;;) {
- if ((groupMask & data[state]) == 0)
- break;
- let accEnd = data[state + 1];
- // Check whether this state can lead to a token in the current group
- // Accept tokens in this state, possibly overwriting
- // lower-precedence / shorter tokens
- for (let i = state + 3; i < accEnd; i += 2)
- if ((data[i + 1] & groupMask) > 0) {
- let term = data[i];
- if (dialect.allows(term) &&
- (input.token.value == -1 || input.token.value == term || parser.overrides(term, input.token.value))) {
- input.acceptToken(term);
- break;
- }
- }
- let next = input.next, low = 0, high = data[state + 2];
- // Special case for EOF
- if (input.next < 0 && high > low && data[accEnd + high * 3 - 3] == 65535 /* End */) {
- state = data[accEnd + high * 3 - 1];
- continue scan;
- }
- // Do a binary search on the state's edges
- for (; low < high;) {
- let mid = (low + high) >> 1;
- let index = accEnd + mid + (mid << 1);
- let from = data[index], to = data[index + 1];
- if (next < from)
- high = mid;
- else if (next >= to)
- low = mid + 1;
- else {
- state = data[index + 2];
- input.advance();
- continue scan;
- }
- }
- break;
- }
- }
- // See lezer-generator/src/encode.ts for comments about the encoding
- // used here
- function decodeArray(input, Type = Uint16Array) {
- if (typeof input != "string")
- return input;
- let array = null;
- for (let pos = 0, out = 0; pos < input.length;) {
- let value = 0;
- for (;;) {
- let next = input.charCodeAt(pos++), stop = false;
- if (next == 126 /* BigValCode */) {
- value = 65535 /* BigVal */;
- break;
- }
- if (next >= 92 /* Gap2 */)
- next--;
- if (next >= 34 /* Gap1 */)
- next--;
- let digit = next - 32 /* Start */;
- if (digit >= 46 /* Base */) {
- digit -= 46 /* Base */;
- stop = true;
- }
- value += digit;
- if (stop)
- break;
- value *= 46 /* Base */;
- }
- if (array)
- array[out++] = value;
- else
- array = new Type(value);
- }
- return array;
- }
- // Environment variable used to control console output
- const verbose = typeof process != "undefined" && process.env && /\bparse\b/.test(process.env.LOG);
- let stackIDs = null;
- var Safety;
- (function (Safety) {
- Safety[Safety["Margin"] = 25] = "Margin";
- })(Safety || (Safety = {}));
- function cutAt(tree, pos, side) {
- let cursor = tree.cursor(IterMode.IncludeAnonymous);
- cursor.moveTo(pos);
- for (;;) {
- if (!(side < 0 ? cursor.childBefore(pos) : cursor.childAfter(pos)))
- for (;;) {
- if ((side < 0 ? cursor.to < pos : cursor.from > pos) && !cursor.type.isError)
- return side < 0 ? Math.max(0, Math.min(cursor.to - 1, pos - 25 /* Margin */))
- : Math.min(tree.length, Math.max(cursor.from + 1, pos + 25 /* Margin */));
- if (side < 0 ? cursor.prevSibling() : cursor.nextSibling())
- break;
- if (!cursor.parent())
- return side < 0 ? 0 : tree.length;
- }
- }
- }
- class FragmentCursor {
- constructor(fragments, nodeSet) {
- this.fragments = fragments;
- this.nodeSet = nodeSet;
- this.i = 0;
- this.fragment = null;
- this.safeFrom = -1;
- this.safeTo = -1;
- this.trees = [];
- this.start = [];
- this.index = [];
- this.nextFragment();
- }
- nextFragment() {
- let fr = this.fragment = this.i == this.fragments.length ? null : this.fragments[this.i++];
- if (fr) {
- this.safeFrom = fr.openStart ? cutAt(fr.tree, fr.from + fr.offset, 1) - fr.offset : fr.from;
- this.safeTo = fr.openEnd ? cutAt(fr.tree, fr.to + fr.offset, -1) - fr.offset : fr.to;
- while (this.trees.length) {
- this.trees.pop();
- this.start.pop();
- this.index.pop();
- }
- this.trees.push(fr.tree);
- this.start.push(-fr.offset);
- this.index.push(0);
- this.nextStart = this.safeFrom;
- }
- else {
- this.nextStart = 1e9;
- }
- }
- // `pos` must be >= any previously given `pos` for this cursor
- nodeAt(pos) {
- if (pos < this.nextStart)
- return null;
- while (this.fragment && this.safeTo <= pos)
- this.nextFragment();
- if (!this.fragment)
- return null;
- for (;;) {
- let last = this.trees.length - 1;
- if (last < 0) { // End of tree
- this.nextFragment();
- return null;
- }
- let top = this.trees[last], index = this.index[last];
- if (index == top.children.length) {
- this.trees.pop();
- this.start.pop();
- this.index.pop();
- continue;
- }
- let next = top.children[index];
- let start = this.start[last] + top.positions[index];
- if (start > pos) {
- this.nextStart = start;
- return null;
- }
- if (next instanceof Tree) {
- if (start == pos) {
- if (start < this.safeFrom)
- return null;
- let end = start + next.length;
- if (end <= this.safeTo) {
- let lookAhead = next.prop(NodeProp.lookAhead);
- if (!lookAhead || end + lookAhead < this.fragment.to)
- return next;
- }
- }
- this.index[last]++;
- if (start + next.length >= Math.max(this.safeFrom, pos)) { // Enter this node
- this.trees.push(next);
- this.start.push(start);
- this.index.push(0);
- }
- }
- else {
- this.index[last]++;
- this.nextStart = start + next.length;
- }
- }
- }
- }
- class TokenCache {
- constructor(parser, stream) {
- this.stream = stream;
- this.tokens = [];
- this.mainToken = null;
- this.actions = [];
- this.tokens = parser.tokenizers.map(_ => new CachedToken);
- }
- getActions(stack) {
- let actionIndex = 0;
- let main = null;
- let { parser } = stack.p, { tokenizers } = parser;
- let mask = parser.stateSlot(stack.state, 3 /* TokenizerMask */);
- let context = stack.curContext ? stack.curContext.hash : 0;
- let lookAhead = 0;
- for (let i = 0; i < tokenizers.length; i++) {
- if (((1 << i) & mask) == 0)
- continue;
- let tokenizer = tokenizers[i], token = this.tokens[i];
- if (main && !tokenizer.fallback)
- continue;
- if (tokenizer.contextual || token.start != stack.pos || token.mask != mask || token.context != context) {
- this.updateCachedToken(token, tokenizer, stack);
- token.mask = mask;
- token.context = context;
- }
- if (token.lookAhead > token.end + 25 /* Margin */)
- lookAhead = Math.max(token.lookAhead, lookAhead);
- if (token.value != 0 /* Err */) {
- let startIndex = actionIndex;
- if (token.extended > -1)
- actionIndex = this.addActions(stack, token.extended, token.end, actionIndex);
- actionIndex = this.addActions(stack, token.value, token.end, actionIndex);
- if (!tokenizer.extend) {
- main = token;
- if (actionIndex > startIndex)
- break;
- }
- }
- }
- while (this.actions.length > actionIndex)
- this.actions.pop();
- if (lookAhead)
- stack.setLookAhead(lookAhead);
- if (!main && stack.pos == this.stream.end) {
- main = new CachedToken;
- main.value = stack.p.parser.eofTerm;
- main.start = main.end = stack.pos;
- actionIndex = this.addActions(stack, main.value, main.end, actionIndex);
- }
- this.mainToken = main;
- return this.actions;
- }
- getMainToken(stack) {
- if (this.mainToken)
- return this.mainToken;
- let main = new CachedToken, { pos, p } = stack;
- main.start = pos;
- main.end = Math.min(pos + 1, p.stream.end);
- main.value = pos == p.stream.end ? p.parser.eofTerm : 0 /* Err */;
- return main;
- }
- updateCachedToken(token, tokenizer, stack) {
- tokenizer.token(this.stream.reset(stack.pos, token), stack);
- if (token.value > -1) {
- let { parser } = stack.p;
- for (let i = 0; i < parser.specialized.length; i++)
- if (parser.specialized[i] == token.value) {
- let result = parser.specializers[i](this.stream.read(token.start, token.end), stack);
- if (result >= 0 && stack.p.parser.dialect.allows(result >> 1)) {
- if ((result & 1) == 0 /* Specialize */)
- token.value = result >> 1;
- else
- token.extended = result >> 1;
- break;
- }
- }
- }
- else {
- token.value = 0 /* Err */;
- token.end = Math.min(stack.p.stream.end, stack.pos + 1);
- }
- }
- putAction(action, token, end, index) {
- // Don't add duplicate actions
- for (let i = 0; i < index; i += 3)
- if (this.actions[i] == action)
- return index;
- this.actions[index++] = action;
- this.actions[index++] = token;
- this.actions[index++] = end;
- return index;
- }
- addActions(stack, token, end, index) {
- let { state } = stack, { parser } = stack.p, { data } = parser;
- for (let set = 0; set < 2; set++) {
- for (let i = parser.stateSlot(state, set ? 2 /* Skip */ : 1 /* Actions */);; i += 3) {
- if (data[i] == 65535 /* End */) {
- if (data[i + 1] == 1 /* Next */) {
- i = pair(data, i + 2);
- }
- else {
- if (index == 0 && data[i + 1] == 2 /* Other */)
- index = this.putAction(pair(data, i + 2), token, end, index);
- break;
- }
- }
- if (data[i] == token)
- index = this.putAction(pair(data, i + 1), token, end, index);
- }
- }
- return index;
- }
- }
- var Rec;
- (function (Rec) {
- Rec[Rec["Distance"] = 5] = "Distance";
- Rec[Rec["MaxRemainingPerStep"] = 3] = "MaxRemainingPerStep";
- // When two stacks have been running independently long enough to
- // add this many elements to their buffers, prune one.
- Rec[Rec["MinBufferLengthPrune"] = 500] = "MinBufferLengthPrune";
- Rec[Rec["ForceReduceLimit"] = 10] = "ForceReduceLimit";
- // Once a stack reaches this depth (in .stack.length) force-reduce
- // it back to CutTo to avoid creating trees that overflow the stack
- // on recursive traversal.
- Rec[Rec["CutDepth"] = 15000] = "CutDepth";
- Rec[Rec["CutTo"] = 9000] = "CutTo";
- })(Rec || (Rec = {}));
- class Parse {
- constructor(parser, input, fragments, ranges) {
- this.parser = parser;
- this.input = input;
- this.ranges = ranges;
- this.recovering = 0;
- this.nextStackID = 0x2654; // ♔, ♕, ♖, ♗, ♘, ♙, ♠, ♡, ♢, ♣, ♤, ♥, ♦, ♧
- this.minStackPos = 0;
- this.reused = [];
- this.stoppedAt = null;
- this.stream = new InputStream(input, ranges);
- this.tokens = new TokenCache(parser, this.stream);
- this.topTerm = parser.top[1];
- let { from } = ranges[0];
- this.stacks = [Stack.start(this, parser.top[0], from)];
- this.fragments = fragments.length && this.stream.end - from > parser.bufferLength * 4
- ? new FragmentCursor(fragments, parser.nodeSet) : null;
- }
- get parsedPos() {
- return this.minStackPos;
- }
- // Move the parser forward. This will process all parse stacks at
- // `this.pos` and try to advance them to a further position. If no
- // stack for such a position is found, it'll start error-recovery.
- //
- // When the parse is finished, this will return a syntax tree. When
- // not, it returns `null`.
- advance() {
- let stacks = this.stacks, pos = this.minStackPos;
- // This will hold stacks beyond `pos`.
- let newStacks = this.stacks = [];
- let stopped, stoppedTokens;
- // Keep advancing any stacks at `pos` until they either move
- // forward or can't be advanced. Gather stacks that can't be
- // advanced further in `stopped`.
- for (let i = 0; i < stacks.length; i++) {
- let stack = stacks[i];
- for (;;) {
- this.tokens.mainToken = null;
- if (stack.pos > pos) {
- newStacks.push(stack);
- }
- else if (this.advanceStack(stack, newStacks, stacks)) {
- continue;
- }
- else {
- if (!stopped) {
- stopped = [];
- stoppedTokens = [];
- }
- stopped.push(stack);
- let tok = this.tokens.getMainToken(stack);
- stoppedTokens.push(tok.value, tok.end);
- }
- break;
- }
- }
- if (!newStacks.length) {
- let finished = stopped && findFinished(stopped);
- if (finished)
- return this.stackToTree(finished);
- if (this.parser.strict) {
- if (verbose && stopped)
- console.log("Stuck with token " + (this.tokens.mainToken ? this.parser.getName(this.tokens.mainToken.value) : "none"));
- throw new SyntaxError("No parse at " + pos);
- }
- if (!this.recovering)
- this.recovering = 5 /* Distance */;
- }
- if (this.recovering && stopped) {
- let finished = this.stoppedAt != null && stopped[0].pos > this.stoppedAt ? stopped[0]
- : this.runRecovery(stopped, stoppedTokens, newStacks);
- if (finished)
- return this.stackToTree(finished.forceAll());
- }
- if (this.recovering) {
- let maxRemaining = this.recovering == 1 ? 1 : this.recovering * 3 /* MaxRemainingPerStep */;
- if (newStacks.length > maxRemaining) {
- newStacks.sort((a, b) => b.score - a.score);
- while (newStacks.length > maxRemaining)
- newStacks.pop();
- }
- if (newStacks.some(s => s.reducePos > pos))
- this.recovering--;
- }
- else if (newStacks.length > 1) {
- // Prune stacks that are in the same state, or that have been
- // running without splitting for a while, to avoid getting stuck
- // with multiple successful stacks running endlessly on.
- outer: for (let i = 0; i < newStacks.length - 1; i++) {
- let stack = newStacks[i];
- for (let j = i + 1; j < newStacks.length; j++) {
- let other = newStacks[j];
- if (stack.sameState(other) ||
- stack.buffer.length > 500 /* MinBufferLengthPrune */ && other.buffer.length > 500 /* MinBufferLengthPrune */) {
- if (((stack.score - other.score) || (stack.buffer.length - other.buffer.length)) > 0) {
- newStacks.splice(j--, 1);
- }
- else {
- newStacks.splice(i--, 1);
- continue outer;
- }
- }
- }
- }
- }
- this.minStackPos = newStacks[0].pos;
- for (let i = 1; i < newStacks.length; i++)
- if (newStacks[i].pos < this.minStackPos)
- this.minStackPos = newStacks[i].pos;
- return null;
- }
- stopAt(pos) {
- if (this.stoppedAt != null && this.stoppedAt < pos)
- throw new RangeError("Can't move stoppedAt forward");
- this.stoppedAt = pos;
- }
- // Returns an updated version of the given stack, or null if the
- // stack can't advance normally. When `split` and `stacks` are
- // given, stacks split off by ambiguous operations will be pushed to
- // `split`, or added to `stacks` if they move `pos` forward.
- advanceStack(stack, stacks, split) {
- let start = stack.pos, { parser } = this;
- let base = verbose ? this.stackID(stack) + " -> " : "";
- if (this.stoppedAt != null && start > this.stoppedAt)
- return stack.forceReduce() ? stack : null;
- if (this.fragments) {
- let strictCx = stack.curContext && stack.curContext.tracker.strict, cxHash = strictCx ? stack.curContext.hash : 0;
- for (let cached = this.fragments.nodeAt(start); cached;) {
- let match = this.parser.nodeSet.types[cached.type.id] == cached.type ? parser.getGoto(stack.state, cached.type.id) : -1;
- if (match > -1 && cached.length && (!strictCx || (cached.prop(NodeProp.contextHash) || 0) == cxHash)) {
- stack.useNode(cached, match);
- if (verbose)
- console.log(base + this.stackID(stack) + ` (via reuse of ${parser.getName(cached.type.id)})`);
- return true;
- }
- if (!(cached instanceof Tree) || cached.children.length == 0 || cached.positions[0] > 0)
- break;
- let inner = cached.children[0];
- if (inner instanceof Tree && cached.positions[0] == 0)
- cached = inner;
- else
- break;
- }
- }
- let defaultReduce = parser.stateSlot(stack.state, 4 /* DefaultReduce */);
- if (defaultReduce > 0) {
- stack.reduce(defaultReduce);
- if (verbose)
- console.log(base + this.stackID(stack) + ` (via always-reduce ${parser.getName(defaultReduce & 65535 /* ValueMask */)})`);
- return true;
- }
- if (stack.stack.length >= 15000 /* CutDepth */) {
- while (stack.stack.length > 9000 /* CutTo */ && stack.forceReduce()) { }
- }
- let actions = this.tokens.getActions(stack);
- for (let i = 0; i < actions.length;) {
- let action = actions[i++], term = actions[i++], end = actions[i++];
- let last = i == actions.length || !split;
- let localStack = last ? stack : stack.split();
- localStack.apply(action, term, end);
- if (verbose)
- console.log(base + this.stackID(localStack) + ` (via ${(action & 65536 /* ReduceFlag */) == 0 ? "shift"
- : `reduce of ${parser.getName(action & 65535 /* ValueMask */)}`} for ${parser.getName(term)} @ ${start}${localStack == stack ? "" : ", split"})`);
- if (last)
- return true;
- else if (localStack.pos > start)
- stacks.push(localStack);
- else
- split.push(localStack);
- }
- return false;
- }
- // Advance a given stack forward as far as it will go. Returns the
- // (possibly updated) stack if it got stuck, or null if it moved
- // forward and was given to `pushStackDedup`.
- advanceFully(stack, newStacks) {
- let pos = stack.pos;
- for (;;) {
- if (!this.advanceStack(stack, null, null))
- return false;
- if (stack.pos > pos) {
- pushStackDedup(stack, newStacks);
- return true;
- }
- }
- }
- runRecovery(stacks, tokens, newStacks) {
- let finished = null, restarted = false;
- for (let i = 0; i < stacks.length; i++) {
- let stack = stacks[i], token = tokens[i << 1], tokenEnd = tokens[(i << 1) + 1];
- let base = verbose ? this.stackID(stack) + " -> " : "";
- if (stack.deadEnd) {
- if (restarted)
- continue;
- restarted = true;
- stack.restart();
- if (verbose)
- console.log(base + this.stackID(stack) + " (restarted)");
- let done = this.advanceFully(stack, newStacks);
- if (done)
- continue;
- }
- let force = stack.split(), forceBase = base;
- for (let j = 0; force.forceReduce() && j < 10 /* ForceReduceLimit */; j++) {
- if (verbose)
- console.log(forceBase + this.stackID(force) + " (via force-reduce)");
- let done = this.advanceFully(force, newStacks);
- if (done)
- break;
- if (verbose)
- forceBase = this.stackID(force) + " -> ";
- }
- for (let insert of stack.recoverByInsert(token)) {
- if (verbose)
- console.log(base + this.stackID(insert) + " (via recover-insert)");
- this.advanceFully(insert, newStacks);
- }
- if (this.stream.end > stack.pos) {
- if (tokenEnd == stack.pos) {
- tokenEnd++;
- token = 0 /* Err */;
- }
- stack.recoverByDelete(token, tokenEnd);
- if (verbose)
- console.log(base + this.stackID(stack) + ` (via recover-delete ${this.parser.getName(token)})`);
- pushStackDedup(stack, newStacks);
- }
- else if (!finished || finished.score < stack.score) {
- finished = stack;
- }
- }
- return finished;
- }
- // Convert the stack's buffer to a syntax tree.
- stackToTree(stack) {
- stack.close();
- return Tree.build({ buffer: StackBufferCursor.create(stack),
- nodeSet: this.parser.nodeSet,
- topID: this.topTerm,
- maxBufferLength: this.parser.bufferLength,
- reused: this.reused,
- start: this.ranges[0].from,
- length: stack.pos - this.ranges[0].from,
- minRepeatType: this.parser.minRepeatTerm });
- }
- stackID(stack) {
- let id = (stackIDs || (stackIDs = new WeakMap)).get(stack);
- if (!id)
- stackIDs.set(stack, id = String.fromCodePoint(this.nextStackID++));
- return id + stack;
- }
- }
- function pushStackDedup(stack, newStacks) {
- for (let i = 0; i < newStacks.length; i++) {
- let other = newStacks[i];
- if (other.pos == stack.pos && other.sameState(stack)) {
- if (newStacks[i].score < stack.score)
- newStacks[i] = stack;
- return;
- }
- }
- newStacks.push(stack);
- }
- class Dialect {
- constructor(source, flags, disabled) {
- this.source = source;
- this.flags = flags;
- this.disabled = disabled;
- }
- allows(term) { return !this.disabled || this.disabled[term] == 0; }
- }
- const id = x => x;
- /// Context trackers are used to track stateful context (such as
- /// indentation in the Python grammar, or parent elements in the XML
- /// grammar) needed by external tokenizers. You declare them in a
- /// grammar file as `@context exportName from "module"`.
- ///
- /// Context values should be immutable, and can be updated (replaced)
- /// on shift or reduce actions.
- ///
- /// The export used in a `@context` declaration should be of this
- /// type.
- class ContextTracker {
- /// Define a context tracker.
- constructor(spec) {
- this.start = spec.start;
- this.shift = spec.shift || id;
- this.reduce = spec.reduce || id;
- this.reuse = spec.reuse || id;
- this.hash = spec.hash || (() => 0);
- this.strict = spec.strict !== false;
- }
- }
- /// Holds the parse tables for a given grammar, as generated by
- /// `lezer-generator`, and provides [methods](#common.Parser) to parse
- /// content with.
- class LRParser extends Parser {
- /// @internal
- constructor(spec) {
- super();
- /// @internal
- this.wrappers = [];
- if (spec.version != 14 /* Version */)
- throw new RangeError(`Parser version (${spec.version}) doesn't match runtime version (${14 /* Version */})`);
- let nodeNames = spec.nodeNames.split(" ");
- this.minRepeatTerm = nodeNames.length;
- for (let i = 0; i < spec.repeatNodeCount; i++)
- nodeNames.push("");
- let topTerms = Object.keys(spec.topRules).map(r => spec.topRules[r][1]);
- let nodeProps = [];
- for (let i = 0; i < nodeNames.length; i++)
- nodeProps.push([]);
- function setProp(nodeID, prop, value) {
- nodeProps[nodeID].push([prop, prop.deserialize(String(value))]);
- }
- if (spec.nodeProps)
- for (let propSpec of spec.nodeProps) {
- let prop = propSpec[0];
- if (typeof prop == "string")
- prop = NodeProp[prop];
- for (let i = 1; i < propSpec.length;) {
- let next = propSpec[i++];
- if (next >= 0) {
- setProp(next, prop, propSpec[i++]);
- }
- else {
- let value = propSpec[i + -next];
- for (let j = -next; j > 0; j--)
- setProp(propSpec[i++], prop, value);
- i++;
- }
- }
- }
- this.nodeSet = new NodeSet(nodeNames.map((name, i) => NodeType.define({
- name: i >= this.minRepeatTerm ? undefined : name,
- id: i,
- props: nodeProps[i],
- top: topTerms.indexOf(i) > -1,
- error: i == 0,
- skipped: spec.skippedNodes && spec.skippedNodes.indexOf(i) > -1
- })));
- if (spec.propSources)
- this.nodeSet = this.nodeSet.extend(...spec.propSources);
- this.strict = false;
- this.bufferLength = DefaultBufferLength;
- let tokenArray = decodeArray(spec.tokenData);
- this.context = spec.context;
- this.specialized = new Uint16Array(spec.specialized ? spec.specialized.length : 0);
- this.specializers = [];
- if (spec.specialized)
- for (let i = 0; i < spec.specialized.length; i++) {
- this.specialized[i] = spec.specialized[i].term;
- this.specializers[i] = spec.specialized[i].get;
- }
- this.states = decodeArray(spec.states, Uint32Array);
- this.data = decodeArray(spec.stateData);
- this.goto = decodeArray(spec.goto);
- this.maxTerm = spec.maxTerm;
- this.tokenizers = spec.tokenizers.map(value => typeof value == "number" ? new TokenGroup(tokenArray, value) : value);
- this.topRules = spec.topRules;
- this.dialects = spec.dialects || {};
- this.dynamicPrecedences = spec.dynamicPrecedences || null;
- this.tokenPrecTable = spec.tokenPrec;
- this.termNames = spec.termNames || null;
- this.maxNode = this.nodeSet.types.length - 1;
- this.dialect = this.parseDialect();
- this.top = this.topRules[Object.keys(this.topRules)[0]];
- }
- createParse(input, fragments, ranges) {
- let parse = new Parse(this, input, fragments, ranges);
- for (let w of this.wrappers)
- parse = w(parse, input, fragments, ranges);
- return parse;
- }
- /// Get a goto table entry @internal
- getGoto(state, term, loose = false) {
- let table = this.goto;
- if (term >= table[0])
- return -1;
- for (let pos = table[term + 1];;) {
- let groupTag = table[pos++], last = groupTag & 1;
- let target = table[pos++];
- if (last && loose)
- return target;
- for (let end = pos + (groupTag >> 1); pos < end; pos++)
- if (table[pos] == state)
- return target;
- if (last)
- return -1;
- }
- }
- /// Check if this state has an action for a given terminal @internal
- hasAction(state, terminal) {
- let data = this.data;
- for (let set = 0; set < 2; set++) {
- for (let i = this.stateSlot(state, set ? 2 /* Skip */ : 1 /* Actions */), next;; i += 3) {
- if ((next = data[i]) == 65535 /* End */) {
- if (data[i + 1] == 1 /* Next */)
- next = data[i = pair(data, i + 2)];
- else if (data[i + 1] == 2 /* Other */)
- return pair(data, i + 2);
- else
- break;
- }
- if (next == terminal || next == 0 /* Err */)
- return pair(data, i + 1);
- }
- }
- return 0;
- }
- /// @internal
- stateSlot(state, slot) {
- return this.states[(state * 6 /* Size */) + slot];
- }
- /// @internal
- stateFlag(state, flag) {
- return (this.stateSlot(state, 0 /* Flags */) & flag) > 0;
- }
- /// @internal
- validAction(state, action) {
- if (action == this.stateSlot(state, 4 /* DefaultReduce */))
- return true;
- for (let i = this.stateSlot(state, 1 /* Actions */);; i += 3) {
- if (this.data[i] == 65535 /* End */) {
- if (this.data[i + 1] == 1 /* Next */)
- i = pair(this.data, i + 2);
- else
- return false;
- }
- if (action == pair(this.data, i + 1))
- return true;
- }
- }
- /// Get the states that can follow this one through shift actions or
- /// goto jumps. @internal
- nextStates(state) {
- let result = [];
- for (let i = this.stateSlot(state, 1 /* Actions */);; i += 3) {
- if (this.data[i] == 65535 /* End */) {
- if (this.data[i + 1] == 1 /* Next */)
- i = pair(this.data, i + 2);
- else
- break;
- }
- if ((this.data[i + 2] & (65536 /* ReduceFlag */ >> 16)) == 0) {
- let value = this.data[i + 1];
- if (!result.some((v, i) => (i & 1) && v == value))
- result.push(this.data[i], value);
- }
- }
- return result;
- }
- /// @internal
- overrides(token, prev) {
- let iPrev = findOffset(this.data, this.tokenPrecTable, prev);
- return iPrev < 0 || findOffset(this.data, this.tokenPrecTable, token) < iPrev;
- }
- /// Configure the parser. Returns a new parser instance that has the
- /// given settings modified. Settings not provided in `config` are
- /// kept from the original parser.
- configure(config) {
- // Hideous reflection-based kludge to make it easy to create a
- // slightly modified copy of a parser.
- let copy = Object.assign(Object.create(LRParser.prototype), this);
- if (config.props)
- copy.nodeSet = this.nodeSet.extend(...config.props);
- if (config.top) {
- let info = this.topRules[config.top];
- if (!info)
- throw new RangeError(`Invalid top rule name ${config.top}`);
- copy.top = info;
- }
- if (config.tokenizers)
- copy.tokenizers = this.tokenizers.map(t => {
- let found = config.tokenizers.find(r => r.from == t);
- return found ? found.to : t;
- });
- if (config.specializers)
- copy.specializers = this.specializers.map(s => {
- let found = config.specializers.find(r => r.from == s);
- return found ? found.to : s;
- });
- if (config.contextTracker)
- copy.context = config.contextTracker;
- if (config.dialect)
- copy.dialect = this.parseDialect(config.dialect);
- if (config.strict != null)
- copy.strict = config.strict;
- if (config.wrap)
- copy.wrappers = copy.wrappers.concat(config.wrap);
- if (config.bufferLength != null)
- copy.bufferLength = config.bufferLength;
- return copy;
- }
- /// Tells you whether any [parse wrappers](#lr.ParserConfig.wrap)
- /// are registered for this parser.
- hasWrappers() {
- return this.wrappers.length > 0;
- }
- /// Returns the name associated with a given term. This will only
- /// work for all terms when the parser was generated with the
- /// `--names` option. By default, only the names of tagged terms are
- /// stored.
- getName(term) {
- return this.termNames ? this.termNames[term] : String(term <= this.maxNode && this.nodeSet.types[term].name || term);
- }
- /// The eof term id is always allocated directly after the node
- /// types. @internal
- get eofTerm() { return this.maxNode + 1; }
- /// The type of top node produced by the parser.
- get topNode() { return this.nodeSet.types[this.top[1]]; }
- /// @internal
- dynamicPrecedence(term) {
- let prec = this.dynamicPrecedences;
- return prec == null ? 0 : prec[term] || 0;
- }
- /// @internal
- parseDialect(dialect) {
- let values = Object.keys(this.dialects), flags = values.map(() => false);
- if (dialect)
- for (let part of dialect.split(" ")) {
- let id = values.indexOf(part);
- if (id >= 0)
- flags[id] = true;
- }
- let disabled = null;
- for (let i = 0; i < values.length; i++)
- if (!flags[i]) {
- for (let j = this.dialects[values[i]], id; (id = this.data[j++]) != 65535 /* End */;)
- (disabled || (disabled = new Uint8Array(this.maxTerm + 1)))[id] = 1;
- }
- return new Dialect(dialect, flags, disabled);
- }
- /// (used by the output of the parser generator) @internal
- static deserialize(spec) {
- return new LRParser(spec);
- }
- }
- function pair(data, off) { return data[off] | (data[off + 1] << 16); }
- function findOffset(data, start, term) {
- for (let i = start, next; (next = data[i]) != 65535 /* End */; i++)
- if (next == term)
- return i - start;
- return -1;
- }
- function findFinished(stacks) {
- let best = null;
- for (let stack of stacks) {
- let stopped = stack.p.stoppedAt;
- if ((stack.pos == stack.p.stream.end || stopped != null && stack.pos > stopped) &&
- stack.p.parser.stateFlag(stack.state, 2 /* Accepting */) &&
- (!best || best.score < stack.score))
- best = stack;
- }
- return best;
- }
- export { ContextTracker, ExternalTokenizer, InputStream, LRParser, Stack };
|