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
1
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 1.0.0-alpha.2 to 1.0.0-alpha.3.0

dist/src/assertTypes.js

1510

dist/index.js

@@ -7,2 +7,53 @@ (function (global, factory) {

function tokenToString(token) {
if (token.text !== undefined && token.text !== '') {
return `'${token.type}' with value '${token.text}'`;
}
else {
return `'${token.type}'`;
}
}
class NoParsletFoundError extends Error {
constructor(token) {
super(`No parslet found for token: ${tokenToString(token)}`);
this.token = token;
Object.setPrototypeOf(this, NoParsletFoundError.prototype);
}
getToken() {
return this.token;
}
}
class EarlyEndOfParseError extends Error {
constructor(token) {
super(`The parsing ended early. The next token was: ${tokenToString(token)}`);
this.token = token;
Object.setPrototypeOf(this, EarlyEndOfParseError.prototype);
}
getToken() {
return this.token;
}
}
class UnexpectedTypeError extends Error {
constructor(result) {
super(`Unexpected type: '${result.type}'`);
Object.setPrototypeOf(this, UnexpectedTypeError.prototype);
}
}
// export class UnexpectedTokenError extends Error {
// private expected: Token
// private found: Token
//
// constructor (expected: Token, found: Token) {
// super(`The parsing ended early. The next token was: ${tokenToString(token)}`)
//
// this.token = token
//
// Object.setPrototypeOf(this, EarlyEndOfParseError.prototype)
// }
//
// getToken() {
// return this.token
// }
// }
function makePunctuationRule(type) {

@@ -132,2 +183,3 @@ return text => {

eofRule,
makePunctuationRule('=>'),
makePunctuationRule('('),

@@ -156,3 +208,2 @@ makePunctuationRule(')'),

makeKeyWordRule('null'),
makeKeyWordRule('void'),
makeKeyWordRule('function'),

@@ -204,6 +255,7 @@ makeKeyWordRule('this'),

read() {
const text = this.text.trim();
for (const rule of rules) {
const token = rule(this.text);
const token = rule(text);
if (token !== null) {
this.text = this.text.slice(token.text.length).trim();
this.text = text.slice(token.text.length);
return token;

@@ -233,9 +285,6 @@ }

class UnexpectedTypeError extends Error {
constructor(result) {
super(`Unexpected type: '${result.type}'`);
Object.setPrototypeOf(this, UnexpectedTypeError.prototype);
function assertTerminal(result) {
if (result === undefined) {
throw new Error('Unexpected undefined');
}
}
function assertTerminal(result) {
if (result.type === 'KEY_VALUE' || result.type === 'NUMBER' || result.type === 'PARAMETER_LIST') {

@@ -248,3 +297,3 @@ throw new UnexpectedTypeError(result);

if (result.type === 'KEY_VALUE') {
if (result.key.type !== 'NAME') {
if (result.left.type !== 'NAME') {
throw new UnexpectedTypeError(result);

@@ -258,3 +307,3 @@ }

if (result.type === 'KEY_VALUE') {
if (result.key.type !== 'NAME') {
if (result.left.type !== 'NAME') {
throw new UnexpectedTypeError(result);

@@ -269,2 +318,15 @@ }

}
function assertNumberOrVariadicName(result) {
var _a;
if (result.type === 'VARIADIC') {
if (((_a = result.element) === null || _a === void 0 ? void 0 : _a.type) === 'NAME') {
return result;
}
throw new UnexpectedTypeError(result);
}
if (result.type !== 'NUMBER' && result.type !== 'NAME') {
throw new UnexpectedTypeError(result);
}
return result;
}

@@ -276,26 +338,21 @@ // higher precedence = higher importance

Precedence[Precedence["PARAMETER_LIST"] = 1] = "PARAMETER_LIST";
Precedence[Precedence["PARENTHESIS"] = 2] = "PARENTHESIS";
Precedence[Precedence["UNION"] = 3] = "UNION";
Precedence[Precedence["PREFIX"] = 4] = "PREFIX";
Precedence[Precedence["POSTFIX"] = 5] = "POSTFIX";
Precedence[Precedence["RECORD"] = 6] = "RECORD";
Precedence[Precedence["UNION"] = 2] = "UNION";
Precedence[Precedence["PREFIX"] = 3] = "PREFIX";
Precedence[Precedence["POSTFIX"] = 4] = "POSTFIX";
Precedence[Precedence["TUPLE"] = 5] = "TUPLE";
Precedence[Precedence["OBJECT"] = 6] = "OBJECT";
Precedence[Precedence["SYMBOL"] = 7] = "SYMBOL";
Precedence[Precedence["OPTIONAL"] = 8] = "OPTIONAL";
Precedence[Precedence["NULLABLE"] = 9] = "NULLABLE";
Precedence[Precedence["FUNCTION"] = 10] = "FUNCTION";
Precedence[Precedence["ARROW"] = 11] = "ARROW";
Precedence[Precedence["KEY_VALUE"] = 12] = "KEY_VALUE";
Precedence[Precedence["GENERIC"] = 13] = "GENERIC";
Precedence[Precedence["PROPERTY_PATH"] = 14] = "PROPERTY_PATH";
Precedence[Precedence["KEY_OF_TYPE_OF"] = 15] = "KEY_OF_TYPE_OF";
Precedence[Precedence["KEY_OF_TYPE_OF"] = 10] = "KEY_OF_TYPE_OF";
Precedence[Precedence["KEY_VALUE"] = 11] = "KEY_VALUE";
Precedence[Precedence["FUNCTION"] = 12] = "FUNCTION";
Precedence[Precedence["ARROW"] = 13] = "ARROW";
Precedence[Precedence["GENERIC"] = 14] = "GENERIC";
Precedence[Precedence["NAME_PATH"] = 15] = "NAME_PATH";
Precedence[Precedence["ARRAY_BRACKETS"] = 16] = "ARRAY_BRACKETS";
Precedence[Precedence["SPECIAL_TYPES"] = 17] = "SPECIAL_TYPES";
Precedence[Precedence["PARENTHESIS"] = 17] = "PARENTHESIS";
Precedence[Precedence["SPECIAL_TYPES"] = 18] = "SPECIAL_TYPES";
})(Precedence || (Precedence = {}));
class NoParsletFoundError extends Error {
constructor(token) {
super(`No parslet found for token: '${token.type}' with value '${token.text}'`);
Object.setPrototypeOf(this, NoParsletFoundError.prototype);
}
}
class ParserEngine {

@@ -312,3 +369,3 @@ constructor(grammar) {

if (!this.consume('EOF')) {
throw new Error(`Unexpected early end of parse. Next token: '${this.getToken().text}'`);
throw new EarlyEndOfParseError(this.getToken());
}

@@ -328,3 +385,3 @@ return result;

try {
return this.parseType(precedence);
return this.parseNonTerminalType(precedence);
}

@@ -395,61 +452,2 @@ catch (e) {

const reservedWords = [
'null',
'true',
'false',
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'export',
'extends',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'return',
'super',
'switch',
'this',
'throw',
'try',
'typeof',
'var',
'void',
'while',
'with',
'yield'
];
class NameParslet {
accepts(type, next) {
return type === 'Identifier' || type === 'this' || type === 'new';
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
const token = parser.getToken();
parser.consume('Identifier') || parser.consume('this') || parser.consume('new');
const result = {
type: 'NAME',
name: token.text
};
if (reservedWords.includes(token.text)) {
result.reservedWord = true;
}
return result;
}
}
function isQuestionMarkUnknownType(next) {

@@ -467,52 +465,24 @@ return next === 'EOF' || next === '|' || next === ',' || next === ')' || next === '>';

parsePrefix(parser) {
switch (parser.getToken().type) {
case 'null':
parser.consume('null');
return {
type: 'NULL'
};
case 'undefined':
parser.consume('undefined');
return {
type: 'UNDEFINED'
};
case '*':
parser.consume('*');
return {
type: 'ALL'
};
case '?':
parser.consume('?');
return {
type: 'UNKNOWN'
};
default:
throw new Error('Unacceptable token: ' + parser.getToken().text);
if (parser.consume('null')) {
return {
type: 'NULL'
};
}
}
}
class VariadicParslet {
accepts(type) {
return type === '...';
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
parser.consume('...');
const shouldClose = parser.consume('[');
const value = parser.parseType(Precedence.PREFIX);
if (shouldClose && !parser.consume(']')) {
throw new Error('Unterminated variadic type. Missing \']\'');
if (parser.consume('undefined')) {
return {
type: 'UNDEFINED'
};
}
value.repeatable = true;
return value;
if (parser.consume('*')) {
return {
type: 'ANY'
};
}
if (parser.consume('?')) {
return {
type: 'UNKNOWN'
};
}
throw new Error('Unacceptable token: ' + parser.getToken().text);
}
parseInfix(parser, left) {
parser.consume('...');
const result = assertTerminal(left);
result.repeatable = true;
return result;
}
}

@@ -525,3 +495,3 @@

getPrecedence() {
return Precedence.RECORD;
return Precedence.OBJECT;
}

@@ -531,12 +501,12 @@ parsePrefix(parser) {

const result = {
type: 'RECORD',
fields: []
type: 'OBJECT',
elements: []
};
if (!parser.consume('}')) {
do {
const field = parser.parseNonTerminalType(Precedence.RECORD);
const field = parser.parseNonTerminalType(Precedence.OBJECT);
if (field.type !== 'NAME' && field.type !== 'NUMBER' && field.type !== 'KEY_VALUE') {
throw new Error('records may only contain \'NAME\', \'NUMBER\' or \'KEY_VALUE\' fields.');
}
result.fields.push(field);
result.elements.push(field);
} while (parser.consume(','));

@@ -551,29 +521,2 @@ if (!parser.consume('}')) {

class ModuleParslet {
accepts(type, next) {
return type === 'module';
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
parser.consume('module');
if (!parser.consume(':')) {
throw new Error('module needs to have a \':\' afterwards.');
}
let result = 'module:';
const allowed = ['Identifier', '~', '@', '/', '#'];
let token = parser.getToken();
while (allowed.includes(token.type)) {
result += token.text;
parser.consume(token.type);
token = parser.getToken();
}
return {
type: 'MODULE',
path: result
};
}
}
class GenericParslet {

@@ -587,3 +530,3 @@ accepts(type, next) {

parseInfix(parser, left) {
parser.consume('.');
const dot = parser.consume('.');
parser.consume('<');

@@ -599,48 +542,8 @@ const objects = [];

type: 'GENERIC',
subject: assertTerminal(left),
objects
};
}
}
class OptionalParslet {
accepts(type, next) {
return type === '=' && next !== '>';
}
getPrecedence() {
return Precedence.OPTIONAL;
}
parseInfix(parser, left) {
parser.consume('=');
const result = assertTerminal(left);
result.optional = true;
return result;
}
}
class PropertyPathParslet {
accepts(type, next) {
return type === '.' && next !== '<';
}
getPrecedence() {
return Precedence.PROPERTY_PATH;
}
parseInfix(parser, left) {
parser.consume('.');
const path = [];
const allowed = ['Identifier', 'Number', 'StringValue'];
let next;
do {
const token = parser.getToken();
if (!allowed.includes(token.type)) {
throw new Error(`The token type '${token.type}' is not allowed in a property path.`);
left: assertTerminal(left),
elements: objects,
meta: {
brackets: '<>',
dot
}
path.push(token.text);
parser.consume(token.type);
next = parser.peekToken();
} while (next.type !== '<' && parser.consume('.'));
return {
type: 'PROPERTY_PATH',
left: assertTerminal(left),
path
};

@@ -652,3 +555,3 @@ }

accepts(type, next) {
return type === '(' && next !== ')';
return type === '(';
}

@@ -660,24 +563,9 @@ getPrecedence() {

parser.consume('(');
const result = parser.parseNonTerminalType(Precedence.ALL);
const result = parser.tryParseType(Precedence.ALL);
if (!parser.consume(')')) {
throw new Error('Unterminated parenthesis');
}
return result;
}
}
class KeyValueParslet {
accepts(type, next) {
return type === ':';
}
getPrecedence() {
return Precedence.KEY_VALUE;
}
parseInfix(parser, left) {
parser.consume(':');
const value = parser.parseType(Precedence.KEY_VALUE);
return {
type: 'KEY_VALUE',
key: left.type === 'NUMBER' ? left : assertTerminal(left),
value: value
type: 'PARENTHESIS',
element: result // NOTE: this can only be non-terminal or undefined if it is a parameter list
};

@@ -720,5 +608,18 @@ }

do {
const next = parser.parseNonTerminalType(Precedence.PARAMETER_LIST);
elements.push(assertNamedKeyValueOrTerminal(next));
try {
const next = parser.parseNonTerminalType(Precedence.PARAMETER_LIST);
elements.push(assertNamedKeyValueOrTerminal(next));
}
catch (e) {
if (this.allowTrailingComma && e instanceof NoParsletFoundError) {
break;
}
else {
throw e;
}
}
} while (parser.consume(','));
if (elements.length > 0 && elements.slice(0, -1).some(e => e.type === 'VARIADIC')) {
throw new Error('Only the last parameter may be a rest parameter');
}
return {

@@ -733,3 +634,3 @@ type: 'PARAMETER_LIST',

accepts(type, next) {
return (type === '?' && !isQuestionMarkUnknownType(next)) || type === '!';
return type === '?' && !isQuestionMarkUnknownType(next);
}

@@ -740,9 +641,10 @@ getPrecedence() {

parsePrefix(parser) {
const nullable = parser.consume('?') || !parser.consume('!');
const value = parser.parseType(Precedence.NULLABLE);
if (value.nullable !== undefined) {
throw new Error('Multiple nullable modifiers on same type');
}
value.nullable = nullable;
return value;
parser.consume('?');
return {
type: 'NULLABLE',
element: parser.parseType(Precedence.NULLABLE),
meta: {
position: 'PREFIX'
}
};
}

@@ -752,3 +654,3 @@ }

accepts(type, next) {
return type === '?' || type === '!';
return type === '?';
}

@@ -759,35 +661,60 @@ getPrecedence() {

parseInfix(parser, left) {
const nullable = parser.consume('?') || !parser.consume('!');
const value = assertTerminal(left);
if (value.nullable !== undefined) {
throw new Error('Multiple nullable modifiers on same type');
}
value.nullable = nullable;
return value;
parser.consume('?');
return {
type: 'NULLABLE',
element: assertTerminal(left),
meta: {
position: 'SUFFIX'
}
};
}
}
class OptionalParslet {
accepts(type, next) {
return type === '=';
}
getPrecedence() {
return Precedence.OPTIONAL;
}
parsePrefix(parser) {
parser.consume('=');
return {
type: 'OPTIONAL',
element: parser.parseType(Precedence.OPTIONAL),
meta: {
position: 'PREFIX'
}
};
}
parseInfix(parser, left) {
parser.consume('=');
return {
type: 'OPTIONAL',
element: assertTerminal(left),
meta: {
position: 'SUFFIX'
}
};
}
}
const baseGrammar = () => {
return {
prefixParslets: [
new NameParslet(),
new SpecialTypesParslet(),
new NullablePrefixParslet(),
new VariadicParslet(),
new OptionalParslet(),
new RecordParslet(),
new ModuleParslet(),
new NumberParslet(),
new ParenthesisParslet()
new ParenthesisParslet(),
new SpecialTypesParslet()
],
infixParslets: [
new ParameterListParslet({
allowTrailingComma: false
allowTrailingComma: true
}),
new PropertyPathParslet(),
new KeyValueParslet(),
new GenericParslet(),
new UnenclosedUnionParslet(),
new OptionalParslet(),
new NullableInfixParslet(),
new PropertyPathParslet()
new NullableInfixParslet()
]

@@ -800,9 +727,12 @@ };

let parameters;
if (value.type === 'PARAMETER_LIST') {
parameters = value.elements;
if (value.element === undefined) {
parameters = [];
}
else if (value.element.type === 'PARAMETER_LIST') {
parameters = value.element.elements;
}
else {
parameters = [assertNamedKeyValueOrTerminal(value)];
parameters = [value.element];
}
return parameters;
return parameters.map(p => assertNamedKeyValueOrTerminal(p));
}

@@ -840,3 +770,3 @@ getNamedParameters(value) {

parser.consume('function');
const hasParenthesis = parser.consume('(');
const hasParenthesis = parser.getToken().type === '(';
if (!this.allowWithoutParenthesis && !hasParenthesis) {

@@ -847,26 +777,26 @@ throw new Error('function is missing parameter list');

type: 'FUNCTION',
parameters: []
parameters: [],
meta: {
arrow: false,
parenthesis: hasParenthesis
}
};
if (hasParenthesis) {
if (!parser.consume(')')) {
const value = parser.parseNonTerminalType(Precedence.ALL);
if (this.allowNamedParameters === undefined) {
result.parameters = this.getUnnamedParameters(value);
}
else {
result.parameters = this.getParameters(value);
for (const p of result.parameters) {
if (p.type === 'KEY_VALUE' && !this.allowNamedParameters.includes(p.key.name)) {
throw new Error(`only allowed named parameters are ${this.allowNamedParameters.join(',')} but got ${p.type}`);
}
const value = parser.parseNonTerminalType(Precedence.FUNCTION);
if (value.type !== 'PARENTHESIS') {
throw new UnexpectedTypeError(value);
}
if (this.allowNamedParameters === undefined) {
result.parameters = this.getUnnamedParameters(value);
}
else {
result.parameters = this.getParameters(value);
for (const p of result.parameters) {
if (p.type === 'KEY_VALUE' && !this.allowNamedParameters.includes(p.left.value)) {
throw new Error(`only allowed named parameters are ${this.allowNamedParameters.join(',')} but got ${p.type}`);
}
}
if (!parser.consume(')')) {
throw new Error('function parameter list is not terminated');
}
}
if (parser.consume(':')) {
if (!parser.consume('void')) {
result.returnType = parser.parseType(Precedence.PREFIX);
}
result.returnType = parser.parseType(Precedence.PREFIX);
}

@@ -883,2 +813,238 @@ else {

class StringValueParslet {
accepts(type) {
return type === 'StringValue';
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
const token = parser.getToken();
parser.consume('StringValue');
return {
type: 'STRING_VALUE',
value: token.text.slice(1, -1),
meta: {
quote: token.text[0]
}
};
}
}
class NamePathParslet {
constructor(opts) {
this.allowJsdocNamePaths = opts.allowJsdocNamePaths;
this.stringValueParslet = new StringValueParslet();
}
accepts(type, next) {
return (type === '.' && next !== '<') || (this.allowJsdocNamePaths && (type === '~' || type === '#'));
}
getPrecedence() {
return Precedence.NAME_PATH;
}
parseInfix(parser, left) {
const type = parser.getToken().text;
parser.consume('.') || parser.consume('~') || parser.consume('#');
let next;
if (parser.getToken().type === 'StringValue') {
next = this.stringValueParslet.parsePrefix(parser);
}
else {
next = parser.parseNonTerminalType(Precedence.NAME_PATH);
if (next.type !== 'NAME' && next.type !== 'NUMBER') {
throw new UnexpectedTypeError(next);
}
}
return {
type: 'NAME_PATH',
left: assertTerminal(left),
right: next,
meta: {
type
}
};
}
}
class KeyValueParslet {
constructor(opts) {
this.allowOnlyNameOrNumberProperties = opts.allowOnlyNameOrNumberProperties;
}
accepts(type, next) {
return type === ':';
}
getPrecedence() {
return Precedence.KEY_VALUE;
}
parseInfix(parser, left) {
if (this.allowOnlyNameOrNumberProperties && left.type !== 'NUMBER' && left.type !== 'NAME') {
throw new UnexpectedTypeError(left);
}
parser.consume(':');
const value = parser.parseType(Precedence.KEY_VALUE);
return {
type: 'KEY_VALUE',
left: left.type === 'NUMBER' ? left : assertTerminal(left),
right: value
};
}
}
class TypeOfParslet {
accepts(type, next) {
return type === 'typeof';
}
getPrecedence() {
return Precedence.KEY_OF_TYPE_OF;
}
parsePrefix(parser) {
parser.consume('typeof');
return {
type: 'TYPE_OF',
element: assertTerminal(parser.parseType(Precedence.KEY_OF_TYPE_OF))
};
}
}
class VariadicParslet {
constructor(opts) {
this.allowEnclosingBrackets = opts.allowEnclosingBrackets;
}
accepts(type) {
return type === '...';
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
parser.consume('...');
const brackets = this.allowEnclosingBrackets && parser.consume('[');
const value = parser.tryParseType(Precedence.PREFIX);
if (brackets && !parser.consume(']')) {
throw new Error('Unterminated variadic type. Missing \']\'');
}
if (value !== undefined) {
return {
type: 'VARIADIC',
element: assertTerminal(value),
meta: {
position: 'PREFIX',
squareBrackets: brackets
}
};
}
else {
return {
type: 'VARIADIC',
meta: {
position: 'ONLY_DOTS',
squareBrackets: false
}
};
}
}
parseInfix(parser, left) {
parser.consume('...');
return {
type: 'VARIADIC',
element: assertTerminal(left),
meta: {
position: 'SUFFIX',
squareBrackets: false
}
};
}
}
const reservedWords = [
'null',
'true',
'false',
'break',
'case',
'catch',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'else',
'export',
'extends',
'finally',
'for',
'function',
'if',
'import',
'in',
'instanceof',
'new',
'return',
'super',
'switch',
'this',
'throw',
'try',
'typeof',
'var',
'void',
'while',
'with',
'yield'
];
class NameParslet {
constructor(options) {
this.allowedAdditionalTokens = options.allowedAdditionalTokens;
}
accepts(type, next) {
return type === 'Identifier' || type === 'this' || type === 'new' || this.allowedAdditionalTokens.includes(type);
}
getPrecedence() {
return Precedence.PREFIX;
}
parsePrefix(parser) {
const token = parser.getToken();
parser.consume('Identifier') || parser.consume('this') || parser.consume('new') ||
this.allowedAdditionalTokens.some(type => parser.consume(type));
return {
type: 'NAME',
value: token.text,
meta: {
reservedWord: reservedWords.includes(token.text)
}
};
}
}
class NotNullableParslet {
accepts(type, next) {
return type === '!';
}
getPrecedence() {
return Precedence.NULLABLE;
}
parsePrefix(parser) {
parser.consume('!');
return {
type: 'NOT_NULLABLE',
element: parser.parseType(Precedence.NULLABLE),
meta: {
position: 'PREFIX'
}
};
}
parseInfix(parser, left) {
parser.consume('!');
return {
type: 'NOT_NULLABLE',
element: assertTerminal(left),
meta: {
position: 'SUFFIX'
}
};
}
}
const closureGrammar = () => {

@@ -889,2 +1055,6 @@ const { prefixParslets, infixParslets } = baseGrammar();

...prefixParslets,
new NameParslet({
allowedAdditionalTokens: []
}),
new TypeOfParslet(),
new FunctionParslet({

@@ -894,5 +1064,21 @@ allowWithoutParenthesis: false,

allowNoReturnType: true
})
}),
new VariadicParslet({
allowEnclosingBrackets: false
}),
new NameParslet({
allowedAdditionalTokens: ['keyof']
}),
new NotNullableParslet()
],
infixParslets
infixParslets: [
...infixParslets,
new NamePathParslet({
allowJsdocNamePaths: false
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: true
}),
new NotNullableParslet()
]
};

@@ -915,10 +1101,7 @@ };

type: 'SYMBOL',
name: left.name
value: left.value
};
if (!parser.consume(')')) {
const next = parser.parseNonTerminalType(Precedence.SYMBOL);
if (next.type !== 'NUMBER' && next.type !== 'NAME') {
throw new Error('Symbol value must be a number or a name');
}
result.value = next;
result.element = assertNumberOrVariadicName(next);
if (!parser.consume(')')) {

@@ -932,30 +1115,2 @@ throw new Error('Symbol does not end after value');

class ClassPathParslet {
accepts(type, next) {
return type === '#' || type === '~';
}
getPrecedence() {
return Precedence.POSTFIX;
}
parseInfix(parser, left) {
if (left.type !== 'NAME') {
throw new Error('All elements of class path have to be identifiers');
}
let result = left.name;
let lastToken = parser.getToken();
while (parser.consume('#') || parser.consume('~') || parser.consume('/')) {
const next = parser.parseType(Precedence.POSTFIX);
if (next.type !== 'NAME') {
throw new Error('All elements of class path have to be identifiers');
}
result += lastToken.text + next.name;
lastToken = parser.getToken();
}
return {
type: 'NAME',
name: result
};
}
}
class ArrayBracketsParslet {

@@ -973,9 +1128,16 @@ accepts(type, next) {

type: 'GENERIC',
subject: {
left: {
type: 'NAME',
name: 'Array'
value: 'Array',
meta: {
reservedWord: false
}
},
objects: [
elements: [
assertTerminal(left)
]
],
meta: {
brackets: '[]',
dot: false
}
};

@@ -985,5 +1147,5 @@ }

class StringValueParslet {
accepts(type) {
return type === 'StringValue';
class ModuleParslet {
accepts(type, next) {
return type === 'module';
}

@@ -994,9 +1156,31 @@ getPrecedence() {

parsePrefix(parser) {
const token = parser.getToken();
parser.consume('StringValue');
return {
type: 'STRING_VALUE',
value: token.text.slice(1, -1),
quote: token.text[0]
};
parser.consume('module');
if (!parser.consume(':')) {
throw new Error('module needs to have a \':\' afterwards.');
}
let token = parser.getToken();
if (parser.consume('StringValue')) {
return {
type: 'MODULE',
value: token.text.slice(1, -1),
meta: {
quote: token.text[0]
}
};
}
else {
let result = '';
const allowed = ['Identifier', '@', '/'];
while (allowed.some(type => parser.consume(type))) {
result += token.text;
token = parser.getToken();
}
return {
type: 'MODULE',
value: result,
meta: {
quote: undefined
}
};
}
}

@@ -1015,3 +1199,11 @@ }

}),
new StringValueParslet()
new StringValueParslet(),
new ModuleParslet(),
new VariadicParslet({
allowEnclosingBrackets: true
}),
new NameParslet({
allowedAdditionalTokens: ['keyof']
}),
new NotNullableParslet()
],

@@ -1021,4 +1213,13 @@ infixParslets: [

new SymbolParslet(),
new ClassPathParslet(),
new ArrayBracketsParslet()
new ArrayBracketsParslet(),
new NamePathParslet({
allowJsdocNamePaths: true
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: false
}),
new VariadicParslet({
allowEnclosingBrackets: true
}),
new NotNullableParslet()
]

@@ -1028,18 +1229,34 @@ };

class TypeOfParslet {
class TupleParslet {
constructor(opts) {
this.allowQuestionMark = opts.allowQuestionMark;
}
accepts(type, next) {
return type === 'typeof';
return type === '[';
}
getPrecedence() {
return Precedence.KEY_OF_TYPE_OF;
return Precedence.TUPLE;
}
parsePrefix(parser) {
parser.consume('typeof');
parser.consume('[');
const result = {
type: 'TYPE_OF'
type: 'TUPLE',
elements: []
};
const value = parser.tryParseType(Precedence.KEY_OF_TYPE_OF);
if (value !== undefined) {
result.value = assertTerminal(value);
if (parser.consume(']')) {
return result;
}
const typeList = parser.parseNonTerminalType(Precedence.ALL);
if (typeList.type === 'PARAMETER_LIST') {
result.elements = typeList.elements.map(assertTerminal);
}
else {
result.elements = [assertTerminal(typeList)];
}
if (!parser.consume(']')) {
throw new Error('Unterminated \'[\'');
}
if (!this.allowQuestionMark && result.elements.some(e => e.type === 'UNKNOWN')) {
throw new Error('Question mark in tuple not allowed');
}
return result;

@@ -1058,10 +1275,6 @@ }

parser.consume('keyof');
const result = {
type: 'KEY_OF'
return {
type: 'KEY_OF',
element: assertTerminal(parser.parseType(Precedence.KEY_OF_TYPE_OF))
};
const value = parser.tryParseType(Precedence.KEY_OF_TYPE_OF);
if (value !== undefined) {
result.value = assertTerminal(value);
}
return result;
}

@@ -1091,3 +1304,3 @@ }

type: 'IMPORT',
path
element: path
};

@@ -1105,17 +1318,16 @@ }

parsePrefix(parser) {
parser.consume('(');
const hasParenthesis = parser.consume('(');
parser.consume(')');
if (!parser.consume('=') || !parser.consume('>')) {
if (!parser.consume('=>')) {
throw new Error('Unexpected empty parenthesis. Expected \'=>\' afterwards.');
}
const result = {
return {
type: 'FUNCTION',
parameters: [],
arrow: true
meta: {
arrow: true,
parenthesis: hasParenthesis
},
returnType: parser.parseType(Precedence.ALL)
};
if (!parser.consume('void')) {
const right = parser.parseType(Precedence.ALL);
result.returnType = right;
}
return result;
}

@@ -1125,3 +1337,3 @@ }

accepts(type, next) {
return type === '=' && next === '>';
return type === '=>';
}

@@ -1132,18 +1344,15 @@ getPrecedence() {

parseInfix(parser, left) {
var _a;
if (((_a = parser.previousToken()) === null || _a === void 0 ? void 0 : _a.type) !== ')') {
throw new Error('Unexpected Arrow. Expected parenthesis before.');
if (left.type !== 'PARENTHESIS') {
throw new UnexpectedTypeError(left);
}
parser.consume('=');
parser.consume('>');
const result = {
parser.consume('=>');
return {
type: 'FUNCTION',
parameters: this.getParameters(left).map(assertNamedKeyValueOrName),
arrow: true
meta: {
arrow: true,
parenthesis: true
},
returnType: parser.parseType(Precedence.ALL)
};
if (!parser.consume('void')) {
const right = parser.parseType(Precedence.ALL);
result.returnType = right;
}
return result;
}

@@ -1154,2 +1363,5 @@ }

const { prefixParslets, infixParslets } = baseGrammar();
// typescript does not support explicit non nullability
// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#patterns-that-are-known-not-to-be-supported
// module seems not to be supported
return {

@@ -1164,4 +1376,14 @@ prefixParslets: [

new FunctionParslet({
allowWithoutParenthesis: true,
allowNoReturnType: false
allowWithoutParenthesis: false,
allowNoReturnType: false,
allowNamedParameters: ['this', 'new']
}),
new TupleParslet({
allowQuestionMark: false
}),
new VariadicParslet({
allowEnclosingBrackets: false
}),
new NameParslet({
allowedAdditionalTokens: []
})

@@ -1172,4 +1394,9 @@ ],

new ArrayBracketsParslet(),
new VariadicParslet(),
new ArrowFunctionWithParametersParslet()
new ArrowFunctionWithParametersParslet(),
new NamePathParslet({
allowJsdocNamePaths: false
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: true
})
]

@@ -1200,121 +1427,425 @@ };

/**
* @internal
*/
function catharsisTransform(object) {
var _a, _b, _c;
const newObject = Object.assign({}, object);
let value;
switch (object.type) {
case 'ALL':
newObject.type = 'AllLiteral';
break;
case 'NULL':
newObject.type = 'NullLiteral';
break;
case 'STRING_VALUE':
newObject.type = 'NameExpression';
delete newObject.value;
delete newObject.quote;
newObject.name = `${object.quote}${object.value}${object.quote}`;
break;
case 'UNDEFINED':
newObject.type = 'UndefinedLiteral';
break;
case 'UNKNOWN':
newObject.type = 'UnknownLiteral';
break;
case 'FUNCTION':
newObject.type = 'FunctionType';
delete newObject.parameters;
newObject.params = object.parameters.filter(p => {
if (p.type === 'KEY_VALUE' && p.key.type === 'NAME') {
if (p.key.name === 'this') {
newObject.this = catharsisTransform(p.value);
return false;
}
if (p.key.name === 'new') {
newObject.new = catharsisTransform(p.value);
return false;
}
}
return true;
}).map(catharsisTransform);
if (object.returnType !== undefined) {
delete newObject.returnType;
newObject.result = catharsisTransform(object.returnType);
function transform(rules, parseResult) {
const rule = rules[parseResult.type];
if (rule === undefined) {
throw new Error(`In this set of transform rules exists no rule for type ${parseResult.type}.`);
}
return rule(parseResult, aParseResult => transform(rules, aParseResult));
}
function notAvailableTransform(parseResult) {
throw new Error('This transform is not available. Are you trying the correct parsing mode?');
}
function extractSpecialParams(source) {
const result = {
params: []
};
for (const param of source.parameters) {
if (param.type === 'KEY_VALUE' && param.left.type === 'NAME') {
if (param.left.value === 'this') {
result.this = param.right;
}
break;
case 'GENERIC':
newObject.type = 'TypeApplication';
delete newObject.objects;
newObject.applications = object.objects.map(catharsisTransform);
delete newObject.subject;
newObject.expression = catharsisTransform(object.subject);
break;
case 'MODULE':
newObject.type = 'NameExpression';
delete newObject.path;
newObject.name = object.path;
break;
case 'NAME':
newObject.type = 'NameExpression';
break;
case 'NUMBER':
newObject.type = 'NameExpression';
delete newObject.value;
newObject.name = object.value.toString(10);
break;
case 'RECORD':
newObject.type = 'RecordType';
newObject.fields = object.fields.map(field => {
if (field.type !== 'KEY_VALUE') {
else if (param.left.value === 'new') {
result.new = param.right;
}
else {
result.params.push(param);
}
}
else {
result.params.push(param);
}
}
return result;
}
const catharsisTransformRules = {
OPTIONAL: (result, transform) => {
const transformed = transform(result.element);
transformed.optional = true;
return transformed;
},
NULLABLE: (result, transform) => {
const transformed = transform(result.element);
transformed.nullable = true;
return transformed;
},
NOT_NULLABLE: (result, transform) => {
const transformed = transform(result.element);
transformed.nullable = false;
return transformed;
},
VARIADIC: (result, transform) => {
if (result.element === undefined) {
throw new Error('dots without value are not allowed in catharsis mode');
}
const transformed = transform(result.element);
transformed.repeatable = true;
return transformed;
},
ANY: () => ({
type: 'AllLiteral'
}),
NULL: () => ({
type: 'NullLiteral'
}),
STRING_VALUE: result => ({
type: 'NameExpression',
name: `${result.meta.quote}${result.value}${result.meta.quote}`
}),
UNDEFINED: () => ({
type: 'UndefinedLiteral'
}),
UNKNOWN: () => ({
type: 'UnknownLiteral'
}),
FUNCTION: (result, transform) => {
const params = extractSpecialParams(result);
const transformed = {
type: 'FunctionType',
params: params.params.map(transform)
};
if (params.this !== undefined) {
transformed.this = transform(params.this);
}
if (params.new !== undefined) {
transformed.new = transform(params.new);
}
if (result.returnType !== undefined) {
transformed.result = transform(result.returnType);
}
return transformed;
},
GENERIC: (result, transform) => ({
type: 'TypeApplication',
applications: result.elements.map(o => transform(o)),
expression: transform(result.left)
}),
MODULE: result => {
var _a;
const quote = (_a = result.meta.quote) !== null && _a !== void 0 ? _a : '';
return {
type: 'NameExpression',
name: 'module:' + quote + result.value + quote
};
},
NAME: result => {
const transformed = {
type: 'NameExpression',
name: result.value
};
if (result.meta.reservedWord) {
transformed.reservedWord = true;
}
return transformed;
},
NUMBER: result => ({
type: 'NameExpression',
name: result.value.toString()
}),
OBJECT: (result, transform) => {
const transformed = {
type: 'RecordType',
fields: []
};
for (const field of result.elements) {
if (field.type !== 'KEY_VALUE') {
transformed.fields.push({
type: 'FieldType',
key: transform(field),
value: undefined
});
}
else {
transformed.fields.push(transform(field));
}
}
return transformed;
},
UNION: (result, transform) => ({
type: 'TypeUnion',
elements: result.elements.map(e => transform(e))
}),
KEY_VALUE: (result, transform) => ({
type: 'FieldType',
key: transform(result.left),
value: transform(result.right)
}),
NAME_PATH: (result, transform) => {
const leftResult = transform(result.left);
const rightResult = transform(result.right);
return {
type: 'NameExpression',
name: `${leftResult.name}${result.meta.type}${rightResult.name}`
};
},
SYMBOL: result => {
let value = '';
let element = result.element;
let trailingDots = false;
if ((element === null || element === void 0 ? void 0 : element.type) === 'VARIADIC') {
if (element.meta.position === 'PREFIX') {
value = '...';
}
else {
trailingDots = true;
}
element = element.element;
}
if ((element === null || element === void 0 ? void 0 : element.type) === 'NAME') {
value += element.value;
}
else if ((element === null || element === void 0 ? void 0 : element.type) === 'NUMBER') {
value += element.value.toString();
}
if (trailingDots) {
value += '...';
}
return {
type: 'NameExpression',
name: `${result.value}(${value})`
};
},
PARENTHESIS: (result, transform) => transform(assertTerminal(result.element)),
IMPORT: notAvailableTransform,
KEY_OF: notAvailableTransform,
PARAMETER_LIST: notAvailableTransform,
TUPLE: notAvailableTransform,
TYPE_OF: notAvailableTransform
};
function catharsisTransform(result) {
return transform(catharsisTransformRules, result);
}
function getQuoteStyle(quote) {
switch (quote) {
case undefined:
return 'none';
case '\'':
return 'single';
case '"':
return 'double';
}
}
function getMemberType(type) {
switch (type) {
case '~':
return 'INNER_MEMBER';
case '#':
return 'INSTANCE_MEMBER';
case '.':
return 'MEMBER';
}
}
const jtpRules = {
OPTIONAL: (result, transform) => ({
type: 'OPTIONAL',
value: transform(result.element),
meta: {
syntax: result.meta.position === 'PREFIX' ? 'PREFIX_EQUAL_SIGN' : 'SUFFIX_EQUALS_SIGN'
}
}),
NULLABLE: (result, transform) => ({
type: 'NULLABLE',
value: transform(result.element),
meta: {
syntax: result.meta.position === 'PREFIX' ? 'PREFIX_QUESTION_MARK' : 'SUFFIX_QUESTION_MARK'
}
}),
NOT_NULLABLE: (result, transform) => ({
type: 'NOT_NULLABLE',
value: transform(result.element),
meta: {
syntax: result.meta.position === 'PREFIX' ? 'PREFIX_BANG' : 'SUFFIX_BANG'
}
}),
VARIADIC: (result, transform) => {
const transformed = {
type: 'VARIADIC',
meta: {
syntax: result.meta.position === 'PREFIX' ? 'PREFIX_DOTS'
: result.meta.position === 'SUFFIX' ? 'SUFFIX_DOTS' : 'ONLY_DOTS'
}
};
if (result.element !== undefined) {
transformed.value = transform(result.element);
}
return transformed;
},
NAME: result => ({
type: 'NAME',
name: result.value
}),
TYPE_OF: (result, transform) => ({
type: 'TYPE_QUERY',
name: transform(result.element)
}),
TUPLE: (result, transform) => ({
type: 'TUPLE',
entries: result.elements.map(transform)
}),
KEY_OF: (result, transform) => ({
type: 'KEY_QUERY',
value: transform(result.element)
}),
IMPORT: result => ({
type: 'IMPORT',
path: {
type: 'STRING_VALUE',
quoteStyle: getQuoteStyle(result.element.meta.quote),
string: result.element.value
}
}),
UNDEFINED: () => ({
type: 'NAME',
name: 'undefined'
}),
ANY: () => ({
type: 'ANY'
}),
FUNCTION: (result, transform) => {
const specialParams = extractSpecialParams(result);
const transformed = {
type: result.meta.arrow ? 'ARROW' : 'FUNCTION',
params: specialParams.params.map(param => {
if (param.type === 'KEY_VALUE') {
return {
type: 'FieldType',
key: catharsisTransform(field),
value: undefined
type: 'NAMED_PARAMETER',
name: param.left.value,
typeName: transform(param.right)
};
}
else {
return catharsisTransform(field);
return transform(param);
}
});
break;
case 'UNION':
newObject.type = 'TypeUnion';
newObject.elements = object.elements.map(catharsisTransform);
break;
case 'KEY_VALUE':
newObject.type = 'FieldType';
newObject.key = catharsisTransform(object.key);
if (object.value !== undefined) {
newObject.value = catharsisTransform(object.value);
}),
new: null,
returns: null
};
if (specialParams.this !== undefined) {
transformed.this = transform(specialParams.this);
}
else if (!result.meta.arrow) {
transformed.this = null;
}
if (specialParams.new !== undefined) {
transformed.new = transform(specialParams.new);
}
if (result.returnType !== undefined) {
transformed.returns = transform(result.returnType);
}
return transformed;
},
GENERIC: (result, transform) => {
const transformed = {
type: 'GENERIC',
subject: transform(result.left),
objects: result.elements.map(transform),
meta: {
syntax: result.meta.brackets === '[]' ? 'SQUARE_BRACKET' : result.meta.dot ? 'ANGLE_BRACKET_WITH_DOT' : 'ANGLE_BRACKET'
}
break;
case 'PROPERTY_PATH':
newObject.type = 'NameExpression';
if (object.left.type !== 'NAME') {
throw new Error('Other left types than \'NAME\' are not supported for catharsis compat mode');
};
if (result.meta.brackets === '[]' && result.elements[0].type === 'FUNCTION' && !result.elements[0].meta.parenthesis) {
transformed.objects[0] = {
type: 'NAME',
name: 'function'
};
}
return transformed;
},
KEY_VALUE: (result, transform) => {
if (result.left.type !== 'NAME' && result.left.type !== 'NUMBER') {
throw new Error('In JTP mode only name and number left values are allowed for record entries.');
}
return {
type: 'RECORD_ENTRY',
key: result.left.value.toString(),
quoteStyle: 'none',
value: transform(result.right),
readonly: false
};
},
OBJECT: (result, transform) => {
const entries = [];
for (const field of result.elements) {
if (field.type === 'KEY_VALUE') {
entries.push(transform(field));
}
delete newObject.left;
delete newObject.path;
newObject.name = object.left.name + '.' + object.path.join('.');
break;
case 'SYMBOL':
newObject.type = 'NameExpression';
delete newObject.value;
value = '';
if (((_a = object.value) === null || _a === void 0 ? void 0 : _a.repeatable) === true) {
value = '...';
else {
const key = field.value;
entries.push({
type: 'RECORD_ENTRY',
key: `${key}`,
quoteStyle: 'none',
value: null,
readonly: false
});
}
if (((_b = object.value) === null || _b === void 0 ? void 0 : _b.type) === 'NAME') {
value += object.value.name;
}
else if (((_c = object.value) === null || _c === void 0 ? void 0 : _c.type) === 'NUMBER') {
value += object.value.value.toString(10);
}
newObject.name = `${object.name}(${value})`;
break;
}
return newObject;
}
return {
type: 'RECORD',
entries
};
},
MODULE: result => ({
type: 'MODULE',
value: {
type: 'FILE_PATH',
quoteStyle: getQuoteStyle(result.meta.quote),
path: result.value
}
}),
NAME_PATH: (result, transform) => {
const transformed = {
type: getMemberType(result.meta.type),
owner: transform(result.left),
name: `${result.right.value}`,
quoteStyle: result.right.type === 'STRING_VALUE' ? getQuoteStyle(result.right.meta.quote) : 'none',
hasEventPrefix: false
};
if (transformed.owner.type === 'MODULE') {
const tModule = transformed.owner;
transformed.owner = transformed.owner.value;
tModule.value = transformed;
return tModule;
}
else {
return transformed;
}
},
UNION: (result, transform) => {
let index = result.elements.length;
let rightTransformed = transform(result.elements[--index]);
let left;
do {
left = result.elements[--index];
rightTransformed = {
type: 'UNION',
left: transform(left),
right: rightTransformed
};
} while (index > 0);
return rightTransformed;
},
PARENTHESIS: (result, transform) => ({
type: 'PARENTHESIS',
value: transform(assertTerminal(result.element))
}),
NULL: () => ({
type: 'NAME',
name: 'null'
}),
UNKNOWN: () => ({
type: 'UNKNOWN'
}),
STRING_VALUE: result => ({
type: 'STRING_VALUE',
quoteStyle: getQuoteStyle(result.meta.quote),
string: result.value
}),
NUMBER: notAvailableTransform,
SYMBOL: notAvailableTransform,
PARAMETER_LIST: notAvailableTransform
};
function jtpTransform(result) {
return transform(jtpRules, result);
}

@@ -1324,2 +1855,3 @@

exports.catharsisTransform = catharsisTransform;
exports.jtpTransform = jtpTransform;

@@ -1326,0 +1858,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

@@ -1,4 +0,5 @@

import { KeyValueResult, NameResult, NonTerminalResult, ParseResult } from './ParseResult';
export declare function assertTerminal(result: NonTerminalResult): ParseResult;
import { KeyValueResult, NameResult, NonTerminalResult, NumberResult, ParseResult, VariadicResult } from './ParseResult';
export declare function assertTerminal(result?: NonTerminalResult): ParseResult;
export declare function assertNamedKeyValueOrTerminal(result: NonTerminalResult): KeyValueResult | ParseResult;
export declare function assertNamedKeyValueOrName(result: NonTerminalResult): KeyValueResult | NameResult;
export declare function assertNumberOrVariadicName(result: NonTerminalResult): NumberResult | NameResult | VariadicResult<NameResult>;
/**
* @packageDocumentation
* @package
* This package provides a parser for jsdoc types.

@@ -7,2 +7,3 @@ */

export * from './ParseResult';
export * from './catharsisTransform';
export * from './transforms/catharsisTransform';
export * from './transforms/jtpTransform';

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

export declare type TokenType = '(' | ')' | '[' | ']' | '{' | '}' | '|' | '<' | '>' | ',' | '*' | '?' | '!' | '=' | ':' | '.' | '@' | '#' | '~' | '/' | '...' | 'null' | 'undefined' | 'void' | 'function' | 'this' | 'new' | 'module' | 'typeof' | 'keyof' | 'import' | 'Identifier' | 'StringValue' | 'Number' | 'EOF';
export declare type TokenType = '(' | ')' | '[' | ']' | '{' | '}' | '|' | '<' | '>' | ',' | '*' | '?' | '!' | '=' | ':' | '.' | '@' | '#' | '~' | '/' | '=>' | '...' | 'null' | 'undefined' | 'function' | 'this' | 'new' | 'module' | 'typeof' | 'keyof' | 'import' | 'Identifier' | 'StringValue' | 'Number' | 'EOF';
export interface Token {

@@ -3,0 +3,0 @@ type: TokenType;

@@ -5,3 +5,3 @@ import { Token, TokenType } from './lexer/Token';

import { Grammar } from './grammars/Grammar';
import { Precedence } from './parslets/Precedence';
import { Precedence } from './Precedence';
export declare class ParserEngine {

@@ -8,0 +8,0 @@ private readonly prefixParslets;

@@ -1,89 +0,125 @@

export declare type ParseResult = NameResult | UnionResult | GenericResult | StringValueResult | NullResult | UndefinedResult | AllResult | UnknownResult | FunctionResult | RecordResult | ModuleResult | PropertyPathResult | SymbolResult | TypeOfResult | KeyOfResult | ImportResult | ArrowResult | TupleResult;
export declare type ParseResult = NameResult | UnionResult | GenericResult | StringValueResult | NullResult | UndefinedResult | AnyResult | UnknownResult | FunctionResult | RecordResult | ModuleResult | NamePathResult | SymbolResult | TypeOfResult | KeyOfResult | ImportResult | TupleResult | OptionalResult<ParseResult> | NullableResult<ParseResult> | NotNullableResult<ParseResult> | VariadicResult<ParseResult> | ParenthesisResult;
export declare type NonTerminalResult = ParseResult | KeyValueResult<ParseResult | NameResult | NumberResult> | NumberResult | ParameterList;
export interface ModifiableResult {
optional?: boolean;
nullable?: boolean;
repeatable?: boolean;
export interface OptionalResult<T extends ParseResult> {
type: 'OPTIONAL';
element: T;
meta: {
position: 'PREFIX' | 'SUFFIX';
};
}
export declare type NameResult = ModifiableResult & {
export interface NullableResult<T extends ParseResult> {
type: 'NULLABLE';
element: T;
meta: {
position: 'PREFIX' | 'SUFFIX';
};
}
export interface NotNullableResult<T extends ParseResult> {
type: 'NOT_NULLABLE';
element: T;
meta: {
position: 'PREFIX' | 'SUFFIX';
};
}
export interface VariadicResult<T extends ParseResult> {
type: 'VARIADIC';
element?: T;
meta: {
position: 'PREFIX' | 'SUFFIX' | 'ONLY_DOTS';
squareBrackets: boolean;
};
}
export interface NameResult {
type: 'NAME';
name: string;
reservedWord?: boolean;
};
export declare type UnionResult = ModifiableResult & {
value: string;
meta: {
reservedWord: boolean;
};
}
export interface UnionResult {
type: 'UNION';
elements: ParseResult[];
};
export declare type GenericResult = ModifiableResult & {
}
export interface GenericResult {
type: 'GENERIC';
subject: ParseResult;
objects: ParseResult[];
};
export declare type StringValueResult = ModifiableResult & {
left: ParseResult;
elements: ParseResult[];
meta: {
brackets: '<>' | '[]';
dot: boolean;
};
}
export interface StringValueResult {
type: 'STRING_VALUE';
value: string;
quote: string;
};
export declare type NullResult = ModifiableResult & {
meta: {
quote: '\'' | '"';
};
}
export interface NullResult {
type: 'NULL';
};
export declare type UndefinedResult = ModifiableResult & {
}
export interface UndefinedResult {
type: 'UNDEFINED';
};
export declare type AllResult = ModifiableResult & {
type: 'ALL';
};
export declare type UnknownResult = ModifiableResult & {
}
export interface AnyResult {
type: 'ANY';
}
export interface UnknownResult {
type: 'UNKNOWN';
};
export declare type FunctionResult = ModifiableResult & {
}
export interface FunctionResult {
type: 'FUNCTION';
parameters: Array<ParseResult | KeyValueResult>;
returnType?: ParseResult;
};
export declare type ArrowResult = ModifiableResult & {
type: 'FUNCTION';
parameters: Array<NameResult | KeyValueResult>;
returnType?: ParseResult;
arrow: true;
};
export declare type KeyValueResult<KeyType = NameResult> = ModifiableResult & {
meta: {
arrow: boolean;
parenthesis: boolean;
};
}
export interface KeyValueResult<KeyType = NameResult> {
type: 'KEY_VALUE';
key: KeyType;
value: ParseResult;
};
export declare type RecordResult = ModifiableResult & {
type: 'RECORD';
fields: Array<KeyValueResult<ParseResult | NumberResult> | ParseResult | NumberResult>;
};
export declare type ModuleResult = ModifiableResult & {
left: KeyType;
right: ParseResult;
}
export interface RecordResult {
type: 'OBJECT';
elements: Array<KeyValueResult<ParseResult | NumberResult> | ParseResult | NumberResult>;
}
export interface ModuleResult {
type: 'MODULE';
path: string;
};
export declare type PropertyPathResult = ModifiableResult & {
type: 'PROPERTY_PATH';
value: string;
meta: {
quote: '\'' | '"' | undefined;
};
}
export interface NamePathResult {
type: 'NAME_PATH';
left: ParseResult;
path: string[];
};
export declare type NumberResult = ModifiableResult & {
right: NameResult | NumberResult | StringValueResult;
meta: {
type: '~' | '#' | '.';
};
}
export interface NumberResult {
type: 'NUMBER';
value: number;
};
export declare type SymbolResult = ModifiableResult & {
}
export interface SymbolResult {
type: 'SYMBOL';
name: string;
value?: NumberResult | NameResult;
};
export declare type TypeOfResult = ModifiableResult & {
value: string;
element?: NumberResult | NameResult | VariadicResult<NameResult>;
}
export interface TypeOfResult {
type: 'TYPE_OF';
value?: ParseResult;
};
export declare type KeyOfResult = ModifiableResult & {
element: ParseResult;
}
export interface KeyOfResult {
type: 'KEY_OF';
value?: ParseResult;
};
export declare type ImportResult = ModifiableResult & {
element: ParseResult;
}
export interface ImportResult {
type: 'IMPORT';
path: StringValueResult;
};
element: StringValueResult;
}
export interface ParameterList {

@@ -93,5 +129,9 @@ type: 'PARAMETER_LIST';

}
export declare type TupleResult = ModifiableResult & {
export interface TupleResult {
type: 'TUPLE';
elements: ParseResult[];
};
}
export interface ParenthesisResult {
type: 'PARENTHESIS';
element: NonTerminalResult | undefined;
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet';

import { NonTerminalResult, ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class ArrayBracketsParslet implements InfixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType, next: TokenType): boolean;

import { InfixParslet, PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { ParserEngine } from '../ParserEngine';
import { ArrowResult, NonTerminalResult } from '../ParseResult';
import { FunctionResult, NonTerminalResult } from '../ParseResult';
import { BaseFunctionParslet } from './BaseFunctionParslet';

@@ -10,3 +10,3 @@ export declare class ArrowFunctionWithoutParametersParslet implements PrefixParslet {

getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ArrowResult;
parsePrefix(parser: ParserEngine): FunctionResult;
}

@@ -16,3 +16,3 @@ export declare class ArrowFunctionWithParametersParslet extends BaseFunctionParslet implements InfixParslet {

getPrecedence(): Precedence;
parseInfix(parser: ParserEngine, left: NonTerminalResult): ArrowResult;
parseInfix(parser: ParserEngine, left: NonTerminalResult): FunctionResult;
}

@@ -1,6 +0,6 @@

import { KeyValueResult, NonTerminalResult, ParseResult } from '../ParseResult';
import { KeyValueResult, ParenthesisResult, ParseResult } from '../ParseResult';
export declare class BaseFunctionParslet {
protected getParameters(value: NonTerminalResult): Array<ParseResult | KeyValueResult>;
protected getNamedParameters(value: NonTerminalResult): KeyValueResult[];
protected getUnnamedParameters(value: NonTerminalResult): ParseResult[];
protected getParameters(value: ParenthesisResult): Array<ParseResult | KeyValueResult>;
protected getNamedParameters(value: ParenthesisResult): KeyValueResult[];
protected getUnnamedParameters(value: ParenthesisResult): ParseResult[];
}

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet';

import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { BaseFunctionParslet } from './BaseFunctionParslet';

@@ -8,0 +8,0 @@ export interface FunctionParsletOptions {

@@ -5,3 +5,3 @@ import { ParserEngine } from '../ParserEngine';

import { InfixParslet } from './Parslet';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class GenericParslet implements InfixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType, next: TokenType): boolean;

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet';

import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class ImportParslet implements PrefixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType, next: TokenType): boolean;

import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { KeyOfResult } from '../ParseResult';
import { Precedence } from '../Precedence';
export declare class KeyOfParslet implements PrefixParslet {
accepts(type: TokenType, next: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parsePrefix(parser: ParserEngine): KeyOfResult;
}
import { InfixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { ParserEngine } from '../ParserEngine';
import { NonTerminalResult } from '../ParseResult';
interface KeyValueParsletOptions {
allowOnlyNameOrNumberProperties: boolean;
}
export declare class KeyValueParslet implements InfixParslet {
private readonly allowOnlyNameOrNumberProperties;
constructor(opts: KeyValueParsletOptions);
accepts(type: TokenType, next: TokenType): boolean;

@@ -11,1 +16,2 @@ getPrecedence(): Precedence;

}
export {};
import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { ParserEngine } from '../ParserEngine';

@@ -5,0 +5,0 @@ import { ParseResult } from '../ParseResult';

import { ParserEngine } from '../ParserEngine';
import { TokenType } from '../lexer/Token';
import { ParseResult } from '../ParseResult';
import { NameResult } from '../ParseResult';
import { PrefixParslet } from './Parslet';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
interface NameParsletOptions {
allowedAdditionalTokens: TokenType[];
}
export declare class NameParslet implements PrefixParslet {
private readonly allowedAdditionalTokens;
constructor(options: NameParsletOptions);
accepts(type: TokenType, next: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parsePrefix(parser: ParserEngine): NameResult;
}
export {};

@@ -5,3 +5,3 @@ import { InfixParslet, PrefixParslet } from './Parslet';

import { NonTerminalResult, ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class NullablePrefixParslet implements PrefixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType, next: TokenType): boolean;

import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { ParserEngine } from '../ParserEngine';

@@ -5,0 +5,0 @@ import { NonTerminalResult } from '../ParseResult';

@@ -1,10 +0,11 @@

import { InfixParslet } from './Parslet';
import { InfixParslet, PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { NonTerminalResult, ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
export declare class OptionalParslet implements InfixParslet {
import { Precedence } from '../Precedence';
export declare class OptionalParslet implements PrefixParslet, InfixParslet {
accepts(type: TokenType, next: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parseInfix(parser: ParserEngine, left: NonTerminalResult): ParseResult;
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet';

import { NonTerminalResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
interface ParameterListParsletOptions {

@@ -8,0 +8,0 @@ allowTrailingComma: boolean;

import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
import { ParserEngine } from '../ParserEngine';
import { NonTerminalResult } from '../ParseResult';
import { ParenthesisResult } from '../ParseResult';
export declare class ParenthesisParslet implements PrefixParslet {
accepts(type: TokenType, next: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): NonTerminalResult;
parsePrefix(parser: ParserEngine): ParenthesisResult;
}
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { NonTerminalResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export interface Parslet {

@@ -6,0 +6,0 @@ accepts: (type: TokenType, next: TokenType) => boolean;

@@ -8,14 +8,15 @@ export declare enum Precedence {

POSTFIX = 5,
RECORD = 6,
SYMBOL = 7,
OPTIONAL = 8,
NULLABLE = 9,
FUNCTION = 10,
ARROW = 11,
KEY_VALUE = 12,
GENERIC = 13,
PROPERTY_PATH = 14,
KEY_OF_TYPE_OF = 15,
ARRAY_BRACKETS = 16,
SPECIAL_TYPES = 17
TUPLE = 6,
OBJECT = 7,
SYMBOL = 8,
OPTIONAL = 9,
NULLABLE = 10,
FUNCTION = 11,
ARROW = 12,
KEY_VALUE = 13,
GENERIC = 14,
NAME_PATH = 15,
KEY_OF_TYPE_OF = 16,
ARRAY_BRACKETS = 17,
SPECIAL_TYPES = 18
}

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet';

import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class RecordParslet implements PrefixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType): boolean;

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet';

import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class SpecialTypesParslet implements PrefixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType, next: TokenType): boolean;

import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { StringValueResult } from '../ParseResult';
import { Precedence } from '../Precedence';
export declare class StringValueParslet implements PrefixParslet {
accepts(type: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parsePrefix(parser: ParserEngine): StringValueResult;
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet';

import { NonTerminalResult, ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class SymbolParslet implements InfixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType): boolean;

import { PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { TypeOfResult } from '../ParseResult';
import { Precedence } from '../Precedence';
export declare class TypeOfParslet implements PrefixParslet {
accepts(type: TokenType, next: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parsePrefix(parser: ParserEngine): TypeOfResult;
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet';

import { TokenType } from '../lexer/Token';
import { Precedence } from './Precedence';
import { Precedence } from '../Precedence';
export declare class UnenclosedUnionParslet implements InfixParslet {

@@ -8,0 +8,0 @@ accepts(type: TokenType): boolean;

import { InfixParslet, PrefixParslet } from './Parslet';
import { TokenType } from '../lexer/Token';
import { ParserEngine } from '../ParserEngine';
import { NonTerminalResult, ParseResult } from '../ParseResult';
import { Precedence } from './Precedence';
import { NonTerminalResult, ParseResult, VariadicResult } from '../ParseResult';
import { Precedence } from '../Precedence';
interface VariadicParsletOptions {
allowEnclosingBrackets: boolean;
}
export declare class VariadicParslet implements PrefixParslet, InfixParslet {
private readonly allowEnclosingBrackets;
constructor(opts: VariadicParsletOptions);
accepts(type: TokenType): boolean;
getPrecedence(): Precedence;
parsePrefix(parser: ParserEngine): ParseResult;
parsePrefix(parser: ParserEngine): VariadicResult<ParseResult>;
parseInfix(parser: ParserEngine, left: NonTerminalResult): ParseResult;
}
export {};

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

import { DiffFixture } from '../Fixture';
export declare const miscDiffs: DiffFixture[];
import { Fixture } from '../Fixture';
export declare const miscDiffs: Fixture[];
import { ParseResult, ParserMode } from '../../src';
import 'mocha';
declare type JtpMode = 'jsdoc' | 'closure' | 'typescript' | 'permissive';
declare type CatharsisMode = 'jsdoc' | 'closure';
declare type CompareMode = ParserMode | 'fail' | 'differ';
export interface Fixture {
description: string;
input: string;
modes: ParserMode[];
jtp: {
[K in JtpMode]: CompareMode;
};
catharsis: {
[K in CatharsisMode]: CompareMode;
};
expected?: ParseResult;
shouldFail?: true;
}
export interface DiffFixture {
description: string;
modes: {
[K in ParserMode]: boolean;
diffExpected?: {
[K in ParserMode]?: ParseResult;
};
input: string;
}
export declare function testFixture(fixture: Fixture): void;
export {};
{
"name": "jsdoc-type-pratt-parser",
"version": "1.0.0-alpha.2",
"version": "1.0.0-alpha.3.0",
"description": "",

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

"test": "npm run typecheck && npm run lint && npm run test:spec",
"test:spec": "mocha -r ts-node/register test/**/*.spec.ts",
"test:spec": "mocha",
"lint": "ts-standard",

@@ -22,5 +22,3 @@ "typecheck": "tsc --noEmit",

"license": "MIT",
"dependencies": {
"typescript": "^4.1.3"
},
"dependencies": {},
"devDependencies": {

@@ -31,3 +29,6 @@ "@rollup/plugin-typescript": "^8.1.1",

"@types/mocha": "^8.2.0",
"@types/node": "^14.14.31",
"catharsis": "^0.9.0",
"chai": "^4.3.0",
"jsdoctypeparser": "^9.0.0",
"lodash": "^4.17.20",

@@ -38,3 +39,4 @@ "mocha": "^8.2.1",

"ts-standard": "^10.0.0",
"typedoc": "^0.20.24"
"typedoc": "^0.20.24",
"typescript": "^4.1.3"
},

@@ -41,0 +43,0 @@ "ts-standard": {

@@ -1,3 +0,11 @@

This project is parser for jsdoc types. It is heavily inspired by http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/, https://github.com/hegemonic/catharsis and https://github.com/jsdoctypeparser/jsdoctypeparser.
[![Test Status](https://github.com/simonseyock/jsdoc-type-pratt-parser/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/simonseyock/jsdoc-type-pratt-parser/actions?query=branch%3Amain)
[![Npm Package](https://badgen.net/npm/v/jsdoc-type-pratt-parser)](https://www.npmjs.com/package/jsdoc-type-pratt-parser)
[![Code Style](https://badgen.net/badge/code%20style/ts-standard/blue?icon=typescript)](https://github.com/standard/ts-standard)
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, but is written as a pratt parser.
* http://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/
* https://github.com/hegemonic/catharsis
* https://github.com/jsdoctypeparser/jsdoctypeparser
Live Demo

@@ -11,7 +19,10 @@ ---------

The usage is not perfect for now as it is not published as a package for now. Dependening on your needs you might want to run `npm run build` before using. An `index.js` in umd format will be built. All exports from `index.ts` should be available.
```
import { Parser } from 'src/index'
npm install jsdoc-type-pratt-parser
```
```js
import { Parser } from 'jsdoc-type-pratt-parser'
const parser = new Parser({

@@ -24,6 +35,11 @@ mode: 'closure'

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:
```
import { Parser, catharsisTransform } from 'src/index'
```js
import { Parser, catharsisTransform } from 'jsdoc-type-pratt-parser'

@@ -37,6 +53,18 @@ const parser = new Parser({

Jsdoctypeparser compat mode:
```js
import { Parser, jtpTransform } from 'jsdoc-type-pratt-parser'
const parser = new Parser({
mode: 'closure'
})
const result = jtpTransform(parser.parse('myType.<string>'))
```
Available Grammars
------------------
At the moment there are 3 modes supported: 'jsdoc', 'closure', 'typescipt' where 'jsdoc' is a superset of 'closure'
At the moment there are 3 modes supported: 'jsdoc', 'closure' and 'typescipt'

@@ -46,10 +74,11 @@ Tests Status

This parser runs most tests of https://github.com/hegemonic/catharsis and also some of the typescript tests of https://github.com/jsdoctypeparser/jsdoctypeparser
This parser runs most tests of https://github.com/hegemonic/catharsis and of
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.
It adds an increasing number of tests on its own, especially the tests to assure the differences between the modes.
The current status can be checked in the github action results: https://github.com/simonseyock/jsdoc-type-pratt-parser/actions
API Documentation
-----------------
A simple api doc can be found here: https://simonseyock.github.io/jsdoc-type-pratt-parser/docs/
An API documentation can be found here: https://simonseyock.github.io/jsdoc-type-pratt-parser/docs/modules.html

@@ -1,12 +0,8 @@

import { KeyValueResult, NameResult, NonTerminalResult, ParseResult } from './ParseResult'
import { KeyValueResult, NameResult, NonTerminalResult, NumberResult, ParseResult, VariadicResult } from './ParseResult'
import { UnexpectedTypeError } from './errors'
class UnexpectedTypeError extends Error {
constructor (result: NonTerminalResult) {
super(`Unexpected type: '${result.type}'`)
Object.setPrototypeOf(this, UnexpectedTypeError.prototype)
export function assertTerminal (result?: NonTerminalResult): ParseResult {
if (result === undefined) {
throw new Error('Unexpected undefined')
}
}
export function assertTerminal (result: NonTerminalResult): ParseResult {
if (result.type === 'KEY_VALUE' || result.type === 'NUMBER' || result.type === 'PARAMETER_LIST') {

@@ -20,3 +16,3 @@ throw new UnexpectedTypeError(result)

if (result.type === 'KEY_VALUE') {
if (result.key.type !== 'NAME') {
if (result.left.type !== 'NAME') {
throw new UnexpectedTypeError(result)

@@ -31,3 +27,3 @@ }

if (result.type === 'KEY_VALUE') {
if (result.key.type !== 'NAME') {
if (result.left.type !== 'NAME') {
throw new UnexpectedTypeError(result)

@@ -41,1 +37,14 @@ }

}
export function assertNumberOrVariadicName (result: NonTerminalResult): NumberResult | NameResult | VariadicResult<NameResult> {
if (result.type === 'VARIADIC') {
if (result.element?.type === 'NAME') {
return result as VariadicResult<NameResult>
}
throw new UnexpectedTypeError(result)
}
if (result.type !== 'NUMBER' && result.type !== 'NAME') {
throw new UnexpectedTypeError(result)
}
return result
}
import { Grammar } from './Grammar'
import { UnenclosedUnionParslet } from '../parslets/UnionParslets'
import { NameParslet } from '../parslets/NameParslet'
import { SpecialTypesParslet } from '../parslets/SpecialTypesParslet'
import { VariadicParslet } from '../parslets/VariadicParslet'
import { RecordParslet } from '../parslets/RecordParslet'
import { ModuleParslet } from '../parslets/ModuleParslet'
import { GenericParslet } from '../parslets/GenericParslet'
import { OptionalParslet } from '../parslets/OptionalParslet'
import { PropertyPathParslet } from '../parslets/PropertyPathParslet'
import { ParenthesisParslet } from '../parslets/ParenthesisParslet'
import { KeyValueParslet } from '../parslets/KeyValueParslet'
import { NumberParslet } from '../parslets/NumberParslet'
import { ParameterListParslet } from '../parslets/ParameterListParslet'
import { NullableInfixParslet, NullablePrefixParslet } from '../parslets/NullableParslets'
import { OptionalParslet } from '../parslets/OptionalParslet'

@@ -20,24 +15,19 @@ export const baseGrammar: Grammar = () => {

prefixParslets: [
new NameParslet(),
new SpecialTypesParslet(),
new NullablePrefixParslet(),
new VariadicParslet(),
new OptionalParslet(),
new RecordParslet(),
new ModuleParslet(),
new NumberParslet(),
new ParenthesisParslet()
new ParenthesisParslet(),
new SpecialTypesParslet()
],
infixParslets: [
new ParameterListParslet({
allowTrailingComma: false
allowTrailingComma: true
}),
new PropertyPathParslet(),
new KeyValueParslet(),
new GenericParslet(),
new UnenclosedUnionParslet(),
new OptionalParslet(),
new NullableInfixParslet(),
new PropertyPathParslet()
new NullableInfixParslet()
]
}
}
import { Grammar } from './Grammar'
import { baseGrammar } from './baseGrammar'
import { FunctionParslet } from '../parslets/FunctionParslet'
import { NamePathParslet } from '../parslets/NamePathParslet'
import { KeyValueParslet } from '../parslets/KeyValueParslet'
import { TypeOfParslet } from '../parslets/TypeOfParslet'
import { VariadicParslet } from '../parslets/VariadicParslet'
import { NameParslet } from '../parslets/NameParslet'
import { NotNullableParslet } from '../parslets/NotNullableParslet'

@@ -14,2 +20,6 @@ export const closureGrammar: Grammar = () => {

...prefixParslets,
new NameParslet({
allowedAdditionalTokens: []
}),
new TypeOfParslet(),
new FunctionParslet({

@@ -19,6 +29,22 @@ allowWithoutParenthesis: false,

allowNoReturnType: true
})
}),
new VariadicParslet({
allowEnclosingBrackets: false
}),
new NameParslet({
allowedAdditionalTokens: ['keyof']
}),
new NotNullableParslet()
],
infixParslets
infixParslets: [
...infixParslets,
new NamePathParslet({
allowJsdocNamePaths: false
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: true
}),
new NotNullableParslet()
]
}
}
import { Grammar } from './Grammar'
import { SymbolParslet } from '../parslets/SymbolParslet'
import { ClassPathParslet } from '../parslets/ClassPathParslet'
import { ArrayBracketsParslet } from '../parslets/ArrayBracketsParslet'

@@ -8,2 +7,8 @@ import { StringValueParslet } from '../parslets/StringValueParslet'

import { baseGrammar } from './baseGrammar'
import { NamePathParslet } from '../parslets/NamePathParslet'
import { KeyValueParslet } from '../parslets/KeyValueParslet'
import { VariadicParslet } from '../parslets/VariadicParslet'
import { ModuleParslet } from '../parslets/ModuleParslet'
import { NameParslet } from '../parslets/NameParslet'
import { NotNullableParslet } from '../parslets/NotNullableParslet'

@@ -24,3 +29,11 @@ export const jsdocGrammar: Grammar = () => {

}),
new StringValueParslet()
new StringValueParslet(),
new ModuleParslet(),
new VariadicParslet({
allowEnclosingBrackets: true
}),
new NameParslet({
allowedAdditionalTokens: ['keyof']
}),
new NotNullableParslet()
],

@@ -30,6 +43,15 @@ infixParslets: [

new SymbolParslet(),
new ClassPathParslet(),
new ArrayBracketsParslet()
new ArrayBracketsParslet(),
new NamePathParslet({
allowJsdocNamePaths: true
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: false
}),
new VariadicParslet({
allowEnclosingBrackets: true
}),
new NotNullableParslet()
]
}
}

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

import { TupleParslet } from '../parslets/TupleParslet'
import { Grammar } from './Grammar'

@@ -9,3 +10,2 @@ import { ArrayBracketsParslet } from '../parslets/ArrayBracketsParslet'

import { FunctionParslet } from '../parslets/FunctionParslet'
import { VariadicParslet } from '../parslets/VariadicParslet'
import {

@@ -15,2 +15,6 @@ ArrowFunctionWithoutParametersParslet,

} from '../parslets/ArrowFunctionParslet'
import { NamePathParslet } from '../parslets/NamePathParslet'
import { KeyValueParslet } from '../parslets/KeyValueParslet'
import { VariadicParslet } from '../parslets/VariadicParslet'
import { NameParslet } from '../parslets/NameParslet'

@@ -23,2 +27,7 @@ export const typescriptGrammar: Grammar = () => {

// typescript does not support explicit non nullability
// https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#patterns-that-are-known-not-to-be-supported
// module seems not to be supported
return {

@@ -33,4 +42,14 @@ prefixParslets: [

new FunctionParslet({
allowWithoutParenthesis: true,
allowNoReturnType: false
allowWithoutParenthesis: false,
allowNoReturnType: false,
allowNamedParameters: ['this', 'new']
}),
new TupleParslet({
allowQuestionMark: false
}),
new VariadicParslet({
allowEnclosingBrackets: false
}),
new NameParslet({
allowedAdditionalTokens: []
})

@@ -41,6 +60,11 @@ ],

new ArrayBracketsParslet(),
new VariadicParslet(),
new ArrowFunctionWithParametersParslet()
new ArrowFunctionWithParametersParslet(),
new NamePathParslet({
allowJsdocNamePaths: false
}),
new KeyValueParslet({
allowOnlyNameOrNumberProperties: true
})
]
}
}
/**
* @packageDocumentation
* @package
* This package provides a parser for jsdoc types.

@@ -8,2 +8,3 @@ */

export * from './ParseResult'
export * from './catharsisTransform'
export * from './transforms/catharsisTransform'
export * from './transforms/jtpTransform'

@@ -142,2 +142,3 @@ import { Token, TokenType } from './Token'

eofRule,
makePunctuationRule('=>'),
makePunctuationRule('('),

@@ -166,3 +167,2 @@ makePunctuationRule(')'),

makeKeyWordRule('null'),
makeKeyWordRule('void'),
makeKeyWordRule('function'),

@@ -223,6 +223,7 @@ makeKeyWordRule('this'),

private read (): Token {
const text = this.text.trim()
for (const rule of rules) {
const token = rule(this.text)
const token = rule(text)
if (token !== null) {
this.text = this.text.slice(token.text.length).trim()
this.text = text.slice(token.text.length)
return token

@@ -229,0 +230,0 @@ }

export type TokenType =
'(' |
')' |
'[' |
']' |
'{' |
'}' |
'|' |
'<' |
'>' |
',' |
'*' |
'?' |
'!' |
'=' |
':' |
'.' |
'@' |
'#' |
'~' |
'/' |
'...' |
'null' |
'undefined' |
'void' |
'function' |
'this' |
'new' |
'module' |
'typeof' |
'keyof' |
'import' |
'Identifier' |
'StringValue' |
'Number' |
'EOF'
'('
| ')'
| '['
| ']'
| '{'
| '}'
| '|'
| '<'
| '>'
| ','
| '*'
| '?'
| '!'
| '='
| ':'
| '.'
| '@'
| '#'
| '~'
| '/'
| '=>'
| '...'
| 'null'
| 'undefined'
| 'function'
| 'this'
| 'new'
| 'module'
| 'typeof'
| 'keyof'
| 'import'
| 'Identifier'
| 'StringValue'
| 'Number'
| 'EOF'

@@ -38,0 +38,0 @@ export interface Token {

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

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

@@ -7,12 +8,4 @@ import { Lexer } from './lexer/Lexer'

import { assertTerminal } from './assertTypes'
import { Precedence } from './parslets/Precedence'
import { Precedence } from './Precedence'
class NoParsletFoundError extends Error {
constructor (token: Token) {
super(`No parslet found for token: '${token.type}' with value '${token.text}'`)
Object.setPrototypeOf(this, NoParsletFoundError.prototype)
}
}
export class ParserEngine {

@@ -41,3 +34,3 @@ private readonly prefixParslets: PrefixParslet[]

if (!this.consume('EOF')) {
throw new Error(`Unexpected early end of parse. Next token: '${this.getToken().text}'`)
throw new EarlyEndOfParseError(this.getToken())
}

@@ -60,3 +53,3 @@ return result

try {
return this.parseType(precedence)
return this.parseNonTerminalType(precedence)
} catch (e) {

@@ -63,0 +56,0 @@ if (e instanceof NoParsletFoundError) {

@@ -8,3 +8,3 @@ export type ParseResult =

| UndefinedResult
| AllResult
| AnyResult
| UnknownResult

@@ -14,3 +14,3 @@ | FunctionResult

| ModuleResult
| PropertyPathResult
| NamePathResult
| SymbolResult

@@ -20,4 +20,8 @@ | TypeOfResult

| ImportResult
| ArrowResult
| TupleResult
| OptionalResult<ParseResult>
| NullableResult<ParseResult>
| NotNullableResult<ParseResult>
| VariadicResult<ParseResult>
| ParenthesisResult

@@ -30,15 +34,44 @@ export type NonTerminalResult =

export interface ModifiableResult {
optional?: boolean
nullable?: boolean
repeatable?: boolean
export interface OptionalResult<T extends ParseResult> {
type: 'OPTIONAL'
element: T
meta: {
position: 'PREFIX' | 'SUFFIX'
}
}
export type NameResult = ModifiableResult & {
export interface NullableResult<T extends ParseResult> {
type: 'NULLABLE'
element: T
meta: {
position: 'PREFIX' | 'SUFFIX'
}
}
export interface NotNullableResult<T extends ParseResult> {
type: 'NOT_NULLABLE'
element: T
meta: {
position: 'PREFIX' | 'SUFFIX'
}
}
export interface VariadicResult<T extends ParseResult> {
type: 'VARIADIC'
element?: T
meta: {
position: 'PREFIX' | 'SUFFIX' | 'ONLY_DOTS'
squareBrackets: boolean
}
}
export interface NameResult {
type: 'NAME'
name: string
reservedWord?: boolean
value: string
meta: {
reservedWord: boolean
}
}
export type UnionResult = ModifiableResult & {
export interface UnionResult {
type: 'UNION'

@@ -48,66 +81,75 @@ elements: ParseResult[]

export type GenericResult = ModifiableResult & {
export interface GenericResult {
type: 'GENERIC'
subject: ParseResult
objects: ParseResult[]
left: ParseResult
elements: ParseResult[]
meta: {
brackets: '<>' | '[]'
dot: boolean
}
}
export type StringValueResult = ModifiableResult & {
export interface StringValueResult {
type: 'STRING_VALUE'
value: string
quote: string
meta: {
quote: '\'' | '"'
}
}
export type NullResult = ModifiableResult & {
export interface NullResult {
type: 'NULL'
}
export type UndefinedResult = ModifiableResult & {
export interface UndefinedResult {
type: 'UNDEFINED'
}
export type AllResult = ModifiableResult & {
type: 'ALL'
export interface AnyResult {
type: 'ANY'
}
export type UnknownResult = ModifiableResult & {
export interface UnknownResult {
type: 'UNKNOWN'
}
export type FunctionResult = ModifiableResult & {
export interface FunctionResult {
type: 'FUNCTION'
parameters: Array<ParseResult | KeyValueResult>
returnType?: ParseResult
meta: {
arrow: boolean
parenthesis: boolean
}
}
export type ArrowResult = ModifiableResult & {
type: 'FUNCTION'
parameters: Array<NameResult | KeyValueResult>
returnType?: ParseResult
arrow: true
}
export type KeyValueResult<KeyType = NameResult> = ModifiableResult & {
export interface KeyValueResult<KeyType = NameResult> {
type: 'KEY_VALUE'
key: KeyType
value: ParseResult
left: KeyType
right: ParseResult
}
export type RecordResult = ModifiableResult & {
type: 'RECORD'
fields: Array<KeyValueResult<ParseResult | NumberResult> | ParseResult | NumberResult>
export interface RecordResult {
type: 'OBJECT'
elements: Array<KeyValueResult<ParseResult | NumberResult> | ParseResult | NumberResult>
}
export type ModuleResult = ModifiableResult & {
export interface ModuleResult {
type: 'MODULE'
path: string
value: string
meta: {
quote: '\'' | '"' | undefined
}
}
export type PropertyPathResult = ModifiableResult & {
type: 'PROPERTY_PATH'
export interface NamePathResult {
type: 'NAME_PATH'
left: ParseResult
path: string[]
right: NameResult | NumberResult | StringValueResult
meta: {
type: '~' | '#' | '.'
}
}
export type NumberResult = ModifiableResult & {
export interface NumberResult {
type: 'NUMBER'

@@ -117,21 +159,21 @@ value: number

export type SymbolResult = ModifiableResult & {
export interface SymbolResult {
type: 'SYMBOL'
name: string
value?: NumberResult | NameResult
value: string
element?: NumberResult | NameResult | VariadicResult<NameResult>
}
export type TypeOfResult = ModifiableResult & {
export interface TypeOfResult {
type: 'TYPE_OF'
value?: ParseResult
element: ParseResult
}
export type KeyOfResult = ModifiableResult & {
export interface KeyOfResult {
type: 'KEY_OF'
value?: ParseResult
element: ParseResult
}
export type ImportResult = ModifiableResult & {
export interface ImportResult {
type: 'IMPORT'
path: StringValueResult
element: StringValueResult
}

@@ -144,5 +186,10 @@

export type TupleResult = ModifiableResult & {
export interface TupleResult {
type: 'TUPLE'
elements: ParseResult[]
}
export interface ParenthesisResult {
type: 'PARENTHESIS'
element: NonTerminalResult | undefined
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet'

import { NonTerminalResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'

@@ -23,11 +23,18 @@

type: 'GENERIC',
subject: {
left: {
type: 'NAME',
name: 'Array'
value: 'Array',
meta: {
reservedWord: false
}
},
objects: [
elements: [
assertTerminal(left)
]
],
meta: {
brackets: '[]',
dot: false
}
}
}
}
import { InfixParslet, PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { ParserEngine } from '../ParserEngine'
import { ArrowResult, NonTerminalResult } from '../ParseResult'
import { FunctionResult, NonTerminalResult } from '../ParseResult'
import { BaseFunctionParslet } from './BaseFunctionParslet'
import { assertNamedKeyValueOrName } from '../assertTypes'
import { UnexpectedTypeError } from '../errors'

@@ -18,18 +19,18 @@ export class ArrowFunctionWithoutParametersParslet implements PrefixParslet {

parsePrefix (parser: ParserEngine): ArrowResult {
parser.consume('(')
parsePrefix (parser: ParserEngine): FunctionResult {
const hasParenthesis = parser.consume('(')
parser.consume(')')
if (!parser.consume('=') || !parser.consume('>')) {
if (!parser.consume('=>')) {
throw new Error('Unexpected empty parenthesis. Expected \'=>\' afterwards.')
}
const result: ArrowResult = {
return {
type: 'FUNCTION',
parameters: [],
arrow: true
meta: {
arrow: true,
parenthesis: hasParenthesis
},
returnType: parser.parseType(Precedence.ALL)
}
if (!parser.consume('void')) {
const right = parser.parseType(Precedence.ALL)
result.returnType = right
}
return result
}

@@ -40,3 +41,3 @@ }

accepts (type: TokenType, next: TokenType): boolean {
return type === '=' && next === '>'
return type === '=>'
}

@@ -48,19 +49,18 @@

parseInfix (parser: ParserEngine, left: NonTerminalResult): ArrowResult {
if (parser.previousToken()?.type !== ')') {
throw new Error('Unexpected Arrow. Expected parenthesis before.')
parseInfix (parser: ParserEngine, left: NonTerminalResult): FunctionResult {
if (left.type !== 'PARENTHESIS') {
throw new UnexpectedTypeError(left)
}
parser.consume('=')
parser.consume('>')
const result: ArrowResult = {
parser.consume('=>')
return {
type: 'FUNCTION',
parameters: this.getParameters(left).map(assertNamedKeyValueOrName),
arrow: true
meta: {
arrow: true,
parenthesis: true
},
returnType: parser.parseType(Precedence.ALL)
}
if (!parser.consume('void')) {
const right = parser.parseType(Precedence.ALL)
result.returnType = right
}
return result
}
}

@@ -1,16 +0,18 @@

import { KeyValueResult, NonTerminalResult, ParseResult } from '../ParseResult'
import { KeyValueResult, NonTerminalResult, ParenthesisResult, ParseResult } from '../ParseResult'
import { assertNamedKeyValueOrTerminal } from '../assertTypes'
export class BaseFunctionParslet {
protected getParameters (value: NonTerminalResult): Array<ParseResult | KeyValueResult> {
let parameters: Array<ParseResult | KeyValueResult>
if (value.type === 'PARAMETER_LIST') {
parameters = value.elements
protected getParameters (value: ParenthesisResult): Array<ParseResult | KeyValueResult> {
let parameters: NonTerminalResult[]
if (value.element === undefined) {
parameters = []
} else if (value.element.type === 'PARAMETER_LIST') {
parameters = value.element.elements
} else {
parameters = [assertNamedKeyValueOrTerminal(value)]
parameters = [value.element]
}
return parameters
return parameters.map(p => assertNamedKeyValueOrTerminal(p))
}
protected getNamedParameters (value: NonTerminalResult): KeyValueResult[] {
protected getNamedParameters (value: ParenthesisResult): KeyValueResult[] {
const parameters = this.getParameters(value)

@@ -23,3 +25,3 @@ if (parameters.some(p => p.type !== 'KEY_VALUE')) {

protected getUnnamedParameters (value: NonTerminalResult): ParseResult[] {
protected getUnnamedParameters (value: ParenthesisResult): ParseResult[] {
const parameters = this.getParameters(value)

@@ -26,0 +28,0 @@ if (parameters.some(p => p.type === 'KEY_VALUE')) {

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

import { FunctionResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { BaseFunctionParslet } from './BaseFunctionParslet'
import { UnexpectedTypeError } from '../errors'

@@ -38,3 +39,3 @@ export interface FunctionParsletOptions {

const hasParenthesis = parser.consume('(')
const hasParenthesis = parser.getToken().type === '('

@@ -46,28 +47,29 @@ if (!this.allowWithoutParenthesis && !hasParenthesis) {

type: 'FUNCTION',
parameters: []
parameters: [],
meta: {
arrow: false,
parenthesis: hasParenthesis
}
}
if (hasParenthesis) {
if (!parser.consume(')')) {
const value = parser.parseNonTerminalType(Precedence.ALL)
if (this.allowNamedParameters === undefined) {
result.parameters = this.getUnnamedParameters(value)
} else {
result.parameters = this.getParameters(value)
for (const p of result.parameters) {
if (p.type === 'KEY_VALUE' && !this.allowNamedParameters.includes(p.key.name)) {
throw new Error(`only allowed named parameters are ${this.allowNamedParameters.join(',')} but got ${p.type}`)
}
const value = parser.parseNonTerminalType(Precedence.FUNCTION)
if (value.type !== 'PARENTHESIS') {
throw new UnexpectedTypeError(value)
}
if (this.allowNamedParameters === undefined) {
result.parameters = this.getUnnamedParameters(value)
} else {
result.parameters = this.getParameters(value)
for (const p of result.parameters) {
if (p.type === 'KEY_VALUE' && !this.allowNamedParameters.includes(p.left.value)) {
throw new Error(`only allowed named parameters are ${this.allowNamedParameters.join(',')} but got ${p.type}`)
}
}
if (!parser.consume(')')) {
throw new Error('function parameter list is not terminated')
}
}
if (parser.consume(':')) {
if (!parser.consume('void')) {
result.returnType = parser.parseType(Precedence.PREFIX)
}
result.returnType = parser.parseType(Precedence.PREFIX)
} else {

@@ -74,0 +76,0 @@ if (!this.allowNoReturnType) {

@@ -5,3 +5,3 @@ import { ParserEngine } from '../ParserEngine'

import { InfixParslet } from './Parslet'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'

@@ -19,3 +19,3 @@

parseInfix (parser: ParserEngine, left: NonTerminalResult): ParseResult {
parser.consume('.')
const dot = parser.consume('.')
parser.consume('<')

@@ -34,6 +34,10 @@

type: 'GENERIC',
subject: assertTerminal(left),
objects
left: assertTerminal(left),
elements: objects,
meta: {
brackets: '<>',
dot
}
}
}
}

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet'

import { ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'

@@ -31,5 +31,5 @@ export class ImportParslet implements PrefixParslet {

type: 'IMPORT',
path
element: path
}
}
}
import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { KeyOfResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { KeyOfResult } from '../ParseResult'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'

@@ -17,13 +17,9 @@

parsePrefix (parser: ParserEngine): ParseResult {
parsePrefix (parser: ParserEngine): KeyOfResult {
parser.consume('keyof')
const result: KeyOfResult = {
type: 'KEY_OF'
return {
type: 'KEY_OF',
element: assertTerminal(parser.parseType(Precedence.KEY_OF_TYPE_OF))
}
const value = parser.tryParseType(Precedence.KEY_OF_TYPE_OF)
if (value !== undefined) {
result.value = assertTerminal(value)
}
return result
}
}
import { InfixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { ParserEngine } from '../ParserEngine'
import { NonTerminalResult } from '../ParseResult'
import { assertTerminal } from '../assertTypes'
import { UnexpectedTypeError } from '../errors'
interface KeyValueParsletOptions {
allowOnlyNameOrNumberProperties: boolean
}
export class KeyValueParslet implements InfixParslet {
private readonly allowOnlyNameOrNumberProperties
constructor (opts: KeyValueParsletOptions) {
this.allowOnlyNameOrNumberProperties = opts.allowOnlyNameOrNumberProperties
}
accepts (type: TokenType, next: TokenType): boolean {

@@ -18,2 +29,5 @@ return type === ':'

parseInfix (parser: ParserEngine, left: NonTerminalResult): NonTerminalResult {
if (this.allowOnlyNameOrNumberProperties && left.type !== 'NUMBER' && left.type !== 'NAME') {
throw new UnexpectedTypeError(left)
}
parser.consume(':')

@@ -23,6 +37,6 @@ const value = parser.parseType(Precedence.KEY_VALUE)

type: 'KEY_VALUE',
key: left.type === 'NUMBER' ? left : assertTerminal(left),
value: value
left: left.type === 'NUMBER' ? left : assertTerminal(left),
right: value
}
}
}
import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { ParserEngine } from '../ParserEngine'

@@ -21,15 +21,27 @@ import { ParseResult } from '../ParseResult'

}
let result = 'module:'
const allowed: TokenType[] = ['Identifier', '~', '@', '/', '#']
let token = parser.getToken()
while (allowed.includes(token.type)) {
result += token.text
parser.consume(token.type)
token = parser.getToken()
if (parser.consume('StringValue')) {
return {
type: 'MODULE',
value: token.text.slice(1, -1),
meta: {
quote: token.text[0] as '\'' | '"'
}
}
} else {
let result = ''
const allowed: TokenType[] = ['Identifier', '@', '/']
while (allowed.some(type => parser.consume(type))) {
result += token.text
token = parser.getToken()
}
return {
type: 'MODULE',
value: result,
meta: {
quote: undefined
}
}
}
return {
type: 'MODULE',
path: result
}
}
}
import { ParserEngine } from '../ParserEngine'
import { TokenType } from '../lexer/Token'
import { NameResult, ParseResult } from '../ParseResult'
import { NameResult } from '../ParseResult'
import { PrefixParslet } from './Parslet'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'

@@ -46,5 +46,15 @@ const reservedWords = [

interface NameParsletOptions {
allowedAdditionalTokens: TokenType[]
}
export class NameParslet implements PrefixParslet {
private readonly allowedAdditionalTokens: TokenType[]
constructor (options: NameParsletOptions) {
this.allowedAdditionalTokens = options.allowedAdditionalTokens
}
accepts (type: TokenType, next: TokenType): boolean {
return type === 'Identifier' || type === 'this' || type === 'new'
return type === 'Identifier' || type === 'this' || type === 'new' || this.allowedAdditionalTokens.includes(type)
}

@@ -56,14 +66,15 @@

parsePrefix (parser: ParserEngine): ParseResult {
parsePrefix (parser: ParserEngine): NameResult {
const token = parser.getToken()
parser.consume('Identifier') || parser.consume('this') || parser.consume('new')
const result: NameResult = {
parser.consume('Identifier') || parser.consume('this') || parser.consume('new') ||
this.allowedAdditionalTokens.some(type => parser.consume(type))
return {
type: 'NAME',
name: token.text
value: token.text,
meta: {
reservedWord: reservedWords.includes(token.text)
}
}
if (reservedWords.includes(token.text)) {
result.reservedWord = true
}
return result
}
}

@@ -5,3 +5,3 @@ import { InfixParslet, PrefixParslet } from './Parslet'

import { NonTerminalResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { isQuestionMarkUnknownType } from './isQuestionMarkUnkownType'

@@ -12,3 +12,3 @@ import { assertTerminal } from '../assertTypes'

accepts (type: TokenType, next: TokenType): boolean {
return (type === '?' && !isQuestionMarkUnknownType(next)) || type === '!'
return type === '?' && !isQuestionMarkUnknownType(next)
}

@@ -21,9 +21,10 @@

parsePrefix (parser: ParserEngine): ParseResult {
const nullable = parser.consume('?') || !parser.consume('!')
const value = parser.parseType(Precedence.NULLABLE)
if (value.nullable !== undefined) {
throw new Error('Multiple nullable modifiers on same type')
parser.consume('?')
return {
type: 'NULLABLE',
element: parser.parseType(Precedence.NULLABLE),
meta: {
position: 'PREFIX'
}
}
value.nullable = nullable
return value
}

@@ -34,3 +35,3 @@ }

accepts (type: TokenType, next: TokenType): boolean {
return type === '?' || type === '!'
return type === '?'
}

@@ -43,10 +44,11 @@

parseInfix (parser: ParserEngine, left: NonTerminalResult): ParseResult {
const nullable = parser.consume('?') || !parser.consume('!')
const value = assertTerminal(left)
if (value.nullable !== undefined) {
throw new Error('Multiple nullable modifiers on same type')
parser.consume('?')
return {
type: 'NULLABLE',
element: assertTerminal(left),
meta: {
position: 'SUFFIX'
}
}
value.nullable = nullable
return value
}
}
import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { ParserEngine } from '../ParserEngine'

@@ -5,0 +5,0 @@ import { NonTerminalResult } from '../ParseResult'

@@ -1,11 +0,11 @@

import { InfixParslet } from './Parslet'
import { InfixParslet, PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { NonTerminalResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'
export class OptionalParslet implements InfixParslet {
export class OptionalParslet implements PrefixParslet, InfixParslet {
accepts (type: TokenType, next: TokenType): boolean {
return type === '=' && next !== '>'
return type === '='
}

@@ -17,8 +17,23 @@

parsePrefix (parser: ParserEngine): ParseResult {
parser.consume('=')
return {
type: 'OPTIONAL',
element: parser.parseType(Precedence.OPTIONAL),
meta: {
position: 'PREFIX'
}
}
}
parseInfix (parser: ParserEngine, left: NonTerminalResult): ParseResult {
parser.consume('=')
const result = assertTerminal(left)
result.optional = true
return result
return {
type: 'OPTIONAL',
element: assertTerminal(left),
meta: {
position: 'SUFFIX'
}
}
}
}

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

import { KeyValueResult, NonTerminalResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertNamedKeyValueOrTerminal } from '../assertTypes'
import { NoParsletFoundError } from '../errors'

@@ -14,3 +15,3 @@ interface ParameterListParsletOptions {

export class ParameterListParslet implements InfixParslet {
private readonly allowTrailingComma: boolean // TODO
private readonly allowTrailingComma: boolean

@@ -35,5 +36,18 @@ constructor (option: ParameterListParsletOptions) {

do {
const next = parser.parseNonTerminalType(Precedence.PARAMETER_LIST)
elements.push(assertNamedKeyValueOrTerminal(next))
try {
const next = parser.parseNonTerminalType(Precedence.PARAMETER_LIST)
elements.push(assertNamedKeyValueOrTerminal(next))
} catch (e) {
if (this.allowTrailingComma && e instanceof NoParsletFoundError) {
break
} else {
throw e
}
}
} while (parser.consume(','))
if (elements.length > 0 && elements.slice(0, -1).some(e => e.type === 'VARIADIC')) {
throw new Error('Only the last parameter may be a rest parameter')
}
return {

@@ -40,0 +54,0 @@ type: 'PARAMETER_LIST',

import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { ParserEngine } from '../ParserEngine'
import { NonTerminalResult } from '../ParseResult'
import { ParenthesisResult } from '../ParseResult'
export class ParenthesisParslet implements PrefixParslet {
accepts (type: TokenType, next: TokenType): boolean {
return type === '(' && next !== ')'
return type === '('
}

@@ -16,10 +16,13 @@

parsePrefix (parser: ParserEngine): NonTerminalResult {
parsePrefix (parser: ParserEngine): ParenthesisResult {
parser.consume('(')
const result = parser.parseNonTerminalType(Precedence.ALL)
const result = parser.tryParseType(Precedence.ALL)
if (!parser.consume(')')) {
throw new Error('Unterminated parenthesis')
}
return result
return {
type: 'PARENTHESIS',
element: result // NOTE: this can only be non-terminal or undefined if it is a parameter list
}
}
}
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { NonTerminalResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'

@@ -6,0 +6,0 @@ export interface Parslet {

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet'

import { ParseResult, RecordResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'

@@ -14,3 +14,3 @@ export class RecordParslet implements PrefixParslet {

getPrecedence (): Precedence {
return Precedence.RECORD
return Precedence.OBJECT
}

@@ -21,4 +21,4 @@

const result: RecordResult = {
type: 'RECORD',
fields: []
type: 'OBJECT',
elements: []
}

@@ -28,3 +28,3 @@

do {
const field = parser.parseNonTerminalType(Precedence.RECORD)
const field = parser.parseNonTerminalType(Precedence.OBJECT)
if (field.type !== 'NAME' && field.type !== 'NUMBER' && field.type !== 'KEY_VALUE') {

@@ -34,3 +34,3 @@ throw new Error('records may only contain \'NAME\', \'NUMBER\' or \'KEY_VALUE\' fields.')

result.fields.push(field)
result.elements.push(field)
} while (parser.consume(','))

@@ -37,0 +37,0 @@ if (!parser.consume('}')) {

@@ -5,3 +5,3 @@ import { PrefixParslet } from './Parslet'

import { ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { isQuestionMarkUnknownType } from './isQuestionMarkUnkownType'

@@ -19,27 +19,28 @@

parsePrefix (parser: ParserEngine): ParseResult {
switch (parser.getToken().type) {
case 'null':
parser.consume('null')
return {
type: 'NULL'
}
case 'undefined':
parser.consume('undefined')
return {
type: 'UNDEFINED'
}
case '*':
parser.consume('*')
return {
type: 'ALL'
}
case '?':
parser.consume('?')
return {
type: 'UNKNOWN'
}
default:
throw new Error('Unacceptable token: ' + parser.getToken().text)
if (parser.consume('null')) {
return {
type: 'NULL'
}
}
if (parser.consume('undefined')) {
return {
type: 'UNDEFINED'
}
}
if (parser.consume('*')) {
return {
type: 'ANY'
}
}
if (parser.consume('?')) {
return {
type: 'UNKNOWN'
}
}
throw new Error('Unacceptable token: ' + parser.getToken().text)
}
}
import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { StringValueResult } from '../ParseResult'
import { Precedence } from '../Precedence'

@@ -16,3 +16,3 @@ export class StringValueParslet implements PrefixParslet {

parsePrefix (parser: ParserEngine): ParseResult {
parsePrefix (parser: ParserEngine): StringValueResult {
const token = parser.getToken()

@@ -23,5 +23,7 @@ parser.consume('StringValue')

value: token.text.slice(1, -1),
quote: token.text[0]
meta: {
quote: token.text[0] as '\'' | '"'
}
}
}
}

@@ -5,3 +5,4 @@ import { InfixParslet } from './Parslet'

import { NonTerminalResult, ParseResult, SymbolResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertNumberOrVariadicName } from '../assertTypes'

@@ -24,10 +25,7 @@ export class SymbolParslet implements InfixParslet {

type: 'SYMBOL',
name: left.name
value: left.value
}
if (!parser.consume(')')) {
const next = parser.parseNonTerminalType(Precedence.SYMBOL)
if (next.type !== 'NUMBER' && next.type !== 'NAME') {
throw new Error('Symbol value must be a number or a name')
}
result.value = next
result.element = assertNumberOrVariadicName(next)
if (!parser.consume(')')) {

@@ -34,0 +32,0 @@ throw new Error('Symbol does not end after value')

import { PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { ParseResult, TypeOfResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { TypeOfResult } from '../ParseResult'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'

@@ -17,13 +17,9 @@

parsePrefix (parser: ParserEngine): ParseResult {
parsePrefix (parser: ParserEngine): TypeOfResult {
parser.consume('typeof')
const result: TypeOfResult = {
type: 'TYPE_OF'
return {
type: 'TYPE_OF',
element: assertTerminal(parser.parseType(Precedence.KEY_OF_TYPE_OF))
}
const value = parser.tryParseType(Precedence.KEY_OF_TYPE_OF)
if (value !== undefined) {
result.value = assertTerminal(value)
}
return result
}
}

@@ -5,3 +5,3 @@ import { InfixParslet } from './Parslet'

import { TokenType } from '../lexer/Token'
import { Precedence } from './Precedence'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'

@@ -8,0 +8,0 @@

import { InfixParslet, PrefixParslet } from './Parslet'
import { TokenType } from '../lexer/Token'
import { ParserEngine } from '../ParserEngine'
import { NonTerminalResult, ParseResult } from '../ParseResult'
import { Precedence } from './Precedence'
import { NonTerminalResult, ParseResult, VariadicResult } from '../ParseResult'
import { Precedence } from '../Precedence'
import { assertTerminal } from '../assertTypes'
interface VariadicParsletOptions {
allowEnclosingBrackets: boolean
}
export class VariadicParslet implements PrefixParslet, InfixParslet {
private readonly allowEnclosingBrackets: boolean
constructor (opts: VariadicParsletOptions) {
this.allowEnclosingBrackets = opts.allowEnclosingBrackets
}
accepts (type: TokenType): boolean {

@@ -17,11 +27,29 @@ return type === '...'

parsePrefix (parser: ParserEngine): ParseResult {
parsePrefix (parser: ParserEngine): VariadicResult<ParseResult> {
parser.consume('...')
const shouldClose = parser.consume('[')
const value = parser.parseType(Precedence.PREFIX)
if (shouldClose && !parser.consume(']')) {
const brackets = this.allowEnclosingBrackets && parser.consume('[')
const value = parser.tryParseType(Precedence.PREFIX)
if (brackets && !parser.consume(']')) {
throw new Error('Unterminated variadic type. Missing \']\'')
}
value.repeatable = true
return value
if (value !== undefined) {
return {
type: 'VARIADIC',
element: assertTerminal(value),
meta: {
position: 'PREFIX',
squareBrackets: brackets
}
}
} else {
return {
type: 'VARIADIC',
meta: {
position: 'ONLY_DOTS',
squareBrackets: false
}
}
}
}

@@ -31,6 +59,11 @@

parser.consume('...')
const result = assertTerminal(left)
result.repeatable = true
return result
return {
type: 'VARIADIC',
element: assertTerminal(left),
meta: {
position: 'SUFFIX',
squareBrackets: false
}
}
}
}
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