@tracespace/parser
Version:
Gerber and NC drill file parser
101 lines (83 loc) • 2.59 kB
text/typescript
import {Token} from '../lexer'
import {TokenRule, SINGLE_TOKEN, MIN_TO_MAX} from './rules'
import {SyntaxRule, MatchState} from './types'
const FULL_MATCH = 'FULL_MATCH'
const PARTIAL_MATCH = 'PARTIAL_MATCH'
const NO_MATCH = 'NO_MATCH'
type ListMatch = typeof FULL_MATCH | typeof PARTIAL_MATCH | typeof NO_MATCH
export function createMatchSyntax<M>(
...grammar: SyntaxRule<M>[]
): (state: MatchState<M> | null, token: Token) => MatchState<M> {
return (state, token) => matchSyntax(state, token, grammar)
}
export function matchSyntax<M>(
state: MatchState<M> | null,
token: Token,
grammar: SyntaxRule<M>[]
): MatchState<M> {
if (state === null) state = {candidates: grammar, tokens: []}
const {candidates: prevCandidates} = state
const candidates = []
const tokens = [...state.tokens, token]
let i
for (i = 0; i < prevCandidates.length; i++) {
const rule = prevCandidates[i]
const result = tokenListMatches(rule.rules, tokens)
if (result === FULL_MATCH) {
const nodes = rule.createNodes(tokens)
return {candidates: [], tokens, nodes, filetype: rule.filetype}
}
if (result === PARTIAL_MATCH) {
candidates.push(rule)
}
}
return {candidates, tokens}
}
function tokenListMatches(
rules: Array<TokenRule>,
tokens: Array<Token>
): ListMatch {
let i = 0
let j = 0
let multiMatchCount = 0
while (i < rules.length && j < tokens.length) {
const rule = rules[i]
const token = tokens[j]
const match = tokenMatches(rule, token)
if (match) {
if (
rule.rule === SINGLE_TOKEN ||
(rule.rule === MIN_TO_MAX && multiMatchCount >= rule.max - 1)
) {
i++
j++
multiMatchCount = 0
} else if (rule.rule === MIN_TO_MAX) {
j++
multiMatchCount++
}
} else if (rule.rule === MIN_TO_MAX && multiMatchCount >= rule.min) {
multiMatchCount = 0
i++
} else {
return NO_MATCH
}
}
if (i < rules.length) return PARTIAL_MATCH
return FULL_MATCH
}
function tokenMatches(rule: TokenRule, token: Token): boolean {
if (rule.rule === SINGLE_TOKEN) {
const typeResult = rule.type === token.type
const valueResult =
rule.value == null ||
(typeof rule.value === 'string' && rule.value === token.value) ||
(rule.value instanceof RegExp && rule.value.test(token.value))
const result = typeResult && valueResult
return rule.negate ? !result : result
}
if (Array.isArray(rule.match)) {
return rule.match.some(match => tokenMatches(match, token))
}
return false
}