jsdoc-type-pratt-parser
Advanced tools
Comparing version 2.2.5 to 3.0.0
import { Token } from './Token'; | ||
export declare class Lexer { | ||
private text; | ||
private current; | ||
private next; | ||
private previous; | ||
lex(text: string): void; | ||
token(): Token; | ||
peek(): Token; | ||
last(): Token | undefined; | ||
advance(): void; | ||
private read; | ||
private readonly text; | ||
readonly current: Token; | ||
readonly next: Token; | ||
readonly previous: Token | undefined; | ||
static create(text: string): Lexer; | ||
private constructor(); | ||
private static read; | ||
advance(): Lexer; | ||
} |
@@ -1,2 +0,1 @@ | ||
import { TokenType } from './lexer/Token'; | ||
import { Lexer } from './lexer/Lexer'; | ||
@@ -7,16 +6,13 @@ import { Grammar } from './grammars/Grammar'; | ||
import { IntermediateResult } from './result/IntermediateResult'; | ||
interface ParserOptions { | ||
grammar: Grammar; | ||
lexer?: Lexer; | ||
parent?: Parser; | ||
} | ||
import { TokenType } from './lexer/Token'; | ||
export declare class Parser { | ||
private readonly grammar; | ||
private readonly lexer; | ||
private readonly parent?; | ||
constructor({ grammar, lexer, parent }: ParserOptions); | ||
private _lexer; | ||
readonly parent?: Parser; | ||
constructor(grammar: Grammar, textOrLexer: string | Lexer, parent?: Parser); | ||
get lexer(): Lexer; | ||
/** | ||
* Parses a given string and throws an error if the parse ended before the end of the string. | ||
*/ | ||
parseText(text: string): RootResult; | ||
parse(): RootResult; | ||
/** | ||
@@ -27,6 +23,2 @@ * Parses with the current lexer and asserts that the result is a {@link RootResult}. | ||
/** | ||
* Tries to parse the current state with all parslets in the grammar and returns the first non null result. | ||
*/ | ||
private tryParslets; | ||
/** | ||
* The main parsing function. First it tries to parse the current state in the prefix step, and then it continues | ||
@@ -40,4 +32,8 @@ * to parse the state in the infix step. | ||
*/ | ||
parseInfixIntermediateType(result: IntermediateResult, precedence: Precedence): IntermediateResult; | ||
parseInfixIntermediateType(left: IntermediateResult, precedence: Precedence): IntermediateResult; | ||
/** | ||
* Tries to parse the current state with all parslets in the grammar and returns the first non null result. | ||
*/ | ||
private tryParslets; | ||
/** | ||
* If the given type equals the current type of the {@link Lexer} advance the lexer. Return true if the lexer was | ||
@@ -47,5 +43,3 @@ * advanced. | ||
consume(types: TokenType | TokenType[]): boolean; | ||
getLexer(): Lexer; | ||
getParent(): Parser | undefined; | ||
acceptLexerState(parser: Parser): void; | ||
} | ||
export {}; |
{ | ||
"name": "jsdoc-type-pratt-parser", | ||
"version": "2.2.5", | ||
"version": "3.0.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -173,50 +173,29 @@ import { Token, TokenType } from './Token' | ||
export class Lexer { | ||
private text: string = '' | ||
private readonly text: string = '' | ||
public readonly current: Token | ||
public readonly next: Token | ||
public readonly previous: Token | undefined | ||
private current: Token | undefined | ||
private next: Token | undefined | ||
private previous: Token | undefined | ||
public static create (text: string): Lexer { | ||
const current = this.read(text) | ||
text = current.text | ||
const next = this.read(text) | ||
text = next.text | ||
return new Lexer(text, undefined, current.token, next.token) | ||
} | ||
lex (text: string): void { | ||
private constructor (text: string, previous: Token | undefined, current: Token, next: Token) { | ||
this.text = text | ||
this.current = undefined | ||
this.next = undefined | ||
this.advance() | ||
this.previous = previous | ||
this.current = current | ||
this.next = next | ||
} | ||
token (): Token { | ||
if (this.current === undefined) { | ||
throw new Error('Lexer not lexing') | ||
} | ||
return this.current | ||
} | ||
peek (): Token { | ||
if (this.next === undefined) { | ||
this.next = this.read() | ||
} | ||
return this.next | ||
} | ||
last (): Token | undefined { | ||
return this.previous | ||
} | ||
advance (): void { | ||
this.previous = this.current | ||
if (this.next !== undefined) { | ||
this.current = this.next | ||
this.next = undefined | ||
return | ||
} | ||
this.current = this.read() | ||
} | ||
private read (): Token { | ||
const text = this.text.trim() | ||
private static read (text: string): { text: string, token: Token } { | ||
text = text.trim() | ||
for (const rule of rules) { | ||
const token = rule(text) | ||
if (token !== null) { | ||
this.text = text.slice(token.text.length) | ||
return token | ||
text = text.slice(token.text.length) | ||
return { text, token } | ||
} | ||
@@ -226,2 +205,7 @@ } | ||
} | ||
advance (): Lexer { | ||
const next = Lexer.read(this.text) | ||
return new Lexer(next.text, this.current, this.next, next.token) | ||
} | ||
} |
@@ -9,14 +9,2 @@ import { Parser } from './Parser' | ||
const parsers = { | ||
jsdoc: new Parser({ | ||
grammar: jsdocGrammar | ||
}), | ||
closure: new Parser({ | ||
grammar: closureGrammar | ||
}), | ||
typescript: new Parser({ | ||
grammar: typescriptGrammar | ||
}) | ||
} | ||
/** | ||
@@ -28,3 +16,10 @@ * This function parses the given expression in the given mode and produces a {@link RootResult}. | ||
export function parse (expression: string, mode: ParseMode): RootResult { | ||
return parsers[mode].parseText(expression) | ||
switch (mode) { | ||
case 'closure': | ||
return (new Parser(closureGrammar, expression)).parse() | ||
case 'jsdoc': | ||
return (new Parser(jsdocGrammar, expression)).parse() | ||
case 'typescript': | ||
return (new Parser(typescriptGrammar, expression)).parse() | ||
} | ||
} | ||
@@ -43,3 +38,3 @@ | ||
try { | ||
return parsers[mode].parseText(expression) | ||
return parse(expression, mode) | ||
} catch (e) { | ||
@@ -46,0 +41,0 @@ error = e |
import { EarlyEndOfParseError, NoParsletFoundError } from './errors' | ||
import { TokenType } from './lexer/Token' | ||
import { Lexer } from './lexer/Lexer' | ||
@@ -9,21 +8,21 @@ import { Grammar } from './grammars/Grammar' | ||
import { IntermediateResult } from './result/IntermediateResult' | ||
import { TokenType } from './lexer/Token' | ||
interface ParserOptions { | ||
grammar: Grammar | ||
lexer?: Lexer | ||
parent?: Parser | ||
} | ||
export class Parser { | ||
private readonly grammar: Grammar | ||
private _lexer: Lexer | ||
public readonly parent?: Parser | ||
private readonly lexer: Lexer | ||
private readonly parent?: Parser | ||
constructor ({ grammar, lexer, parent }: ParserOptions) { | ||
this.lexer = lexer ?? new Lexer() | ||
constructor (grammar: Grammar, textOrLexer: string | Lexer, parent?: Parser) { | ||
this.grammar = grammar | ||
if (typeof textOrLexer === 'string') { | ||
this._lexer = Lexer.create(textOrLexer) | ||
} else { | ||
this._lexer = textOrLexer | ||
} | ||
this.parent = parent | ||
} | ||
this.grammar = grammar | ||
get lexer (): Lexer { | ||
return this._lexer | ||
} | ||
@@ -34,7 +33,6 @@ | ||
*/ | ||
parseText (text: string): RootResult { | ||
this.lexer.lex(text) | ||
parse (): RootResult { | ||
const result = this.parseType(Precedence.ALL) | ||
if (this.lexer.token().type !== 'EOF') { | ||
throw new EarlyEndOfParseError(this.lexer.token()) | ||
if (this.lexer.current.type !== 'EOF') { | ||
throw new EarlyEndOfParseError(this.lexer.current) | ||
} | ||
@@ -52,15 +50,2 @@ return result | ||
/** | ||
* Tries to parse the current state with all parslets in the grammar and returns the first non null result. | ||
*/ | ||
private tryParslets (precedence: Precedence, left: IntermediateResult | null): IntermediateResult | null { | ||
for (const parslet of this.grammar) { | ||
const result = parslet(this, precedence, left) | ||
if (result !== null) { | ||
return result | ||
} | ||
} | ||
return null | ||
} | ||
/** | ||
* The main parsing function. First it tries to parse the current state in the prefix step, and then it continues | ||
@@ -70,6 +55,6 @@ * to parse the state in the infix step. | ||
public parseIntermediateType (precedence: Precedence): IntermediateResult { | ||
const result = this.tryParslets(precedence, null) | ||
const result = this.tryParslets(null, precedence) | ||
if (result === null) { | ||
throw new NoParsletFoundError(this.lexer.token()) | ||
throw new NoParsletFoundError(this.lexer.current) | ||
} | ||
@@ -84,35 +69,46 @@ | ||
*/ | ||
public parseInfixIntermediateType (result: IntermediateResult, precedence: Precedence): IntermediateResult { | ||
let newResult = this.tryParslets(precedence, result) | ||
public parseInfixIntermediateType (left: IntermediateResult, precedence: Precedence): IntermediateResult { | ||
let result = this.tryParslets(left, precedence) | ||
while (newResult !== null) { | ||
result = newResult | ||
newResult = this.tryParslets(precedence, result) | ||
while (result !== null) { | ||
left = result | ||
result = this.tryParslets(left, precedence) | ||
} | ||
return result | ||
return left | ||
} | ||
/** | ||
* Tries to parse the current state with all parslets in the grammar and returns the first non null result. | ||
*/ | ||
private tryParslets (left: IntermediateResult | null, precedence: Precedence): IntermediateResult | null { | ||
for (const parslet of this.grammar) { | ||
const result = parslet(this, precedence, left) | ||
if (result !== null) { | ||
return result | ||
} | ||
} | ||
return null | ||
} | ||
/** | ||
* If the given type equals the current type of the {@link Lexer} advance the lexer. Return true if the lexer was | ||
* advanced. | ||
*/ | ||
public consume (types: TokenType|TokenType[]): boolean { | ||
public consume (types: TokenType | TokenType[]): boolean { | ||
if (!Array.isArray(types)) { | ||
types = [types] | ||
} | ||
if (!types.includes(this.lexer.token().type)) { | ||
if (types.includes(this.lexer.current.type)) { | ||
this._lexer = this.lexer.advance() | ||
return true | ||
} else { | ||
return false | ||
} | ||
this.lexer.advance() | ||
return true | ||
} | ||
getLexer (): Lexer { | ||
return this.lexer | ||
public acceptLexerState (parser: Parser): void { | ||
this._lexer = parser.lexer | ||
} | ||
getParent (): Parser | undefined { | ||
return this.parent | ||
} | ||
} |
@@ -41,3 +41,3 @@ import { composeParslet, ParsletFunction } from './Parslet' | ||
const hasParenthesis = parser.getLexer().token().type === '(' | ||
const hasParenthesis = parser.lexer.current.type === '(' | ||
@@ -44,0 +44,0 @@ if (!hasParenthesis) { |
@@ -30,6 +30,7 @@ import { composeParslet, ParsletFunction } from './Parslet' | ||
// object parslet uses a special grammar and for the value we want to switch back to the parent | ||
parser = parser.getParent() ?? parser | ||
const parentParser = parser.parent ?? parser | ||
parentParser.acceptLexerState(parser) | ||
if (left.type === 'JsdocTypeNumber' || left.type === 'JsdocTypeName' || left.type === 'JsdocTypeStringValue') { | ||
parser.consume(':') | ||
parentParser.consume(':') | ||
@@ -41,6 +42,9 @@ let quote | ||
const right = parentParser.parseType(Precedence.KEY_VALUE) | ||
parser.acceptLexerState(parentParser) | ||
return { | ||
type: 'JsdocTypeKeyValue', | ||
key: left.value.toString(), | ||
right: parser.parseType(Precedence.KEY_VALUE), | ||
right: right, | ||
optional: optional, | ||
@@ -58,8 +62,11 @@ readonly: readonlyProperty, | ||
parser.consume(':') | ||
parentParser.consume(':') | ||
const right = parentParser.parseType(Precedence.KEY_VALUE) | ||
parser.acceptLexerState(parentParser) | ||
return { | ||
type: 'JsdocTypeKeyValue', | ||
left: assertRootResult(left), | ||
right: parser.parseType(Precedence.KEY_VALUE), | ||
right: right, | ||
meta: { | ||
@@ -66,0 +73,0 @@ hasLeftSideExpression: true |
@@ -11,3 +11,3 @@ import { TokenType } from '../lexer/Token' | ||
parsePrefix: parser => { | ||
const { type, text } = parser.getLexer().token() | ||
const { type, text } = parser.lexer.current | ||
parser.consume(type) | ||
@@ -14,0 +14,0 @@ |
@@ -18,4 +18,4 @@ import { ParsletFunction } from './Parslet' | ||
} | ||
const type = parser.getLexer().token().type | ||
const next = parser.getLexer().peek().type | ||
const type = parser.lexer.current.type | ||
const next = parser.lexer.next.type | ||
@@ -46,9 +46,7 @@ const accept = (type === '.' && next !== '<') || | ||
const pathParser = pathGrammar !== null | ||
? new Parser({ | ||
grammar: pathGrammar, | ||
lexer: parser.getLexer() | ||
}) | ||
? new Parser(pathGrammar, parser.lexer, parser) | ||
: parser | ||
const parsed = pathParser.parseIntermediateType(Precedence.NAME_PATH) | ||
parser.acceptLexerState(pathParser) | ||
let right: PropertyResult | SpecialNamePath<'event'> | ||
@@ -96,3 +94,3 @@ | ||
if (brackets && !parser.consume(']')) { | ||
const token = parser.getLexer().token() | ||
const token = parser.lexer.current | ||
throw new Error(`Unterminated square brackets. Next token is '${token.type}' ` + | ||
@@ -99,0 +97,0 @@ `with text '${token.text}'`) |
@@ -7,4 +7,4 @@ import { ParsletFunction } from './Parslet' | ||
export const nullableParslet: ParsletFunction = (parser, precedence, left) => { | ||
const type = parser.getLexer().token().type | ||
const next = parser.getLexer().peek().type | ||
const type = parser.lexer.current.type | ||
const next = parser.lexer.next.type | ||
@@ -11,0 +11,0 @@ const accept = ((left == null) && type === '?' && !isQuestionMarkUnknownType(next)) || |
@@ -7,3 +7,3 @@ import { composeParslet } from './Parslet' | ||
parsePrefix: parser => { | ||
const value = parseFloat(parser.getLexer().token().text) | ||
const value = parseFloat(parser.lexer.current.text) | ||
parser.consume('Number') | ||
@@ -10,0 +10,0 @@ return { |
@@ -28,12 +28,8 @@ import { composeParslet, ParsletFunction } from './Parslet' | ||
const lexer = parser.getLexer() | ||
const fieldParser = new Parser(objectFieldGrammar, parser.lexer, parser) | ||
const fieldParser = new Parser({ | ||
grammar: objectFieldGrammar, | ||
lexer: lexer, | ||
parent: parser | ||
}) | ||
while (true) { | ||
fieldParser.acceptLexerState(parser) | ||
let field = fieldParser.parseIntermediateType(Precedence.OBJECT) | ||
parser.acceptLexerState(fieldParser) | ||
@@ -79,3 +75,3 @@ if (field === undefined && allowKeyTypes) { | ||
} | ||
const type = parser.getLexer().token().type | ||
const type = parser.lexer.current.type | ||
if (type === '}') { | ||
@@ -82,0 +78,0 @@ break |
@@ -32,7 +32,7 @@ import { TokenType } from '../lexer/Token' | ||
export function composeParslet (options: ComposeParsletOptions): ParsletFunction { | ||
const parslet = (parser: Parser, curPrecedence: Precedence, left: IntermediateResult | null): IntermediateResult | null => { | ||
const type = parser.getLexer().token().type | ||
const next = parser.getLexer().peek().type | ||
const parslet: ParsletFunction = (parser, curPrecedence, left) => { | ||
const type = parser.lexer.current.type | ||
const next = parser.lexer.next.type | ||
if (left == null) { | ||
if (left === null) { | ||
if ('parsePrefix' in options) { | ||
@@ -50,3 +50,2 @@ if (options.accept(type, next)) { | ||
} | ||
return null | ||
@@ -53,0 +52,0 @@ } |
@@ -17,3 +17,3 @@ import { composeParslet, ParsletFunction } from './Parslet' | ||
parsePrefix: parser => { | ||
const type = parser.getLexer().token().type as SpecialNamePathType | ||
const type = parser.lexer.current.type as SpecialNamePathType | ||
parser.consume(type) | ||
@@ -28,10 +28,5 @@ | ||
const moduleParser = new Parser({ | ||
grammar: pathGrammar, | ||
lexer: parser.getLexer() | ||
}) | ||
let result: SpecialNamePath | ||
let token = parser.getLexer().token() | ||
let token = parser.lexer.current | ||
if (parser.consume('StringValue')) { | ||
@@ -51,3 +46,3 @@ result = { | ||
value += token.text | ||
token = parser.getLexer().token() | ||
token = parser.lexer.current | ||
} | ||
@@ -64,5 +59,9 @@ result = { | ||
return assertRootResult(moduleParser.parseInfixIntermediateType(result, Precedence.ALL)) | ||
const moduleParser = new Parser(pathGrammar, parser.lexer, parser) | ||
const moduleResult = moduleParser.parseInfixIntermediateType(result, Precedence.ALL) | ||
parser.acceptLexerState(moduleParser) | ||
return assertRootResult(moduleResult) | ||
} | ||
}) | ||
} |
@@ -33,4 +33,4 @@ import { composeParslet } from './Parslet' | ||
throw new Error('Unacceptable token: ' + parser.getLexer().token().text) | ||
throw new Error('Unacceptable token: ' + parser.lexer.current.text) | ||
} | ||
}) |
@@ -7,3 +7,3 @@ import { composeParslet } from './Parslet' | ||
parsePrefix: parser => { | ||
const text = parser.getLexer().token().text | ||
const text = parser.lexer.current.text | ||
parser.consume('StringValue') | ||
@@ -10,0 +10,0 @@ return { |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
225499
6246