From 3611dedf249af3fd4e0b2c2d96f1b25d54efbaed Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Sat, 10 Sep 2022 14:11:04 +0200 Subject: [PATCH] Enable parsing enum-declarations --- src/cst.ts | 104 +++++++++++++++++++++++++++++++++++++++++++++ src/diagnostics.ts | 1 + src/parser.ts | 77 ++++++++++++++++++++++++++++++++- src/scanner.ts | 2 + 4 files changed, 182 insertions(+), 2 deletions(-) diff --git a/src/cst.ts b/src/cst.ts index 2224f7c2f..7c61cacfc 100644 --- a/src/cst.ts +++ b/src/cst.ts @@ -92,6 +92,7 @@ export const enum SyntaxKind { ModKeyword, ImportKeyword, StructKeyword, + EnumKeyword, TypeKeyword, ReturnKeyword, MatchKeyword, @@ -150,6 +151,7 @@ export const enum SyntaxKind { SuffixFuncDecl, LetDeclaration, StructDeclaration, + EnumDeclaration, ImportDeclaration, TypeAliasDeclaration, @@ -160,6 +162,10 @@ export const enum SyntaxKind { // Structure declaration members StructDeclarationField, + // Enum declaration elements + EnumDeclarationStructElement, + EnumDeclarationTupleElement, + // Other nodes WrappedOperator, Initializer, @@ -745,6 +751,16 @@ export class StructKeyword extends TokenBase { } +export class EnumKeyword extends TokenBase { + + public readonly kind = SyntaxKind.EnumKeyword; + + public get text(): string { + return 'enum'; + } + +} + export class ReturnKeyword extends TokenBase { public readonly kind = SyntaxKind.ReturnKeyword; @@ -870,6 +886,7 @@ export type Token | IfKeyword | ElseKeyword | ElifKeyword + | EnumKeyword export type TokenKind = Token['kind'] @@ -1557,6 +1574,88 @@ export class Param extends SyntaxBase { } +export class EnumDeclarationStructElement extends SyntaxBase { + + public readonly kind = SyntaxKind.EnumDeclarationStructElement; + + public constructor( + public name: IdentifierAlt, + public blockStart: BlockStart, + public members: StructDeclarationField[], + ) { + super(); + } + + public getFirstToken(): Token { + return this.name; + } + + public getLastToken(): Token { + if (this.members.length > 0) { + return this.members[this.members.length-1].getLastToken(); + } + return this.blockStart; + } + +} + +export class EnumDeclarationTupleElement extends SyntaxBase { + + public readonly kind = SyntaxKind.EnumDeclarationTupleElement; + + public constructor( + public name: IdentifierAlt, + public elements: TypeExpression[], + ) { + super(); + } + + public getFirstToken(): Token { + return this.name; + } + + public getLastToken(): Token { + if (this.elements.length > 0) { + return this.elements[this.elements.length-1].getLastToken(); + } + return this.name; + } + +} + +export type EnumDeclarationElement + = EnumDeclarationStructElement + | EnumDeclarationTupleElement + +export class EnumDeclaration extends SyntaxBase { + + public readonly kind = SyntaxKind.EnumDeclaration; + + public constructor( + public pubKeyword: PubKeyword | null, + public enumKeyword: EnumKeyword, + public name: IdentifierAlt, + public members: EnumDeclarationElement[] | null, + ) { + super(); + } + + public getFirstToken(): Token { + if (this.pubKeyword !== null) { + return this.pubKeyword; + } + return this.enumKeyword; + } + + public getLastToken(): Token { + if (this.members !== null && this.members.length > 0) { + return this.members[this.members.length-1].getLastToken(); + } + return this.name; + } + +} + export class StructDeclarationField extends SyntaxBase { public readonly kind = SyntaxKind.StructDeclarationField; @@ -1584,6 +1683,7 @@ export class StructDeclaration extends SyntaxBase { public readonly kind = SyntaxKind.StructDeclaration; public constructor( + public pubKeyword: PubKeyword | null, public structKeyword: StructKeyword, public name: IdentifierAlt, public members: StructDeclarationField[] | null, @@ -1592,6 +1692,9 @@ export class StructDeclaration extends SyntaxBase { } public getFirstToken(): Token { + if (this.pubKeyword !== null) { + return this.pubKeyword; + } return this.structKeyword; } @@ -1769,6 +1872,7 @@ export type Declaration = LetDeclaration | ImportDeclaration | StructDeclaration + | EnumDeclaration export class Initializer extends SyntaxBase { diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 2aa71ca9f..5ea4c1749 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -70,6 +70,7 @@ const DESCRIPTIONS: Partial> = { [SyntaxKind.RBrace]: "'}'", [SyntaxKind.LBracket]: "'['", [SyntaxKind.RBracket]: "']'", + [SyntaxKind.IdentifierAlt]: 'an identifier starting with an uppercase letter', [SyntaxKind.ConstantExpression]: 'a constant expression', [SyntaxKind.ReferenceExpression]: 'a reference expression', [SyntaxKind.LineFoldEnd]: 'the end of the current line-fold', diff --git a/src/parser.ts b/src/parser.ts index 0c4cf0785..aa910df68 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -48,6 +48,9 @@ import { IdentifierAlt, WrappedOperator, ArrowTypeExpression, + EnumDeclarationStructElement, + EnumDeclaration, + EnumDeclarationTupleElement, } from "./cst" import { Stream } from "./util"; @@ -407,8 +410,76 @@ export class Parser { return this.parseBinaryOperatorAfterExpr(lhs, 0); } + public parseEnumDeclaration(): EnumDeclaration { + let pubKeyword = null; + let t0 = this.getToken(); + if (t0.kind == SyntaxKind.PubKeyword) { + pubKeyword = t0; + t0 = this.getToken(); + } + if (t0.kind !== SyntaxKind.EnumKeyword) { + this.raiseParseError(t0, [ SyntaxKind.EnumKeyword ]); + } + const name = this.expectToken(SyntaxKind.IdentifierAlt); + const t1 = this.peekToken(); + let members = null; + if (t1.kind === SyntaxKind.BlockStart) { + members = []; + this.getToken(); + for (;;) { + const t2 = this.peekToken(); + if (t2.kind === SyntaxKind.BlockEnd) { + this.getToken(); + break; + } + const name = this.expectToken(SyntaxKind.IdentifierAlt); + const t3 = this.peekToken(); + let member; + if (t3.kind === SyntaxKind.BlockStart) { + this.getToken(); + const members = []; + for (;;) { + const name = this.expectToken(SyntaxKind.Identifier); + const colon = this.expectToken(SyntaxKind.Colon); + const typeExpr = this.parseTypeExpression(); + members.push(new StructDeclarationField(name, colon, typeExpr)); + const t4 = this.peekToken(); + if (t4.kind === SyntaxKind.BlockEnd) { + this.getToken(); + break; + } + } + member = new EnumDeclarationStructElement(name, t3, members); + } else { + const typeExps = []; + for (;;) { + const t3 = this.peekToken(); + if (t3.kind === SyntaxKind.LineFoldEnd) { + break; + } + const typeExpr = this.parsePrimitiveTypeExpression(); + typeExps.push(typeExpr); + } + member = new EnumDeclarationTupleElement(name, typeExps); + } + members.push(member); + this.expectToken(SyntaxKind.LineFoldEnd); + } + } + this.expectToken(SyntaxKind.LineFoldEnd); + return new EnumDeclaration(pubKeyword, t0, name, members); + } + public parseStructDeclaration(): StructDeclaration { - const structKeyword = this.expectToken(SyntaxKind.StructKeyword); + let pubKeyword = null; + let t0 = this.getToken(); + if (t0.kind === SyntaxKind.PubKeyword) { + pubKeyword = t0; + t0 = this.getToken(); + } + if (t0.kind !== SyntaxKind.StructKeyword) { + this.raiseParseError(t0, [ SyntaxKind.StructKeyword ]); + } const name = this.expectToken(SyntaxKind.IdentifierAlt); const t2 = this.peekToken() let members = null; @@ -430,7 +501,7 @@ export class Parser { } } this.expectToken(SyntaxKind.LineFoldEnd); - return new StructDeclaration(structKeyword, name, members); + return new StructDeclaration(pubKeyword, t0, name, members); } private parsePatternStartingWithConstructor() { @@ -731,6 +802,8 @@ export class Parser { return this.parseImportDeclaration(); case SyntaxKind.StructKeyword: return this.parseStructDeclaration(); + case SyntaxKind.EnumKeyword: + return this.parseEnumDeclaration(); case SyntaxKind.IfKeyword: return this.parseIfStatement(); default: diff --git a/src/scanner.ts b/src/scanner.ts index 8b2459eb5..828cabe94 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -36,6 +36,7 @@ import { IfKeyword, StructKeyword, RArrow, + EnumKeyword, } from "./cst" import { Diagnostics, UnexpectedCharDiagnostic } from "./diagnostics" import { Stream, BufferedStream, assert } from "./util"; @@ -356,6 +357,7 @@ export class Scanner extends BufferedStream { case 'else': return new ElseKeyword(startPos); case 'elif': return new ElifKeyword(startPos); case 'struct': return new StructKeyword(startPos); + case 'enum': return new EnumKeyword(startPos); default: if (isUpper(text[0])) { return new IdentifierAlt(text, startPos);