Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

jsdoc-type-pratt-parser

Package Overview
Dependencies
Maintainers
2
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

jsdoc-type-pratt-parser - npm Package Compare versions

Comparing version 2.2.0 to 2.2.1

3

dist/src/assertTypes.d.ts
import { KeyValueResult } from './result/NonRootResult';
import { NameResult, NumberResult, RootResult, VariadicResult } from './result/RootResult';
import { IntermediateResult } from './result/IntermediateResult';
/**
* Throws an error if the provided result is not a {@link RootResult}
*/
export declare function assertRootResult(result?: IntermediateResult): RootResult;

@@ -5,0 +8,0 @@ export declare function assertPlainKeyValueOrRootResult(result: IntermediateResult): KeyValueResult | RootResult;

@@ -1,2 +0,2 @@

import { Token, TokenType } from './lexer/Token';
import { TokenType } from './lexer/Token';
import { Lexer } from './lexer/Lexer';

@@ -17,11 +17,29 @@ import { Grammar } from './grammars/Grammar';

constructor({ grammar, lexer, parent }: ParserOptions);
/**
* Parses a given string and throws an error if the parse ended before the end of the string.
*/
parseText(text: string): RootResult;
/**
* Parses with the current lexer and asserts that the result is a {@link RootResult}.
*/
parseType(precedence: Precedence): 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
* to parse the state in the infix step.
*/
parseIntermediateType(precedence: Precedence): IntermediateResult;
/**
* In the infix parsing step the parser continues to parse the current state with all parslets until none returns
* a result.
*/
parseInfixIntermediateType(result: IntermediateResult, precedence: Precedence): IntermediateResult;
/**
* If the given type equals the current type of the {@link Lexer} advance the lexer. Return true if the lexer was
* advanced.
*/
consume(types: TokenType | TokenType[]): boolean;
getToken(): Token;
peekToken(): Token;
previousToken(): Token | undefined;
getLexer(): Lexer;

@@ -28,0 +46,0 @@ getParent(): Parser | undefined;

import { NonRootResult } from './result/NonRootResult';
import { RootResult } from './result/RootResult';
/**
* A node visitor function.
* @param node the visited node.
* @param parentNode the parent node.
* @param property the property on the parent node that contains the visited node. It can be the node itself or
* an array of nodes.
*/
declare type NodeVisitor = (node: NonRootResult, parentNode?: NonRootResult, property?: string) => void;
/**
* A function to traverse an AST. It traverses it depth first.
* @param node the node to start traversing at.
* @param onEnter node visitor function that will be called on entering the node. This corresponds to preorder traversing.
* @param onLeave node visitor function that will be called on leaving the node. This corresponds to postorder traversing.
*/
export declare function traverse(node: RootResult, onEnter?: NodeVisitor, onLeave?: NodeVisitor): void;
export {};

5

package.json
{
"name": "jsdoc-type-pratt-parser",
"version": "2.2.0",
"version": "2.2.1",
"description": "",

@@ -21,3 +21,4 @@ "main": "dist/index.js",

"prepublishOnly": "npm run build",
"semantic-release": "semantic-release"
"semantic-release": "semantic-release",
"benchmark": "npm run build && node benchmark/benchmark.js"
},

@@ -24,0 +25,0 @@ "author": "Simon Seyock (https://github.com/simonseyock)",

@@ -9,7 +9,10 @@ [![Npm Package](https://badgen.net/npm/v/jsdoc-type-pratt-parser)](https://www.npmjs.com/package/jsdoc-type-pratt-parser)

This project is a parser for jsdoc types. It is heavily inspired by the existing libraries catharsis and
jsdoctypeparser, but does not use PEG.js, instead it is written as a pratt parser.
* https://github.com/hegemonic/catharsis
* https://github.com/jsdoctypeparser/jsdoctypeparser
This project is a parser for jsdoc types. It takes jsdoc type expressions like `Array<string>` and creates an abstract
syntax tree (AST) out of it. It is heavily inspired by the existing libraries [catharsis](https://github.com/hegemonic/catharsis) and [jsdoctypeparser](https://github.com/jsdoctypeparser/jsdoctypeparser), but does
not use [PEG.js](https://pegjs.org/), instead it is written as a pratt parser.
You can find some more information about pratt parsers here:
* https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing
* http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
* https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html

@@ -22,2 +25,3 @@ ## Table of Contents

* [Transforms](#transforms)
* [Traverse](#traverse)
* [Tests Status](#tests-status)

@@ -40,3 +44,3 @@ * [Performance](#performance)

const result = parse('myType.<string>', 'closure')
const result = parse('SomeType<string>', 'typescript')
```

@@ -46,3 +50,4 @@

An API documentation can be found here: https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html
An API documentation can be found [here](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html).
It is still lacking in some points. Feel free to create issues or PRs to improve this.

@@ -55,46 +60,64 @@ ## Available Grammars

This library supports compatibility modes for catharsis and jsdoctypeparser. The provided transform functions attempt to
transform the output to the expected output of the target library. This will not always be the same as some types are
parsed differently. These modes are thought to make transition easier, but it is advised to use the native output as
this will be more uniform and will contain more information.
Catharsis compat mode:
A common task to do on ASTs are transforms, for example a stringification. This library includes some transform and
utilities to implement your own.
[`stringify`](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#stringify):
```js
import { parse, catharsisTransform } from 'jsdoc-type-pratt-parser'
import { stringify } from 'jsdoc-type-pratt-parser'
const result = catharsisTransform(parse('myType.<string>', 'closure'))
const val = stringify({ type: 'JsdocTypeName', value: 'name'}) // -> 'name'
```
Jsdoctypeparser compat mode:
You can customize the stringification by using [`stringifyRules`](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#stringifyRules)
and [`transform`](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#transform):
```js
import { parse, jtpTransform } from 'jsdoc-type-pratt-parser'
import { stringifyRules, transform } from 'jsdoc-type-pratt-parser'
const result = jtpTransform(parse('myType.<string>', 'closure'))
const rules = stringifyRules()
// `result` is the current node and `transform` is a function to transform child nodes.
rules.NAME = (result, transform) => 'something else'
const val = transform(rules, { type: 'JsdocTypeName', value: 'name'}) // -> 'something else'
```
Stringify:
You can also build your own transform rules by implementing the [`TransformRules<TransformResultType>`](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#TransformRules) interface or you
can build upon the [identity ruleset](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#identityTransformRules) like this:
```js
import { stringify } from 'jsdoc-type-pratt-parser'
import { identityTransformRules, transform } from 'jsdoc-type-pratt-parser'
const val = stringify({ type: 'JsdocTypeName', value: 'name'}) // -> 'name'
const myRules = identityTransformRules()
myRules.NAME = () => ({ type: 'JsdocTypeName', value: 'funky' })
const val = transform(myRules, result)
```
You can customize the stringification by using `stringifyRules` and `transform`:
This library also supports compatibility modes for catharsis and jsdoctypeparser. The provided transform functions attempt to
transform the output to the expected output of the target library. This will not always be the same as some types are
parsed differently. These modes are thought to make transition easier, but it is advised to use the native output as
this will be more uniform and will contain more information.
[Catharsis compat mode](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#catharsisTransform):
```js
import { stringifyRules, transform } from 'jsdoc-type-pratt-parser'
import { parse, catharsisTransform } from 'jsdoc-type-pratt-parser'
const rules = stringifyRules()
const result = catharsisTransform(parse('myType.<string>', 'closure'))
```
// `result` is the current node and `transform` is a function to transform child nodes.
rules.NAME = (result, transform) => 'something else'
[Jsdoctypeparser compat mode](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#jtpTransform):
const val = transform(rules, { type: 'JsdocTypeName', value: 'name'}) // -> 'something else'
```js
import { parse, jtpTransform } from 'jsdoc-type-pratt-parser'
const result = jtpTransform(parse('myType.<string>', 'closure'))
```
You can traverse a result tree with the `traverse` function:
## Traverse
You can traverse an AST with the [`traverse`](https://jsdoc-type-pratt-parser.github.io/jsdoc-type-pratt-parser/docs/modules.html#traverse) function:
```js

@@ -112,45 +135,33 @@ import { traverse } from 'jsdoc-type-pratt-parser'

You can also build your own transform rules by implementing the `TransformRules<TransformResultType>` interface or you
can build upon the identity ruleset like this:
```js
import { identityTransformRules, transform } from 'jsdoc-type-pratt-parser'
const myRules = identityTransformRules()
myRules.NAME = () => ({ type: 'JsdocTypeName', value: 'funky' })
const val = transform(myRules, result)
```
## Tests Status
This parser runs most tests of https://github.com/hegemonic/catharsis and
https://github.com/jsdoctypeparser/jsdoctypeparser. It compares the results of the different parsing libraries. If you
want to find out where the output differs, look in the tests for the comments `// This seems to be an error of ...` or
the `differ` keyword which indicates that differing results are produced.
https://github.com/jsdoctypeparser/jsdoctypeparser. It compares the results of the different parsing libraries. If you
want to find out where the output differs, look in the tests for the comments `// This seems to be an error of ...` or
the `differ` keyword which indicates that differing results are produced.
## Performance
A simple performance [comparision](benchmark/benchmark.js) using [Benchmark.js](https://benchmarkjs.com/) produced the following results:
A simple [performance comparision](benchmark/benchmark.js) using [Benchmark.js](https://benchmarkjs.com/) produced the following results:
```
Testing expression: Name
catharsis x 36,338 ops/sec ±1.10% (1071 runs sampled)
jsdoc-type-pratt-parser x 400,260 ops/sec ±0.87% (1070 runs sampled)
jsdoctypeparser x 61,847 ops/sec ±1.18% (1071 runs sampled)
catharsis x 37,816 ops/sec ±1.22% (1086 runs sampled)
jsdoc-type-pratt-parser x 602,617 ops/sec ±0.16% (1090 runs sampled)
jsdoctypeparser x 53,256 ops/sec ±0.73% (1081 runs sampled)
The fastest was jsdoc-type-pratt-parser
Testing expression: Array<number>
catharsis x 7,969 ops/sec ±1.05% (1079 runs sampled)
jsdoc-type-pratt-parser x 159,001 ops/sec ±0.95% (1074 runs sampled)
jsdoctypeparser x 42,278 ops/sec ±1.01% (1070 runs sampled)
catharsis x 10,124 ops/sec ±0.56% (1084 runs sampled)
jsdoc-type-pratt-parser x 228,660 ops/sec ±0.40% (1084 runs sampled)
jsdoctypeparser x 42,365 ops/sec ±0.60% (1070 runs sampled)
The fastest was jsdoc-type-pratt-parser
Testing expression: { keyA: Type<A | "string val" >, keyB: function(string, B): A }
catharsis x 933 ops/sec ±1.15% (1070 runs sampled)
jsdoc-type-pratt-parser x 29,596 ops/sec ±0.90% (1068 runs sampled)
jsdoctypeparser x 16,206 ops/sec ±1.38% (1055 runs sampled)
catharsis x 1,138 ops/sec ±0.66% (1087 runs sampled)
jsdoc-type-pratt-parser x 46,535 ops/sec ±0.47% (1090 runs sampled)
jsdoctypeparser x 18,291 ops/sec ±0.71% (1084 runs sampled)
The fastest was jsdoc-type-pratt-parser
```
the test uses catharsis without cache, as this is just a simple lookup table that could easily be implemented for any parser.
The benchmark test uses catharsis without cache.

@@ -160,2 +171,2 @@ ## Development

If you want to contribute see the [Development Guide](DEVELOPMENT.md) to get some pointers. Feel free to create issues if
there is missing information.
there is information missing.

@@ -6,2 +6,5 @@ import { KeyValueResult } from './result/NonRootResult'

/**
* Throws an error if the provided result is not a {@link RootResult}
*/
export function assertRootResult (result?: IntermediateResult): RootResult {

@@ -8,0 +11,0 @@ if (result === undefined) {

import { EarlyEndOfParseError, NoParsletFoundError } from './errors'
import { Token, TokenType } from './lexer/Token'
import { TokenType } from './lexer/Token'
import { Lexer } from './lexer/Lexer'

@@ -30,7 +30,10 @@ import { Grammar } from './grammars/Grammar'

/**
* Parses a given string and throws an error if the parse ended before the end of the string.
*/
parseText (text: string): RootResult {
this.lexer.lex(text)
const result = this.parseType(Precedence.ALL)
if (this.getToken().type !== 'EOF') {
throw new EarlyEndOfParseError(this.getToken())
if (this.lexer.token().type !== 'EOF') {
throw new EarlyEndOfParseError(this.lexer.token())
}

@@ -40,2 +43,5 @@ return result

/**
* Parses with the current lexer and asserts that the result is a {@link RootResult}.
*/
public parseType (precedence: Precedence): RootResult {

@@ -45,2 +51,5 @@ return assertRootResult(this.parseIntermediateType(precedence))

/**
* 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 {

@@ -56,2 +65,6 @@ for (const parslet of this.grammar) {

/**
* The main parsing function. First it tries to parse the current state in the prefix step, and then it continues
* to parse the state in the infix step.
*/
public parseIntermediateType (precedence: Precedence): IntermediateResult {

@@ -61,3 +74,3 @@ const result = this.tryParslets(precedence, null)

if (result === null) {
throw new NoParsletFoundError(this.getToken())
throw new NoParsletFoundError(this.lexer.token())
}

@@ -68,2 +81,6 @@

/**
* In the infix parsing step the parser continues to parse the current state with all parslets until none returns
* a result.
*/
public parseInfixIntermediateType (result: IntermediateResult, precedence: Precedence): IntermediateResult {

@@ -80,2 +97,6 @@ let newResult = this.tryParslets(precedence, result)

/**
* 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 {

@@ -92,14 +113,2 @@ if (!Array.isArray(types)) {

public getToken (): Token {
return this.lexer.token()
}
public peekToken (): Token {
return this.lexer.peek()
}
public previousToken (): Token | undefined {
return this.lexer.last()
}
getLexer (): Lexer {

@@ -106,0 +115,0 @@ return this.lexer

@@ -41,3 +41,3 @@ import { composeParslet, ParsletFunction } from './Parslet'

const hasParenthesis = parser.getToken().type === '('
const hasParenthesis = parser.getLexer().token().type === '('

@@ -44,0 +44,0 @@ if (!hasParenthesis) {

@@ -94,4 +94,5 @@ import { ParsletFunction } from './Parslet'

if (brackets && !parser.consume(']')) {
throw new Error(`Unterminated square brackets. Next token is '${parser.getToken().type}' ` +
`with text '${parser.getToken().text}'`)
const token = parser.getLexer().token()
throw new Error(`Unterminated square brackets. Next token is '${token.type}' ` +
`with text '${token.text}'`)
}

@@ -98,0 +99,0 @@

@@ -7,9 +7,9 @@ import { composeParslet } from './Parslet'

parsePrefix: parser => {
const token = parser.getToken()
const value = parseFloat(parser.getLexer().token().text)
parser.consume('Number')
return {
type: 'JsdocTypeNumber',
value: parseInt(token.text, 10)
value
}
}
})

@@ -12,3 +12,3 @@ import { composeParslet } from './Parslet'

if (left.type !== 'JsdocTypeName') {
throw new UnexpectedTypeError(left, 'Predicate always have to have names on the left side.')
throw new UnexpectedTypeError(left, 'A typescript predicate always has to have a name on the left side.')
}

@@ -15,0 +15,0 @@

@@ -34,3 +34,3 @@ import { composeParslet, ParsletFunction } from './Parslet'

let token = parser.getToken()
let token = parser.getLexer().token()
if (parser.consume('StringValue')) {

@@ -50,3 +50,3 @@ result = {

value += token.text
token = parser.getToken()
token = parser.getLexer().token()
}

@@ -53,0 +53,0 @@ result = {

@@ -33,4 +33,4 @@ import { composeParslet } from './Parslet'

throw new Error('Unacceptable token: ' + parser.getToken().text)
throw new Error('Unacceptable token: ' + parser.getLexer().token().text)
}
})

@@ -7,9 +7,9 @@ import { composeParslet } from './Parslet'

parsePrefix: parser => {
const token = parser.getToken()
const text = parser.getLexer().token().text
parser.consume('StringValue')
return {
type: 'JsdocTypeStringValue',
value: token.text.slice(1, -1),
value: text.slice(1, -1),
meta: {
quote: token.text[0] === '\'' ? 'single' : 'double'
quote: text[0] === '\'' ? 'single' : 'double'
}

@@ -16,0 +16,0 @@ }

@@ -5,2 +5,9 @@ import { NonRootResult } from './result/NonRootResult'

/**
* A node visitor function.
* @param node the visited node.
* @param parentNode the parent node.
* @param property the property on the parent node that contains the visited node. It can be the node itself or
* an array of nodes.
*/
type NodeVisitor = (node: NonRootResult, parentNode?: NonRootResult, property?: string) => void

@@ -31,4 +38,10 @@

/**
* A function to traverse an AST. It traverses it depth first.
* @param node the node to start traversing at.
* @param onEnter node visitor function that will be called on entering the node. This corresponds to preorder traversing.
* @param onLeave node visitor function that will be called on leaving the node. This corresponds to postorder traversing.
*/
export function traverse (node: RootResult, onEnter?: NodeVisitor, onLeave?: NodeVisitor): void {
_traverse(node, undefined, undefined, onEnter, onLeave)
}

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc