jsdoc-type-pratt-parser
Version:
[](https://www.npmjs.com/package/jsdoc-type-pratt-parser) []
115 lines (103 loc) • 3.55 kB
text/typescript
import type { ParsletFunction } from './Parslet.js'
import { Precedence } from '../Precedence.js'
import { assertRootResult } from '../assertTypes.js'
import { Parser } from '../Parser.js'
import type { NamePathResult, SpecialNamePath } from '../result/RootResult.js'
import { UnexpectedTypeError } from '../errors.js'
import type { PropertyResult, IndexedAccessIndexResult } from '../result/NonRootResult.js'
import type { Grammar } from '../grammars/Grammar.js'
export function createNamePathParslet ({ allowSquareBracketsOnAnyType, allowJsdocNamePaths, pathGrammar }: {
allowJsdocNamePaths: boolean
allowSquareBracketsOnAnyType: boolean
pathGrammar: Grammar | null
}): ParsletFunction {
return function namePathParslet (parser, precedence, left) {
if ((left == null) || precedence >= Precedence.NAME_PATH) {
return null
}
const type = parser.lexer.current.type
const next = parser.lexer.next.type
const accept = (type === '.' && next !== '<') ||
(type === '[' && (allowSquareBracketsOnAnyType || left.type === 'JsdocTypeName')) ||
(allowJsdocNamePaths && (type === '~' || type === '#'))
if (!accept) {
return null
}
let pathType: NamePathResult['pathType']
let brackets = false
if (parser.consume('.')) {
pathType = 'property'
} else if (parser.consume('[')) {
pathType = 'property-brackets'
brackets = true
} else if (parser.consume('~')) {
pathType = 'inner'
} else {
parser.consume('#')
pathType = 'instance'
}
const pathParser = brackets && allowSquareBracketsOnAnyType
? parser
: pathGrammar !== null
? new Parser(pathGrammar, parser.lexer, parser)
: parser
const parsed = pathParser.parseType(Precedence.NAME_PATH)
parser.acceptLexerState(pathParser)
let right: PropertyResult | SpecialNamePath<'event'> | IndexedAccessIndexResult
switch (parsed.type) {
case 'JsdocTypeName':
right = {
type: 'JsdocTypeProperty',
value: parsed.value,
meta: {
quote: undefined
}
}
break
case 'JsdocTypeNumber':
right = {
type: 'JsdocTypeProperty',
value: parsed.value.toString(10),
meta: {
quote: undefined
}
}
break
case 'JsdocTypeStringValue':
right = {
type: 'JsdocTypeProperty',
value: parsed.value,
meta: {
quote: parsed.meta.quote
}
}
break
case 'JsdocTypeSpecialNamePath':
if (parsed.specialType === 'event') {
right = parsed as SpecialNamePath<'event'>
} else {
throw new UnexpectedTypeError(parsed, 'Type \'JsdocTypeSpecialNamePath\' is only allowed with specialType \'event\'')
}
break
default:
if (!brackets || !allowSquareBracketsOnAnyType) {
throw new UnexpectedTypeError(parsed, 'Expecting \'JsdocTypeName\', \'JsdocTypeNumber\', \'JsdocStringValue\' or \'JsdocTypeSpecialNamePath\'')
}
right = {
type: 'JsdocTypeIndexedAccessIndex',
right: parsed
}
}
if (brackets && !parser.consume(']')) {
const token = parser.lexer.current
throw new Error(`Unterminated square brackets. Next token is '${token.type}' ` +
`with text '${token.text}'`)
}
return {
type: 'JsdocTypeNamePath',
left: assertRootResult(left),
right,
pathType
}
}
}