Initial commit
This commit is contained in:
commit
b3a51a4115
10336 changed files with 2381973 additions and 0 deletions
71
node_modules/chevrotain/src/parse/grammar/first.ts
generated
vendored
Normal file
71
node_modules/chevrotain/src/parse/grammar/first.ts
generated
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import flatten from "lodash/flatten"
|
||||
import uniq from "lodash/uniq"
|
||||
import map from "lodash/map"
|
||||
import { NonTerminal, Terminal } from "@chevrotain/gast"
|
||||
import {
|
||||
isBranchingProd,
|
||||
isOptionalProd,
|
||||
isSequenceProd
|
||||
} from "@chevrotain/gast"
|
||||
import { IProduction, TokenType } from "@chevrotain/types"
|
||||
|
||||
export function first(prod: IProduction): TokenType[] {
|
||||
/* istanbul ignore else */
|
||||
if (prod instanceof NonTerminal) {
|
||||
// this could in theory cause infinite loops if
|
||||
// (1) prod A refs prod B.
|
||||
// (2) prod B refs prod A
|
||||
// (3) AB can match the empty set
|
||||
// in other words a cycle where everything is optional so the first will keep
|
||||
// looking ahead for the next optional part and will never exit
|
||||
// currently there is no safeguard for this unique edge case because
|
||||
// (1) not sure a grammar in which this can happen is useful for anything (productive)
|
||||
return first((<NonTerminal>prod).referencedRule)
|
||||
} else if (prod instanceof Terminal) {
|
||||
return firstForTerminal(<Terminal>prod)
|
||||
} else if (isSequenceProd(prod)) {
|
||||
return firstForSequence(prod)
|
||||
} else if (isBranchingProd(prod)) {
|
||||
return firstForBranching(prod)
|
||||
} else {
|
||||
throw Error("non exhaustive match")
|
||||
}
|
||||
}
|
||||
|
||||
export function firstForSequence(prod: {
|
||||
definition: IProduction[]
|
||||
}): TokenType[] {
|
||||
let firstSet: TokenType[] = []
|
||||
const seq = prod.definition
|
||||
let nextSubProdIdx = 0
|
||||
let hasInnerProdsRemaining = seq.length > nextSubProdIdx
|
||||
let currSubProd
|
||||
// so we enter the loop at least once (if the definition is not empty
|
||||
let isLastInnerProdOptional = true
|
||||
// scan a sequence until it's end or until we have found a NONE optional production in it
|
||||
while (hasInnerProdsRemaining && isLastInnerProdOptional) {
|
||||
currSubProd = seq[nextSubProdIdx]
|
||||
isLastInnerProdOptional = isOptionalProd(currSubProd)
|
||||
firstSet = firstSet.concat(first(currSubProd))
|
||||
nextSubProdIdx = nextSubProdIdx + 1
|
||||
hasInnerProdsRemaining = seq.length > nextSubProdIdx
|
||||
}
|
||||
|
||||
return uniq(firstSet)
|
||||
}
|
||||
|
||||
export function firstForBranching(prod: {
|
||||
definition: IProduction[]
|
||||
}): TokenType[] {
|
||||
const allAlternativesFirsts: TokenType[][] = map(
|
||||
prod.definition,
|
||||
(innerProd) => {
|
||||
return first(innerProd)
|
||||
}
|
||||
)
|
||||
return uniq(flatten<TokenType>(allAlternativesFirsts))
|
||||
}
|
||||
|
||||
export function firstForTerminal(terminal: Terminal): TokenType[] {
|
||||
return [terminal.terminalType]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue