import { privateDecrypt } from "crypto"; import { ReferenceTypeExpression, SourceFile, SourceFileElement, StructDeclaration, StructDeclarationField, SyntaxKind, Token, TokenKind, Expression, TypeExpression, ConstantExpression, ReferenceExpression, Dot, Identifier, TupleExpression, PrefixExpression, ExpressionStatement, ImportDeclaration, Param, Pattern, BindPattern, LetDeclaration, TypeAssert, ExprBody, BlockBody, QualifiedName, NestedExpression, NamedTuplePattern, StructPattern, VariadicStructPatternElement, PunnedFieldStructPatternElement, FieldStructPatternElement, TuplePattern, InfixExpression, } from "./cst" import { Stream, MultiDict } from "./util"; const DESCRIPTIONS: Record = { [SyntaxKind.StringLiteral]: 'a string literal', [SyntaxKind.Identifier]: "an identifier", [SyntaxKind.Comma]: "','", [SyntaxKind.Colon]: "':'", [SyntaxKind.Integer]: "an integer", [SyntaxKind.LParen]: "'('", [SyntaxKind.RParen]: "')'", [SyntaxKind.LBrace]: "'{'", [SyntaxKind.RBrace]: "'}'", [SyntaxKind.LBracket]: "'['", [SyntaxKind.RBracket]: "']'", [SyntaxKind.ConstantExpression]: 'a constant expression', [SyntaxKind.ReferenceExpression]: 'a reference expression', [SyntaxKind.LineFoldEnd]: 'the end of the current line-fold', [SyntaxKind.TupleExpression]: 'a tuple expression such as (1, 2)', [SyntaxKind.ReferenceExpression]: 'a reference to some variable', [SyntaxKind.NestedExpression]: 'an expression nested with parentheses', [SyntaxKind.ConstantExpression]: 'a constant expression such as 1 or "foo"', } function describeSyntaxKind(kind: SyntaxKind): string { const desc = DESCRIPTIONS[kind]; if (desc === undefined) { throw new Error(`Could not describe SyntaxKind '${kind}'`); } return desc } function describeExpected(expected: SyntaxKind[]) { if (expected.length === 0) { return 'nothing'; } let out = describeSyntaxKind(expected[0]); if (expected.length === 1) { return out; } for (let i = 1; i < expected.length-1; i++) { const kind = expected[i]; out += ', ' + describeSyntaxKind(kind); } out += ' or ' + describeSyntaxKind(expected[expected.length-1]) return out; } class ParseError extends Error { public constructor( public actual: Token, public expected: SyntaxKind[], ) { super(`got '${actual.text}' but expected ${describeExpected(expected)}`); } } function isConstructor(token: Token): boolean { return token.kind === SyntaxKind.Identifier && token.text[0].toUpperCase() === token.text[0]; } function isBinaryOperatorLike(token: Token): boolean { return token.kind === SyntaxKind.CustomOperator; } function isPrefixOperatorLike(token: Token): boolean { return token.kind === SyntaxKind.CustomOperator; } const enum OperatorMode { None = 0, Prefix = 1, InfixL = 2, InfixR = 4, Infix = 6, Suffix = 8, } interface OperatorInfo { name: string, mode: OperatorMode, precedence: number, } const EXPR_OPERATOR_TABLE: Array<[string, OperatorMode, number]> = [ ["**", OperatorMode.InfixR, 11], ["*", OperatorMode.InfixL, 8], ["/", OperatorMode.InfixL, 8], ["+", OperatorMode.InfixL, 7], ["-", OperatorMode.InfixL, 7], ["<", OperatorMode.InfixL, 6], [">", OperatorMode.InfixL, 6], ["<=", OperatorMode.InfixL, 5], [">=", OperatorMode.InfixL, 5], ["==", OperatorMode.InfixL, 5], ["!=", OperatorMode.InfixL, 5], ["<*", OperatorMode.InfixL, 4], [":", OperatorMode.InfixL, 3], ["<|>", OperatorMode.InfixL, 2], ["", OperatorMode.InfixL, 1], ["$", OperatorMode.InfixR, 0] ]; export class Parser { private prefixExprOperators = new Set(); private binaryExprOperators = new Map(); private suffixExprOperators = new Set(); public constructor( public tokens: Stream, ) { for (const [name, mode, precedence] of EXPR_OPERATOR_TABLE) { this.binaryExprOperators.set(name, { name, mode, precedence }); } } private getToken(): Token { return this.tokens.get(); } private peekToken(offset = 1): Token { return this.tokens.peek(offset); } private assertToken(token: Token, expectedKind: K): void { if (token.kind !== expectedKind) { this.raiseParseError(token, [ expectedKind ]); } } private expectToken(expectedKind: K): Token & { kind: K } { const token = this.getToken(); if (token.kind !== expectedKind) { this.raiseParseError(token, [ expectedKind ]) } return token as Token & { kind: K }; } private raiseParseError(actual: Token, expected: SyntaxKind[]): never { throw new ParseError(actual, expected); } private peekTokenAfterModifiers(): Token { let t0; for (let i = 1;;i++) { t0 = this.peekToken(i); if (t0.kind !== SyntaxKind.PubKeyword) { break; } } return t0; } public parseReferenceTypeExpression(): ReferenceTypeExpression { const name = this.expectToken(SyntaxKind.Identifier); return new ReferenceTypeExpression([], name); } public parseTypeExpression(): TypeExpression { const t0 = this.peekToken(); switch (t0.kind) { case SyntaxKind.Identifier: return this.parseReferenceTypeExpression(); default: throw new ParseError(t0, [ SyntaxKind.Identifier ]); } } public parseConstantExpression(): ConstantExpression { const token = this.getToken() if (token.kind !== SyntaxKind.StringLiteral && token.kind !== SyntaxKind.Integer) { this.raiseParseError(token, [ SyntaxKind.StringLiteral, SyntaxKind.Integer ]) } return new ConstantExpression(token); } public parseQualifiedName(): QualifiedName { const modulePath: Array<[Identifier, Dot]> = []; let name = this.expectToken(SyntaxKind.Identifier) for (;;) { const t1 = this.peekToken() if (t1.kind !== SyntaxKind.Dot) { break; } modulePath.push([name, t1]); name = this.expectToken(SyntaxKind.Identifier) } return new QualifiedName(modulePath, name); } public parseReferenceExpression(): ReferenceExpression { return new ReferenceExpression(this.parseQualifiedName()); } private parseExpressionWithParens(): Expression { const lparen = this.expectToken(SyntaxKind.LParen) const t1 = this.peekToken(); if (t1.kind === SyntaxKind.RParen) { this.getToken(); return new TupleExpression(lparen, [], t1); } else if (t1.kind === SyntaxKind.Constructor) { } else { const expression = this.parseExpression(); const t2 = this.expectToken(SyntaxKind.RParen); return new NestedExpression(lparen, expression, t2); } } private parseExpressionNoOperators(): Expression { const t0 = this.peekToken(); switch (t0.kind) { case SyntaxKind.LParen: return this.parseExpressionWithParens(); case SyntaxKind.Identifier: return this.parseReferenceExpression(); case SyntaxKind.Integer: case SyntaxKind.StringLiteral: return this.parseConstantExpression(); default: this.raiseParseError(t0, [ SyntaxKind.TupleExpression, SyntaxKind.NestedExpression, SyntaxKind.ConstantExpression, SyntaxKind.ReferenceExpression ]); } } private parseUnaryExpression(): Expression { let result = this.parseExpressionNoOperators() const prefixes = []; for (;;) { const t0 = this.peekToken(); if (!isPrefixOperatorLike(t0)) { break; } if (!this.prefixExprOperators.has(t0.text)) { break; } prefixes.push(t0); this.getToken() } for (let i = prefixes.length-1; i >= 0; i--) { const operator = prefixes[i]; result = new PrefixExpression(operator, result); } return result; } private parseBinaryOperatorAfterExpr(lhs: Expression, minPrecedence: number) { for (;;) { const t0 = this.peekToken(); if (!isBinaryOperatorLike(t0)) { break; } const info0 = this.binaryExprOperators.get(t0.text); if (info0 === undefined || info0.precedence < minPrecedence) { break; } this.getToken(); let rhs = this.parseUnaryExpression(); for (;;) { const t1 = this.peekToken(); if (!isBinaryOperatorLike(t1)) { break; } const info1 = this.binaryExprOperators.get(t1.text); if (info1 === undefined || info1.precedence < info0.precedence || (info1.precedence === info0.precedence && (info1.mode & OperatorMode.InfixR) === 0)) { break; } rhs = this.parseBinaryOperatorAfterExpr(rhs, info0.precedence); } lhs = new InfixExpression(lhs, t0, rhs); } return lhs; } public parseExpression(): Expression { const lhs = this.parseUnaryExpression(); return this.parseBinaryOperatorAfterExpr(lhs, 0); } public parseStructDeclaration(): StructDeclaration { const structKeyword = this.expectToken(SyntaxKind.StructKeyword); const name = this.expectToken(SyntaxKind.Identifier); const t2 = this.peekToken() let members = null; if (t2.kind === SyntaxKind.BlockStart) { this.getToken(); members = []; for (;;) { const name = this.expectToken(SyntaxKind.Identifier); const colon = this.expectToken(SyntaxKind.Colon); const typeExpr = this.parseTypeExpression(); const member = new StructDeclarationField(name, colon, typeExpr); members.push(member); } } else { this.assertToken(t2, SyntaxKind.LineFoldEnd); } return new StructDeclaration(structKeyword, name, members); } private parsePatternStartingWithConstructor() { const name = this.expectToken(SyntaxKind.Constructor); const t2 = this.peekToken(); if (t2.kind === SyntaxKind.LBrace) { this.getToken(); const fields = []; let rbrace; for (;;) { const t3 = this.peekToken(); if (t3.kind === SyntaxKind.RBrace) { rbrace = t3; break; } else if (t3.kind === SyntaxKind.Identifier) { this.getToken(); const t4 = this.peekToken(); if (t4.kind === SyntaxKind.Equals) { this.getToken(); const pattern = this.parsePattern(); fields.push(new FieldStructPatternElement(t3, t4, pattern)); } else { fields.push(new PunnedFieldStructPatternElement(t3)); } } else if (t3.kind === SyntaxKind.DotDot) { this.getToken(); fields.push(new VariadicStructPatternElement(t3, null)); } else { this.raiseParseError(t3, [ SyntaxKind.Identifier, SyntaxKind.DotDot ]); } const t5 = this.peekToken(); if (t5.kind === SyntaxKind.Comma) { this.getToken(); } else if (t5.kind === SyntaxKind.RBrace) { rbrace = t5; break; } else { this.raiseParseError(t5, [ SyntaxKind.Comma, SyntaxKind.RBrace ]); } } return new StructPattern(name, t2, fields, rbrace); } else { const patterns = []; for (;;) { const t3 = this.peekToken(); if (t3.kind === SyntaxKind.RParen) { break; } patterns.push(this.parsePattern()); } return new NamedTuplePattern(name, patterns); } } public parseTuplePattern(): TuplePattern { const lparen = this.expectToken(SyntaxKind.LParen); const elements = []; let rparen; for (;;) { const t1 = this.peekToken(); if (t1.kind === SyntaxKind.RParen) { rparen = t1; break; } elements.push(this.parsePattern()); const t2 = this.peekToken(); if (t2.kind === SyntaxKind.Comma) { this.getToken(); } else if (t2.kind === SyntaxKind.RParen) { rparen = t2; break; } else { this.raiseParseError(t2, [ SyntaxKind.Comma, SyntaxKind.RParen ]); } } this.getToken(); return new TuplePattern(lparen, elements, rparen); } public parsePattern(): Pattern { const t0 = this.peekToken(); switch (t0.kind) { case SyntaxKind.LParen: { this.getToken(); const t1 = this.peekToken(); if (t1.kind === SyntaxKind.Constructor) { return this.parsePatternStartingWithConstructor(); } else { return this.parseTuplePattern(); } } case SyntaxKind.Identifier: this.getToken(); return new BindPattern(t0); default: this.raiseParseError(t0, [ SyntaxKind.Identifier ]); } } public parseParam(): Param { const pattern = this.parsePattern(); return new Param(pattern); } public parseDeclartionWithLetKeyword(): LetDeclaration { let t0 = this.getToken(); let pubKeyword = null; let mutKeyword = null; if (t0.kind === SyntaxKind.PubKeyword) { pubKeyword = t0; t0 = this.getToken(); } if (t0.kind !== SyntaxKind.LetKeyword) { this.raiseParseError(t0, [ SyntaxKind.LetKeyword ]); } const t1 = this.peekToken(); if (t1.kind === SyntaxKind.MutKeyword) { this.getToken(); mutKeyword = t1; } const pattern = this.parsePattern(); const params = []; for (;;) { const t2 = this.peekToken(); if (t2.kind === SyntaxKind.Colon || t2.kind === SyntaxKind.BlockStart || t2.kind === SyntaxKind.Equals || t2.kind === SyntaxKind.LineFoldEnd) { break; } params.push(this.parseParam()); } let typeAssert = null; let t3 = this.getToken(); if (t3.kind === SyntaxKind.Colon) { const typeExpression = this.parseTypeExpression(); typeAssert = new TypeAssert(t3, typeExpression); t3 = this.getToken(); } let body = null; switch (t3.kind) { case SyntaxKind.BlockStart: { const elements = []; for (;;) { const t4 = this.peekToken(); if (t4.kind === SyntaxKind.BlockEnd) { break; } elements.push(this.parseLetBodyElement()); } body = new BlockBody(t3, elements); t3 = this.getToken(); break; } case SyntaxKind.Equals: { const expression = this.parseExpression(); body = new ExprBody(t3, expression); t3 = this.getToken(); break; } case SyntaxKind.LineFoldEnd: break; } if (t3.kind !== SyntaxKind.LineFoldEnd) { this.raiseParseError(t3, [ SyntaxKind.LineFoldEnd ]); } return new LetDeclaration( pubKeyword, t0, mutKeyword, pattern, params, typeAssert, body ); } public parseExpressionStatement(): ExpressionStatement { const expression = this.parseExpression(); this.expectToken(SyntaxKind.LineFoldEnd) return new ExpressionStatement(expression); } public parseImportDeclaration(): ImportDeclaration { const importKeyword = this.expectToken(SyntaxKind.ImportKeyword); const importSource = this.expectToken(SyntaxKind.StringLiteral); return new ImportDeclaration(importKeyword, importSource); } public parseSourceFileElement(): SourceFileElement { const t0 = this.peekTokenAfterModifiers(); switch (t0.kind) { case SyntaxKind.LetKeyword: return this.parseDeclartionWithLetKeyword(); case SyntaxKind.ImportKeyword: return this.parseImportDeclaration(); case SyntaxKind.StructKeyword: return this.parseStructDeclaration(); default: return this.parseExpressionStatement(); } } public parseSourceFile(): SourceFile { const elements = []; let eof; for (;;) { const t0 = this.peekToken(); if (t0.kind === SyntaxKind.EndOfFile) { eof = t0; break; } const element = this.parseSourceFileElement(); elements.push(element); } return new SourceFile(elements, eof); } }