Make more JavaScript nodes parsable

This commit is contained in:
Sam Vervaeck 2020-05-22 23:59:43 +02:00
parent 02948613a9
commit 70064860d4
5 changed files with 272 additions and 58 deletions

View file

@ -294,9 +294,14 @@ node JSString > JSToken {
value: String,
}
node JSInteger > JSToken {
value: Int,
}
node JSFromKeyword > JSToken;
node JSReturnKeyword > JSToken;
node JSTryKeyword > JSToken;
node JSFinallyKeyword > JSToken;
node JSCatchKeyword > JSToken;
node JSImportKeyword > JSToken;
node JSAsKeyword > JSToken;
@ -377,6 +382,12 @@ node JSConditionalExpression > JSExpression {
alternate: JSExpression,
}
type JSValue = Int
node JSLiteralExpression > JSExpression {
value: JSValue,
}
node JSReferenceExpression > JSExpression {
name: String,
}
@ -385,6 +396,17 @@ node JSSourceElement;
node JSStatement > JSSourceElement;
node JSCatchBlock {
bindings: Option<JSPattern>,
elements: Vec<JSSourceElement>,
}
node JSTryCatchStatement {
tryBlock: Vec<JSSourceElement>,
catchBlock: Option<JSCatchBlock>,
finalBlock: Option<Vec<JSSourceElement>>,
}
node JSExpressionStatement > JSStatement {
expression: JSExpression,
}
@ -395,6 +417,10 @@ node JSConditionalStatement > JSStatement {
alternate: Vec<JSStatement>,
}
node JSReturnStatement > JSStatement {
value: Option<JSExpression>,
}
node JSParameter {
index: usize,
bindings: JSPattern,

173
src/ast.d.ts vendored
View file

@ -75,59 +75,65 @@ export const enum SyntaxKind {
JSOperator = 89,
JSIdentifier = 90,
JSString = 91,
JSFromKeyword = 92,
JSReturnKeyword = 93,
JSTryKeyword = 94,
JSCatchKeyword = 95,
JSImportKeyword = 96,
JSAsKeyword = 97,
JSConstKeyword = 98,
JSLetKeyword = 99,
JSExportKeyword = 100,
JSFunctionKeyword = 101,
JSWhileKeyword = 102,
JSForKeyword = 103,
JSCloseBrace = 104,
JSCloseBracket = 105,
JSCloseParen = 106,
JSOpenBrace = 107,
JSOpenBracket = 108,
JSOpenParen = 109,
JSSemi = 110,
JSComma = 111,
JSDot = 112,
JSDotDotDot = 113,
JSMulOp = 114,
JSAddOp = 115,
JSDivOp = 116,
JSSubOp = 117,
JSLtOp = 118,
JSGtOp = 119,
JSBOrOp = 120,
JSBXorOp = 121,
JSBAndOp = 122,
JSBNotOp = 123,
JSNotOp = 124,
JSBindPattern = 126,
JSConstantExpression = 128,
JSMemberExpression = 129,
JSCallExpression = 130,
JSBinaryExpression = 131,
JSUnaryExpression = 132,
JSNewExpression = 133,
JSSequenceExpression = 134,
JSConditionalExpression = 135,
JSReferenceExpression = 136,
JSExpressionStatement = 139,
JSConditionalStatement = 140,
JSParameter = 141,
JSImportStarBinding = 145,
JSImportAsBinding = 146,
JSImportDeclaration = 147,
JSFunctionDeclaration = 148,
JSArrowFunctionDeclaration = 149,
JSLetDeclaration = 150,
JSSourceFile = 151,
JSInteger = 92,
JSFromKeyword = 93,
JSReturnKeyword = 94,
JSTryKeyword = 95,
JSFinallyKeyword = 96,
JSCatchKeyword = 97,
JSImportKeyword = 98,
JSAsKeyword = 99,
JSConstKeyword = 100,
JSLetKeyword = 101,
JSExportKeyword = 102,
JSFunctionKeyword = 103,
JSWhileKeyword = 104,
JSForKeyword = 105,
JSCloseBrace = 106,
JSCloseBracket = 107,
JSCloseParen = 108,
JSOpenBrace = 109,
JSOpenBracket = 110,
JSOpenParen = 111,
JSSemi = 112,
JSComma = 113,
JSDot = 114,
JSDotDotDot = 115,
JSMulOp = 116,
JSAddOp = 117,
JSDivOp = 118,
JSSubOp = 119,
JSLtOp = 120,
JSGtOp = 121,
JSBOrOp = 122,
JSBXorOp = 123,
JSBAndOp = 124,
JSBNotOp = 125,
JSNotOp = 126,
JSBindPattern = 128,
JSConstantExpression = 130,
JSMemberExpression = 131,
JSCallExpression = 132,
JSBinaryExpression = 133,
JSUnaryExpression = 134,
JSNewExpression = 135,
JSSequenceExpression = 136,
JSConditionalExpression = 137,
JSLiteralExpression = 139,
JSReferenceExpression = 140,
JSCatchBlock = 143,
JSTryCatchStatement = 144,
JSExpressionStatement = 145,
JSConditionalStatement = 146,
JSReturnStatement = 147,
JSParameter = 148,
JSImportStarBinding = 152,
JSImportAsBinding = 153,
JSImportDeclaration = 154,
JSFunctionDeclaration = 155,
JSArrowFunctionDeclaration = 156,
JSLetDeclaration = 157,
JSSourceFile = 158,
}
@ -650,9 +656,11 @@ export type JSToken
| JSOperator
| JSIdentifier
| JSString
| JSInteger
| JSFromKeyword
| JSReturnKeyword
| JSTryKeyword
| JSFinallyKeyword
| JSCatchKeyword
| JSImportKeyword
| JSAsKeyword
@ -700,6 +708,11 @@ export interface JSString extends SyntaxBase {
value: string;
}
export interface JSInteger extends SyntaxBase {
kind: SyntaxKind.JSInteger;
value: bigint;
}
export interface JSFromKeyword extends SyntaxBase {
kind: SyntaxKind.JSFromKeyword;
}
@ -712,6 +725,10 @@ export interface JSTryKeyword extends SyntaxBase {
kind: SyntaxKind.JSTryKeyword;
}
export interface JSFinallyKeyword extends SyntaxBase {
kind: SyntaxKind.JSFinallyKeyword;
}
export interface JSCatchKeyword extends SyntaxBase {
kind: SyntaxKind.JSCatchKeyword;
}
@ -850,6 +867,7 @@ export type JSExpression
| JSNewExpression
| JSSequenceExpression
| JSConditionalExpression
| JSLiteralExpression
| JSReferenceExpression
@ -901,6 +919,11 @@ export interface JSConditionalExpression extends SyntaxBase {
alternate: JSExpression;
}
export interface JSLiteralExpression extends SyntaxBase {
kind: SyntaxKind.JSLiteralExpression;
value: JSValue;
}
export interface JSReferenceExpression extends SyntaxBase {
kind: SyntaxKind.JSReferenceExpression;
name: string;
@ -909,6 +932,7 @@ export interface JSReferenceExpression extends SyntaxBase {
export type JSSourceElement
= JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
| JSImportDeclaration
| JSFunctionDeclaration
| JSArrowFunctionDeclaration
@ -918,8 +942,22 @@ export type JSSourceElement
export type JSStatement
= JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
export interface JSCatchBlock extends SyntaxBase {
kind: SyntaxKind.JSCatchBlock;
bindings: JSPattern | null;
elements: JSSourceElement[];
}
export interface JSTryCatchStatement extends SyntaxBase {
kind: SyntaxKind.JSTryCatchStatement;
tryBlock: JSSourceElement[];
catchBlock: JSCatchBlock | null;
finalBlock: JSSourceElement[] | null;
}
export interface JSExpressionStatement extends SyntaxBase {
kind: SyntaxKind.JSExpressionStatement;
expression: JSExpression;
@ -932,6 +970,11 @@ export interface JSConditionalStatement extends SyntaxBase {
alternate: JSStatement[];
}
export interface JSReturnStatement extends SyntaxBase {
kind: SyntaxKind.JSReturnStatement;
value: JSExpression | null;
}
export interface JSParameter extends SyntaxBase {
kind: SyntaxKind.JSParameter;
index: number;
@ -1074,9 +1117,11 @@ export type JSSyntax
= JSOperator
| JSIdentifier
| JSString
| JSInteger
| JSFromKeyword
| JSReturnKeyword
| JSTryKeyword
| JSFinallyKeyword
| JSCatchKeyword
| JSImportKeyword
| JSAsKeyword
@ -1116,9 +1161,13 @@ export type JSSyntax
| JSNewExpression
| JSSequenceExpression
| JSConditionalExpression
| JSLiteralExpression
| JSReferenceExpression
| JSCatchBlock
| JSTryCatchStatement
| JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
| JSParameter
| JSImportStarBinding
| JSImportAsBinding
@ -1205,9 +1254,11 @@ export type Syntax
| JSOperator
| JSIdentifier
| JSString
| JSInteger
| JSFromKeyword
| JSReturnKeyword
| JSTryKeyword
| JSFinallyKeyword
| JSCatchKeyword
| JSImportKeyword
| JSAsKeyword
@ -1247,9 +1298,13 @@ export type Syntax
| JSNewExpression
| JSSequenceExpression
| JSConditionalExpression
| JSLiteralExpression
| JSReferenceExpression
| JSCatchBlock
| JSTryCatchStatement
| JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
| JSParameter
| JSImportStarBinding
| JSImportAsBinding
@ -1337,9 +1392,11 @@ export function createBoltRecordDeclaration(modifiers: BoltDeclarationModifiers,
export function createJSOperator(text: string, span?: TextSpan | null): JSOperator;
export function createJSIdentifier(text: string, span?: TextSpan | null): JSIdentifier;
export function createJSString(value: string, span?: TextSpan | null): JSString;
export function createJSInteger(value: bigint, span?: TextSpan | null): JSInteger;
export function createJSFromKeyword(span?: TextSpan | null): JSFromKeyword;
export function createJSReturnKeyword(span?: TextSpan | null): JSReturnKeyword;
export function createJSTryKeyword(span?: TextSpan | null): JSTryKeyword;
export function createJSFinallyKeyword(span?: TextSpan | null): JSFinallyKeyword;
export function createJSCatchKeyword(span?: TextSpan | null): JSCatchKeyword;
export function createJSImportKeyword(span?: TextSpan | null): JSImportKeyword;
export function createJSAsKeyword(span?: TextSpan | null): JSAsKeyword;
@ -1379,9 +1436,13 @@ export function createJSUnaryExpression(operator: JSOperator, operand: JSExpress
export function createJSNewExpression(target: JSExpression, arguments: JSExpression[], span?: TextSpan | null): JSNewExpression;
export function createJSSequenceExpression(expressions: JSExpression[], span?: TextSpan | null): JSSequenceExpression;
export function createJSConditionalExpression(test: JSExpression, consequent: JSExpression, alternate: JSExpression, span?: TextSpan | null): JSConditionalExpression;
export function createJSLiteralExpression(value: JSValue, span?: TextSpan | null): JSLiteralExpression;
export function createJSReferenceExpression(name: string, span?: TextSpan | null): JSReferenceExpression;
export function createJSCatchBlock(bindings: JSPattern | null, elements: JSSourceElement[], span?: TextSpan | null): JSCatchBlock;
export function createJSTryCatchStatement(tryBlock: JSSourceElement[], catchBlock: JSCatchBlock | null, finalBlock: JSSourceElement[] | null, span?: TextSpan | null): JSTryCatchStatement;
export function createJSExpressionStatement(expression: JSExpression, span?: TextSpan | null): JSExpressionStatement;
export function createJSConditionalStatement(test: JSExpression, consequent: JSStatement[], alternate: JSStatement[], span?: TextSpan | null): JSConditionalStatement;
export function createJSReturnStatement(value: JSExpression | null, span?: TextSpan | null): JSReturnStatement;
export function createJSParameter(index: number, bindings: JSPattern, defaultValue: JSExpression | null, span?: TextSpan | null): JSParameter;
export function createJSImportStarBinding(local: JSIdentifier, span?: TextSpan | null): JSImportStarBinding;
export function createJSImportAsBinding(remote: JSIdentifier, local: JSIdentifier | null, span?: TextSpan | null): JSImportAsBinding;
@ -1478,9 +1539,11 @@ export function isJSToken(value: any): value is JSToken;
export function isJSOperator(value: any): value is JSOperator;
export function isJSIdentifier(value: any): value is JSIdentifier;
export function isJSString(value: any): value is JSString;
export function isJSInteger(value: any): value is JSInteger;
export function isJSFromKeyword(value: any): value is JSFromKeyword;
export function isJSReturnKeyword(value: any): value is JSReturnKeyword;
export function isJSTryKeyword(value: any): value is JSTryKeyword;
export function isJSFinallyKeyword(value: any): value is JSFinallyKeyword;
export function isJSCatchKeyword(value: any): value is JSCatchKeyword;
export function isJSImportKeyword(value: any): value is JSImportKeyword;
export function isJSAsKeyword(value: any): value is JSAsKeyword;
@ -1522,11 +1585,15 @@ export function isJSUnaryExpression(value: any): value is JSUnaryExpression;
export function isJSNewExpression(value: any): value is JSNewExpression;
export function isJSSequenceExpression(value: any): value is JSSequenceExpression;
export function isJSConditionalExpression(value: any): value is JSConditionalExpression;
export function isJSLiteralExpression(value: any): value is JSLiteralExpression;
export function isJSReferenceExpression(value: any): value is JSReferenceExpression;
export function isJSSourceElement(value: any): value is JSSourceElement;
export function isJSStatement(value: any): value is JSStatement;
export function isJSCatchBlock(value: any): value is JSCatchBlock;
export function isJSTryCatchStatement(value: any): value is JSTryCatchStatement;
export function isJSExpressionStatement(value: any): value is JSExpressionStatement;
export function isJSConditionalStatement(value: any): value is JSConditionalStatement;
export function isJSReturnStatement(value: any): value is JSReturnStatement;
export function isJSParameter(value: any): value is JSParameter;
export function isJSDeclaration(value: any): value is JSDeclaration;
export function isJSImportBinding(value: any): value is JSImportBinding;

View file

@ -19,11 +19,33 @@ import {
JSString,
createJSImportDeclaration,
createJSImportStarBinding,
createJSImportAsBinding
createJSImportAsBinding,
JSReturnKeyword,
createJSReturnStatement,
JSReturnStatement,
createJSTryCatchStatement,
JSPattern,
createJSLiteralExpression,
} from "../../ast"
export type JSTokenStream = Stream<JSToken>;
const T0_EXPRESSION = [
SyntaxKind.JSIdentifier,
SyntaxKind.JSString,
SyntaxKind.JSAddOp,
SyntaxKind.JSSubOp,
SyntaxKind.JSNotOp,
SyntaxKind.JSBNotOp,
];
const T0_STATEMENT = [
...T0_EXPRESSION,
SyntaxKind.JSReturnKeyword,
SyntaxKind.JSTryKeyword,
SyntaxKind.JSForKeyword,
];
const T0_DECLARATION = [
SyntaxKind.JSConstKeyword,
SyntaxKind.JSLetKeyword,
@ -34,6 +56,11 @@ const T0_DECLARATION = [
export class JSParser {
public parseJSPattern(tokens: JSTokenStream): JSPattern {
// TODO
tokens.get();
}
public parseJSReferenceExpression(tokens: JSTokenStream): JSReferenceExpression {
const t0 = tokens.get();
assertToken(t0, SyntaxKind.JSIdentifier);
@ -46,6 +73,11 @@ export class JSParser {
const t0 = tokens.peek();
if (t0.kind === SyntaxKind.JSIdentifier) {
return this.parseJSReferenceExpression(tokens);
} else if (t0.kind === SyntaxKind.JSInteger) {
tokens.get();
const result = createJSLiteralExpression(t0.value);
setOrigNodeRange(result, t0, t0);
return result;
} else {
throw new ParseError(t0, [SyntaxKind.JSIdentifier]);
}
@ -56,7 +88,11 @@ export class JSParser {
let result = this.parsePrimitiveJSExpression(tokens);
while (true) {
const t1 = tokens.peek();
if (t1.kind === SyntaxKind.JSCloseBrace || t1.kind === SyntaxKind.JSCloseParen || t1.kind === SyntaxKind.JSCloseBracket || t1.kind === SyntaxKind.JSSemi) {
if (t1.kind === SyntaxKind.JSCloseBrace
|| t1.kind === SyntaxKind.JSCloseParen
|| t1.kind === SyntaxKind.JSCloseBracket
|| t1.kind === SyntaxKind.JSComma
|| t1.kind === SyntaxKind.JSSemi) {
break;
}
if (t1.kind === SyntaxKind.JSDot) {
@ -105,8 +141,79 @@ export class JSParser {
return result;
}
public parseJSReturnStatement(tokens: JSTokenStream): JSReturnStatement {
let value = null;
const t0 = tokens.get();
assertToken(t0, SyntaxKind.JSReturnKeyword);
const t1 = tokens.peek();
if (T0_EXPRESSION.indexOf(t1.kind) !== -1) {
value = this.parseJSExpression(tokens);
}
const result = createJSReturnStatement(value)
setOrigNodeRange(result, t0, value !== null ? value : t0);
return result;
}
public parseJSTryCatchStatement(tokens: JSTokenStream): JSTryCatchStatement {
let catchBlock = null;
let finallyBlock = null;
let lastToken: JSToken;
const t0 = tokens.get();
assertToken(t0, SyntaxKind.JSTryKeyword);
const t1 = tokens.get();
assertToken(t1, SyntaxKind.JSOpenBrace);
const tryBlock = this.parseJSSourceElementList(tokens);
const t3 = tokens.get();
assertToken(t3, SyntaxKind.JSCloseBrace);
let t4 = tokens.peek();
if (t4.kind === SyntaxKind.JSCatchKeyword) {
tokens.get();
const t5 = tokens.get();
let bindings = null;
if (t5.kind === SyntaxKind.JSOpenParen) {
bindings = this.parseJSPattern(tokens);
const t6 = tokens.get();
assertToken(t6, SyntaxKind.JSCloseParen);
}
const t7 = tokens.get();
assertToken(t7, SyntaxKind.JSOpenBrace);
const elements = this.parseJSSourceElementList(tokens);
const t8 = tokens.get();
assertToken(t8, SyntaxKind.JSCloseBrace);
lastToken = t8
t4 = tokens.peek();
}
if (t4.kind === SyntaxKind.JSFinallyKeyword) {
tokens.get();
const t7 = tokens.get();
assertToken(t7, SyntaxKind.JSOpenBrace);
finallyBlock = this.parseJSSourceElementList(tokens);
const t8 = tokens.get();
assertToken(t8, SyntaxKind.JSCloseBrace);
lastToken = t8
}
const result = createJSTryCatchStatement(tryBlock, catchBlock, finallyBlock)
setOrigNodeRange(result, t0, lastToken!);
return result;
}
public parseJSStatement(tokens: JSTokenStream): JSStatement {
const t0 = tokens.peek();
if (t0.kind === SyntaxKind.JSReturnKeyword) {
return this.parseJSReturnStatement(tokens);
} else if (t0.kind === SyntaxKind.JSTryKeyword) {
return this.parseJSTryCatchStatement(tokens);
} else if (T0_EXPRESSION.indexOf(t0.kind) !== -1) {
return this.parseJSExpressionStatement(tokens);
} else {
throw new ParseError(t0, T0_STATEMENT);
}
}
public parseImportDeclaration(tokens: JSTokenStream): JSImportDeclaration {
@ -179,7 +286,7 @@ export class JSParser {
const elements: JSSourceElement[] = [];
while (true) {
const t0 = tokens.peek();
if (t0.kind === SyntaxKind.EndOfFile) {
if (t0.kind === SyntaxKind.EndOfFile || t0.kind === SyntaxKind.JSCloseBrace) {
break;
}
if (t0.kind === SyntaxKind.JSSemi) {

View file

@ -41,10 +41,12 @@ import {
createJSCatchKeyword,
createJSFromKeyword,
createJSString,
createJSTryKeyword,
createJSInteger,
} from "../../ast"
function isWhiteSpace(ch: string): boolean {
return /[\u0009\u000B\u000C\u0020\u00A0\u000B\uFEFF\p{Zs}]/.test(ch)
return /[\u0009\u000B\u000C\u0020\u00A0\u000B\uFEFF\p{Zs}]/u.test(ch)
}
function isLineTerminator(ch: string): boolean {
@ -287,6 +289,12 @@ export class JSScanner {
}
}
if (c0 === '0') {
this.getChar();
const endPos = this.currPos.clone();
return createJSInteger(0, new TextSpan(this.file, startPos, endPos));
}
if (isOperator(c0)) {
const text = this.takeWhile(isOperator)
const span = new TextSpan(this.file, startPos, this.currPos.clone());
@ -322,6 +330,7 @@ export class JSScanner {
const span = new TextSpan(this.file, startPos, endPos);
switch (name) {
case 'return': return createJSReturnKeyword(span);
case 'try': return createJSTryKeyword(span);
case 'catch': return createJSCatchKeyword(span);
case 'from': return createJSFromKeyword(span);
case 'let': return createJSLetKeyword(span);

View file

@ -223,6 +223,7 @@ export function getFileStem(filepath: string): string {
export function describeKind(kind: SyntaxKind): string {
switch (kind) {
case SyntaxKind.JSIdentifier:
return "a JavaScript identifier"
case SyntaxKind.BoltIdentifier:
return "an identifier"
case SyntaxKind.BoltOperator:
@ -331,6 +332,10 @@ export function describeKind(kind: SyntaxKind): string {
return "a JavaScript string"
case SyntaxKind.JSReturnKeyword:
return "'return'";
case SyntaxKind.JSForKeyword:
return "'for'";
case SyntaxKind.JSTryKeyword:
return "'try'";
default:
throw new Error(`failed to describe ${kindToString(kind)}`)
}