From def4adba408f28a97268403479d2df681fa2c06b Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Sun, 24 May 2020 11:17:56 +0200 Subject: [PATCH] Re-write the type checker to be more robust --- spec/ast.txt | 21 ++-- src/ast.d.ts | 252 +++++++++++++++++++++-------------------- src/checker.ts | 299 +++++++++++++++++++++++++++++++++++++++---------- src/common.ts | 2 +- src/parser.ts | 85 +++++++++++--- src/scanner.ts | 9 +- src/types.ts | 28 +++++ src/util.ts | 2 + 8 files changed, 483 insertions(+), 215 deletions(-) diff --git a/spec/ast.txt b/spec/ast.txt index 5f497a3eb..c9ed59a2f 100644 --- a/spec/ast.txt +++ b/spec/ast.txt @@ -36,16 +36,17 @@ node BoltAssignment > BoltToken { operator: Option, } -node BoltComma > BoltToken; -node BoltSemi > BoltToken; -node BoltColon > BoltToken; -node BoltDot > BoltToken; -node BoltDotDot > BoltToken; -node BoltRArrow > BoltToken; -node BoltLArrow > BoltToken; -node BoltEqSign > BoltToken; -node BoltGtSign > BoltToken; -node BoltLtSign > BoltToken; +node BoltComma > BoltToken; +node BoltSemi > BoltToken; +node BoltColon > BoltToken; +node BoltDot > BoltToken; +node BoltDotDot > BoltToken; +node BoltRArrow > BoltToken; +node BoltRArrowAlt > BoltToken; +node BoltLArrow > BoltToken; +node BoltEqSign > BoltToken; +node BoltGtSign > BoltToken; +node BoltLtSign > BoltToken; node BoltKeyword; diff --git a/src/ast.d.ts b/src/ast.d.ts index 915b1d5b3..498ffde9f 100644 --- a/src/ast.d.ts +++ b/src/ast.d.ts @@ -13,127 +13,128 @@ export const enum SyntaxKind { BoltDot = 15, BoltDotDot = 16, BoltRArrow = 17, - BoltLArrow = 18, - BoltEqSign = 19, - BoltGtSign = 20, - BoltLtSign = 21, - BoltFnKeyword = 23, - BoltForeignKeyword = 24, - BoltForKeyword = 25, - BoltLetKeyword = 26, - BoltReturnKeyword = 27, - BoltLoopKeyword = 28, - BoltYieldKeyword = 29, - BoltMatchKeyword = 30, - BoltImportKeyword = 31, - BoltPubKeyword = 32, - BoltModKeyword = 33, - BoltMutKeyword = 34, - BoltEnumKeyword = 35, - BoltStructKeyword = 36, - BoltTypeKeyword = 37, - BoltTraitKeyword = 38, - BoltImplKeyword = 39, - BoltParenthesized = 41, - BoltBraced = 42, - BoltBracketed = 43, - BoltSourceFile = 44, - BoltQualName = 45, - BoltReferenceTypeExpression = 47, - BoltTypeParameter = 48, - BoltBindPattern = 50, - BoltTypePattern = 51, - BoltExpressionPattern = 52, - BoltTuplePatternElement = 53, - BoltTuplePattern = 54, - BoltRecordPatternField = 55, - BoltRecordPattern = 56, - BoltReferenceExpression = 58, - BoltCallExpression = 59, - BoltYieldExpression = 60, - BoltMatchArm = 61, - BoltMatchExpression = 62, - BoltCase = 63, - BoltCaseExpression = 64, - BoltBlockExpression = 65, - BoltConstantExpression = 66, - BoltReturnStatement = 68, - BoltResumeStatement = 69, - BoltExpressionStatement = 70, - BoltParameter = 71, - BoltModule = 75, - BoltFunctionDeclaration = 77, - BoltVariableDeclaration = 78, - BoltPlainImportSymbol = 80, - BoltImportDeclaration = 81, - BoltTraitDeclaration = 82, - BoltImplDeclaration = 83, - BoltTypeAliasDeclaration = 84, - BoltRecordField = 86, - BoltRecordDeclaration = 87, - BoltMacroCall = 89, - JSOperator = 92, - JSIdentifier = 93, - JSString = 94, - JSInteger = 95, - JSFromKeyword = 96, - JSReturnKeyword = 97, - JSTryKeyword = 98, - JSFinallyKeyword = 99, - JSCatchKeyword = 100, - JSImportKeyword = 101, - JSAsKeyword = 102, - JSConstKeyword = 103, - JSLetKeyword = 104, - JSExportKeyword = 105, - JSFunctionKeyword = 106, - JSWhileKeyword = 107, - JSForKeyword = 108, - JSCloseBrace = 109, - JSCloseBracket = 110, - JSCloseParen = 111, - JSOpenBrace = 112, - JSOpenBracket = 113, - JSOpenParen = 114, - JSSemi = 115, - JSComma = 116, - JSDot = 117, - JSDotDotDot = 118, - JSMulOp = 119, - JSAddOp = 120, - JSDivOp = 121, - JSSubOp = 122, - JSLtOp = 123, - JSGtOp = 124, - JSBOrOp = 125, - JSBXorOp = 126, - JSBAndOp = 127, - JSBNotOp = 128, - JSNotOp = 129, - JSBindPattern = 131, - JSConstantExpression = 133, - JSMemberExpression = 134, - JSCallExpression = 135, - JSBinaryExpression = 136, - JSUnaryExpression = 137, - JSNewExpression = 138, - JSSequenceExpression = 139, - JSConditionalExpression = 140, - JSLiteralExpression = 142, - JSReferenceExpression = 143, - JSCatchBlock = 146, - JSTryCatchStatement = 147, - JSExpressionStatement = 148, - JSConditionalStatement = 149, - JSReturnStatement = 150, - JSParameter = 151, - JSImportStarBinding = 155, - JSImportAsBinding = 156, - JSImportDeclaration = 157, - JSFunctionDeclaration = 158, - JSArrowFunctionDeclaration = 159, - JSLetDeclaration = 160, - JSSourceFile = 161, + BoltRArrowAlt = 18, + BoltLArrow = 19, + BoltEqSign = 20, + BoltGtSign = 21, + BoltLtSign = 22, + BoltFnKeyword = 24, + BoltForeignKeyword = 25, + BoltForKeyword = 26, + BoltLetKeyword = 27, + BoltReturnKeyword = 28, + BoltLoopKeyword = 29, + BoltYieldKeyword = 30, + BoltMatchKeyword = 31, + BoltImportKeyword = 32, + BoltPubKeyword = 33, + BoltModKeyword = 34, + BoltMutKeyword = 35, + BoltEnumKeyword = 36, + BoltStructKeyword = 37, + BoltTypeKeyword = 38, + BoltTraitKeyword = 39, + BoltImplKeyword = 40, + BoltParenthesized = 42, + BoltBraced = 43, + BoltBracketed = 44, + BoltSourceFile = 45, + BoltQualName = 46, + BoltReferenceTypeExpression = 48, + BoltTypeParameter = 49, + BoltBindPattern = 51, + BoltTypePattern = 52, + BoltExpressionPattern = 53, + BoltTuplePatternElement = 54, + BoltTuplePattern = 55, + BoltRecordPatternField = 56, + BoltRecordPattern = 57, + BoltReferenceExpression = 59, + BoltCallExpression = 60, + BoltYieldExpression = 61, + BoltMatchArm = 62, + BoltMatchExpression = 63, + BoltCase = 64, + BoltCaseExpression = 65, + BoltBlockExpression = 66, + BoltConstantExpression = 67, + BoltReturnStatement = 69, + BoltResumeStatement = 70, + BoltExpressionStatement = 71, + BoltParameter = 72, + BoltModule = 76, + BoltFunctionDeclaration = 78, + BoltVariableDeclaration = 79, + BoltPlainImportSymbol = 81, + BoltImportDeclaration = 82, + BoltTraitDeclaration = 83, + BoltImplDeclaration = 84, + BoltTypeAliasDeclaration = 85, + BoltRecordField = 87, + BoltRecordDeclaration = 88, + BoltMacroCall = 90, + JSOperator = 93, + JSIdentifier = 94, + JSString = 95, + JSInteger = 96, + JSFromKeyword = 97, + JSReturnKeyword = 98, + JSTryKeyword = 99, + JSFinallyKeyword = 100, + JSCatchKeyword = 101, + JSImportKeyword = 102, + JSAsKeyword = 103, + JSConstKeyword = 104, + JSLetKeyword = 105, + JSExportKeyword = 106, + JSFunctionKeyword = 107, + JSWhileKeyword = 108, + JSForKeyword = 109, + JSCloseBrace = 110, + JSCloseBracket = 111, + JSCloseParen = 112, + JSOpenBrace = 113, + JSOpenBracket = 114, + JSOpenParen = 115, + JSSemi = 116, + JSComma = 117, + JSDot = 118, + JSDotDotDot = 119, + JSMulOp = 120, + JSAddOp = 121, + JSDivOp = 122, + JSSubOp = 123, + JSLtOp = 124, + JSGtOp = 125, + JSBOrOp = 126, + JSBXorOp = 127, + JSBAndOp = 128, + JSBNotOp = 129, + JSNotOp = 130, + JSBindPattern = 132, + JSConstantExpression = 134, + JSMemberExpression = 135, + JSCallExpression = 136, + JSBinaryExpression = 137, + JSUnaryExpression = 138, + JSNewExpression = 139, + JSSequenceExpression = 140, + JSConditionalExpression = 141, + JSLiteralExpression = 143, + JSReferenceExpression = 144, + JSCatchBlock = 147, + JSTryCatchStatement = 148, + JSExpressionStatement = 149, + JSConditionalStatement = 150, + JSReturnStatement = 151, + JSParameter = 152, + JSImportStarBinding = 156, + JSImportAsBinding = 157, + JSImportDeclaration = 158, + JSFunctionDeclaration = 159, + JSArrowFunctionDeclaration = 160, + JSLetDeclaration = 161, + JSSourceFile = 162, } @@ -175,6 +176,7 @@ export type BoltToken | BoltDot | BoltDotDot | BoltRArrow + | BoltRArrowAlt | BoltLArrow | BoltEqSign | BoltGtSign @@ -255,6 +257,10 @@ export interface BoltRArrow extends SyntaxBase { kind: SyntaxKind.BoltRArrow; } +export interface BoltRArrowAlt extends SyntaxBase { + kind: SyntaxKind.BoltRArrowAlt; +} + export interface BoltLArrow extends SyntaxBase { kind: SyntaxKind.BoltLArrow; } @@ -1085,6 +1091,7 @@ export type BoltSyntax | BoltDot | BoltDotDot | BoltRArrow + | BoltRArrowAlt | BoltLArrow | BoltEqSign | BoltGtSign @@ -1225,6 +1232,7 @@ export type Syntax | BoltDot | BoltDotDot | BoltRArrow + | BoltRArrowAlt | BoltLArrow | BoltEqSign | BoltGtSign @@ -1363,6 +1371,7 @@ export function createBoltColon(span?: TextSpan | null): BoltColon; export function createBoltDot(span?: TextSpan | null): BoltDot; export function createBoltDotDot(span?: TextSpan | null): BoltDotDot; export function createBoltRArrow(span?: TextSpan | null): BoltRArrow; +export function createBoltRArrowAlt(span?: TextSpan | null): BoltRArrowAlt; export function createBoltLArrow(span?: TextSpan | null): BoltLArrow; export function createBoltEqSign(span?: TextSpan | null): BoltEqSign; export function createBoltGtSign(span?: TextSpan | null): BoltGtSign; @@ -1500,6 +1509,7 @@ export function isBoltColon(value: any): value is BoltColon; export function isBoltDot(value: any): value is BoltDot; export function isBoltDotDot(value: any): value is BoltDotDot; export function isBoltRArrow(value: any): value is BoltRArrow; +export function isBoltRArrowAlt(value: any): value is BoltRArrowAlt; export function isBoltLArrow(value: any): value is BoltLArrow; export function isBoltEqSign(value: any): value is BoltEqSign; export function isBoltGtSign(value: any): value is BoltGtSign; diff --git a/src/checker.ts b/src/checker.ts index df46bcd37..9d5b01fcf 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -23,7 +23,25 @@ * Note that the `pub`-keyword is not present on `MyType1`. */ -import {Syntax, SyntaxKind, BoltReferenceExpression, BoltDeclaration, BoltSourceFile, BoltSyntax, BoltReferenceTypeExpression, BoltTypeDeclaration, BoltExpression, BoltFunctionDeclaration, BoltFunctionBodyElement, kindToString, createBoltReferenceTypeExpression, createBoltIdentifier} from "./ast"; +import { + Syntax, + SyntaxKind, + BoltReferenceExpression, + BoltDeclaration, + BoltSourceFile, + BoltSyntax, + BoltReferenceTypeExpression, + BoltTypeDeclaration, + BoltExpression, + BoltFunctionDeclaration, + BoltFunctionBodyElement, + kindToString, + BoltStatement, + BoltTypeExpression, + BoltSourceElement, + isBoltStatement, + isBoltDeclaration +} from "./ast"; import {FastStringMap, memoize, assert} from "./util"; import { DiagnosticPrinter, @@ -34,7 +52,8 @@ import { E_DECLARATION_NOT_FOUND, E_INVALID_ARGUMENTS } from "./diagnostics"; -import {createAnyType, isOpaqueType, createOpaqueType, Type} from "./types"; +import { createAnyType, isOpaqueType, createOpaqueType, Type, createVoidType, createVariantType, isVoidType } from "./types"; +import { getReturnStatementsInFunctionBody } from "./common"; interface SymbolInfo { declarations: BoltDeclaration[]; @@ -78,74 +97,223 @@ export class TypeChecker { public checkSourceFile(node: BoltSourceFile): void { - const refExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceExpression); - for (const refExp of refExps) { - if (this.resolveReferenceExpression(refExp) === null) { - this.diagnostics.add({ - message: E_DECLARATION_NOT_FOUND, - args: { name: refExp.name.name.text }, - severity: 'error', - node: refExp, - }) - } + const self = this; + for (const element of node.elements) { + visitSourceElement(element); } - const typeRefExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceTypeExpression); - for (const typeRefExp of typeRefExps) { - if (this.resolveTypeReferenceExpression(typeRefExp) === null) { - this.diagnostics.add({ - message: E_TYPE_DECLARATION_NOT_FOUND, - args: { name: typeRefExp.name.name.text }, - severity: 'error', - node: typeRefExp, - }) - } - } + function visitExpression(node: BoltExpression) { - const callExps = node.findAllChildrenOfKind(SyntaxKind.BoltCallExpression); + switch (node.kind) { - for (const callExp of callExps) { - - const fnDecls = this.getAllFunctionsInExpression(callExp.operator); - - for (const fnDecl of fnDecls) { - - if (fnDecl.params.length > callExp.operands.length) { - this.diagnostics.add({ - message: E_TOO_FEW_ARGUMENTS_FOR_FUNCTION_CALL, - args: { expected: fnDecl.params.length, actual: callExp.operands.length }, - severity: 'error', - node: callExp, - }) - } - - if (fnDecl.params.length < callExp.operands.length) { - this.diagnostics.add({ - message: E_TOO_MANY_ARGUMENTS_FOR_FUNCTION_CALL, - args: { expected: fnDecl.params.length, actual: callExp.operands.length }, - severity: 'error', - node: callExp, - }) - } - - const paramCount = fnDecl.params.length; - for (let i = 0; i < paramCount; i++) { - const arg = callExp.operands[i]; - const param = fnDecl.params[i]; - let argType = this.getTypeOfNode(arg); - let paramType = this.getTypeOfNode(param); - if (!this.isTypeAssignableTo(argType, paramType)) { - this.diagnostics.add({ - message: E_INVALID_ARGUMENTS, + case SyntaxKind.BoltReferenceExpression: + { + if (self.resolveReferenceExpression(node) === null) { + self.diagnostics.add({ + message: E_DECLARATION_NOT_FOUND, + args: { name: node.name.name.text }, severity: 'error', - args: { name: fnDecl.name.text }, - node: arg, + node: node, + }) + } + break; + } + + case SyntaxKind.BoltCallExpression: + { + + const fnDecls = self.getAllFunctionsInExpression(node.operator); + + for (const fnDecl of fnDecls) { + + if (fnDecl.params.length > node.operands.length) { + + self.diagnostics.add({ + message: E_TOO_FEW_ARGUMENTS_FOR_FUNCTION_CALL, + args: { expected: fnDecl.params.length, actual: node.operands.length }, + severity: 'error', + node: node, + }); + + } else if (fnDecl.params.length < node.operands.length) { + + self.diagnostics.add({ + message: E_TOO_MANY_ARGUMENTS_FOR_FUNCTION_CALL, + args: { expected: fnDecl.params.length, actual: node.operands.length }, + severity: 'error', + node: node, + }); + + } else { + + const paramCount = fnDecl.params.length; + for (let i = 0; i < paramCount; i++) { + const arg = node.operands[i]; + const param = fnDecl.params[i]; + let argType = self.getTypeOfNode(arg); + let paramType = self.getTypeOfNode(param); + if (!self.isTypeAssignableTo(argType, paramType)) { + self.diagnostics.add({ + message: E_INVALID_ARGUMENTS, + severity: 'error', + args: { name: fnDecl.name.text }, + node: arg, + }); + } + } + + } + + } + + break; + } + + default: + throw new Error(`Unknown node of type ${kindToString(node.kind)}.`); + + } + + } + + function visitTypeExpressionn(node: BoltTypeExpression) { + + switch (node.kind) { + + case SyntaxKind.BoltReferenceTypeExpression: + { + if (self.resolveTypeReferenceExpression(node) === null) { + self.diagnostics.add({ + message: E_TYPE_DECLARATION_NOT_FOUND, + args: { name: node.name.name.text }, + severity: 'error', + node: node, + }) + } + break; + } + + default: + throw new Error(`Unknown node of type ${kindToString(node.kind)}.`); + } + } + + function visitDeclaration(node: BoltDeclaration) { + + switch (node.kind) { + + case SyntaxKind.BoltRecordDeclaration: + { + if (node.members !== null) { + for (const member of node.members) { + if (member.kind === SyntaxKind.BoltRecordField) { + visitTypeExpressionn(member.type); + } + } + } + break; + } + + case SyntaxKind.BoltFunctionDeclaration: + { + let fnReturnType: Type = createAnyType(); + + if (node.returnType !== null) { + fnReturnType = self.getTypeOfNode(node.returnType); + } + + if (node.body !== null) { + const returnStmts = getReturnStatementsInFunctionBody(node.body) + const validReturnTypes: Type[] = []; + for (const returnStmt of returnStmts) { + if (returnStmt.value === null) { + if (!isVoidType(fnReturnType)) { + self.diagnostics.add({ + message: E_MUST_RETURN_A_VALUE, + node: returnStmt, + severity: 'error', + }); + } + } else { + checkExpressionMatchesType(returnStmt.value, fnReturnType); + } + //const returnType = self.getTypeOfNode(returnStmt); + //if (!self.isTypeAssignableTo(fnReturnType, returnType)) { + //self.diagnostics.add({ + //severity: 'error', + //node: returnStmt.value !== null ? returnStmt.value : returnStmt, + //args: { left: fnReturnType, right: returnType }, + //message: E_TYPES_NOT_ASSIGNABLE, + //}); + //} else { + //validReturnTypes.push(returnType); + //} + } + } + + // TODO Sort the return types and find the largest types, eliminating types that fall under other types. + // Next, add the resulting types as type hints to `fnReturnType`. + + break; + } + + default: + throw new Error(`Unknown node of type ${kindToString(node.kind)}.`); + + } + + } + + function checkExpressionMatchesType(node: BoltExpression, expectedType: Type) { + switch (node.kind) { + case SyntaxKind.BoltMatchExpression: + { + for (const matchArm of node.arms) { + checkExpressionMatchesType(matchArm.body, expectedType); + } + break; + } + default: + { + const actualType = self.getTypeOfNode(node); + if (!self.isTypeAssignableTo(expectedType, actualType)) { + self.diagnostics.add({ + severity: 'error', + message: E_TYPES_NOT_ASSIGNABLE, + args: { left: expectedType, right: actualType }, + node, }); } + break; } - } + } + function visitStatement(node: BoltStatement) { + switch (node.kind) { + case SyntaxKind.BoltExpressionStatement: + // TODO check for values that should be unwrapped + visitExpression(node.expression); + break; + case SyntaxKind.BoltReturnStatement: + if (node.value !== null) { + visitExpression(node.value); + } + break; + + default: + throw new Error(`Unknown node of type ${kindToString(node.kind)}.`); + } + } + + function visitSourceElement(node: BoltSourceElement) { + if (isBoltStatement(node)) { + visitStatement(node); + } else if (isBoltDeclaration(node)) { + visitDeclaration(node); + } else { + throw new Error(`Unknown node of kind ${kindToString(node)}`); + } } } @@ -185,6 +353,13 @@ export class TypeChecker { } return type; } + case SyntaxKind.BoltReturnStatement: + { + if (node.value === null) { + return createVoidType(); + } + return this.getTypeOfNode(node.value) + } case SyntaxKind.BoltConstantExpression: { let type; @@ -200,6 +375,10 @@ export class TypeChecker { assert(type !== null); return type; } + case SyntaxKind.BoltMatchExpression: + { + return createVariantType(...node.arms.map(arm => this.getTypeOfNode(arm.body))); + } default: throw new Error(`Could not derive type of node ${kindToString(node.kind)}.`); } diff --git a/src/common.ts b/src/common.ts index dc9067b2f..218b4d152 100644 --- a/src/common.ts +++ b/src/common.ts @@ -7,7 +7,7 @@ import { export type BoltFunctionBody = BoltFunctionBodyElement[]; -export function getAllReturnStatements(body: BoltFunctionBody): BoltReturnStatement[] { +export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltReturnStatement[] { const results: BoltReturnStatement[] = []; diff --git a/src/parser.ts b/src/parser.ts index 686d4583a..198dbf44c 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -59,6 +59,11 @@ import { createBoltSourceFile, BoltRecordField, setParents, + BoltMatchExpression, + createBoltMatchArm, + BoltMatchArm, + createBoltMatchExpression, + createBoltExpressionPattern, } from "./ast" import { parseForeignLanguage } from "./foreign" @@ -81,6 +86,13 @@ export function isModifierKeyword(kind: SyntaxKind) { || kind === SyntaxKind.BoltForeignKeyword; } +function assertNoTokens(tokens: BoltTokenStream) { + const t0 = tokens.peek(1); + if (t0.kind !== SyntaxKind.EndOfFile) { + throw new ParseError(t0, [SyntaxKind.EndOfFile]); + } +} + const KIND_EXPRESSION_T0 = [ SyntaxKind.BoltStringLiteral, SyntaxKind.BoltIntegerLiteral, @@ -156,13 +168,6 @@ export class Parser { ] ]); - protected assertEmpty(tokens: BoltTokenStream) { - const t0 = tokens.peek(1); - if (t0.kind !== SyntaxKind.EndOfFile) { - throw new ParseError(t0, [SyntaxKind.EndOfFile]); - } - } - public parse(kind: SyntaxKind, tokens: BoltTokenStream): BoltSyntax { return (this as any)['parse' + kindToString(kind).substring('Bolt'.length)](tokens); } @@ -204,6 +209,17 @@ export class Parser { const t0 = tokens.peek(1); if (t0.kind === SyntaxKind.BoltIdentifier) { return this.parseBindPattern(tokens); + } else if (t0.kind === SyntaxKind.BoltOperator && t0.text === '^') { + tokens.get(); + const refExpr = this.parseReferenceExpression(tokens); + const result = createBoltExpressionPattern(refExpr); + setOrigNodeRange(result, t0, refExpr); + return result; + } else if (KIND_EXPRESSION_T0.indexOf(t0.kind) !== -1) { + const expr = this.parseExpression(tokens); + const result = createBoltExpressionPattern(expr); + setOrigNodeRange(result, expr, expr); + return result; } else { throw new ParseError(t0, [SyntaxKind.BoltIdentifier]) } @@ -328,9 +344,43 @@ export class Parser { return node; } + public parseMatchExpression(tokens: BoltTokenStream): BoltMatchExpression { + const t0 = tokens.get(); + assertToken(t0, SyntaxKind.BoltMatchKeyword); + const expr = this.parseExpression(tokens); + const t1 = tokens.get(); + assertToken(t1, SyntaxKind.BoltBraced); + const innerTokens = createTokenStream(t1); + const matchArms: BoltMatchArm[] = []; + while (true) { + const t2 = innerTokens.peek(); + if (t2.kind === SyntaxKind.EndOfFile) { + break; + } + const pattern = this.parsePattern(innerTokens); + const t3 = innerTokens.get(); + assertToken(t3, SyntaxKind.BoltRArrowAlt); + const expression = this.parseExpression(innerTokens); + const arm = createBoltMatchArm(pattern, expression); + setOrigNodeRange(arm, pattern, expression); + matchArms.push(arm); + const t4 = tokens.peek(); + if (t4.kind === SyntaxKind.EndOfFile) { + break; + } + assertToken(t4, SyntaxKind.BoltComma); + tokens.get(); + } + const result = createBoltMatchExpression(expr, matchArms); + setOrigNodeRange(result, t0, t1); + return result; + } + protected parsePrimitiveExpression(tokens: BoltTokenStream): BoltExpression { const t0 = tokens.peek(); - if (t0.kind === SyntaxKind.BoltIntegerLiteral || t0.kind === SyntaxKind.BoltStringLiteral) { + if (t0.kind === SyntaxKind.BoltMatchKeyword) { + return this.parseMatchExpression(tokens); + } else if (t0.kind === SyntaxKind.BoltIntegerLiteral || t0.kind === SyntaxKind.BoltStringLiteral) { return this.parseConstantExpression(tokens); } else if (t0.kind === SyntaxKind.BoltIdentifier) { return this.parseReferenceExpression(tokens); @@ -714,7 +764,7 @@ export class Parser { tokens.get(); const innerTokens = createTokenStream(t0); const param = this.parseParameter(innerTokens, i++) - this.assertEmpty(innerTokens); + assertNoTokens(innerTokens); return param } else { throw new ParseError(t0, [SyntaxKind.BoltIdentifier, SyntaxKind.BoltParenthesized]) @@ -794,7 +844,7 @@ export class Parser { tokens.get(); switch (target) { case "Bolt": - body = this.parseStatements(tokens); + body = this.parseStatements(createTokenStream(t3)); break; default: body = parseForeignLanguage(target, t3.text, t3.span!.file, t3.span!.start); @@ -870,8 +920,7 @@ export class Parser { let t0 = tokens.peek(1); let i = 1; if (t0.kind === SyntaxKind.BoltPubKeyword) { - i += 1; - t0 = tokens.peek(i); + t0 = tokens.peek(++i); if (t0.kind !== SyntaxKind.BoltForeignKeyword) { if (KIND_DECLARATION_KEYWORD.indexOf(t0.kind) === -1) { throw new ParseError(t0, KIND_DECLARATION_KEYWORD); @@ -918,8 +967,8 @@ export class Parser { } if (t0.kind === SyntaxKind.BoltForeignKeyword) { mustBeFunctionOrVariable = true; - i++; - t0 = tokens.peek(++i); + i += 2; + t0 = tokens.peek(i); } if (mustBeFunctionOrVariable && t0.kind !== SyntaxKind.BoltStructKeyword @@ -1015,11 +1064,11 @@ export class Parser { const operator = this.parsePrimitiveExpression(tokens) - const t2 = tokens.get(); - if (t2.kind === SyntaxKind.EndOfFile) { + const t2 = tokens.peek(); + if (t2.kind !== SyntaxKind.BoltParenthesized) { return operator; } - assertToken(t2, SyntaxKind.BoltParenthesized); + tokens.get(); const args: BoltExpression[] = [] const innerTokens = createTokenStream(t2); @@ -1197,4 +1246,4 @@ export function parseSourceFile(filepath: string): BoltSourceFile { setParents(sourceFile); return sourceFile; } - +; diff --git a/src/scanner.ts b/src/scanner.ts index bb240b324..42047f604 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -11,9 +11,8 @@ import { setParents, SyntaxKind, BoltToken, - BoltSentence, + createBoltRArrowAlt, createEndOfFile, - createBoltSentence, createBoltIdentifier, createBoltRArrow, createBoltOperator, @@ -39,16 +38,14 @@ import { createBoltFnKeyword, createBoltLArrow, createBoltDotDot, - createJSIdentifier, - JSToken, createBoltLtSign, createBoltGtSign, createBoltModKeyword, createBoltTypeKeyword, createBoltForKeyword, - createBoltTraitDeclaration, createBoltTraitKeyword, createBoltImplKeyword, + createBoltMatchKeyword, } from "./ast" export enum PunctType { @@ -297,6 +294,7 @@ export class Scanner { case 'mod': return createBoltModKeyword(span); case 'fn': return createBoltFnKeyword(span); case 'return': return createBoltReturnKeyword(span); + case 'match': return createBoltMatchKeyword(span); case 'yield': return createBoltYieldKeyword(span); case 'for': return createBoltForKeyword(span); case 'trait': return createBoltTraitKeyword(span); @@ -318,6 +316,7 @@ export class Scanner { switch (text) { case '->': return createBoltRArrow(span); + case '=>': return createBoltRArrowAlt(span); case '<-': return createBoltLArrow(span); case '<': return createBoltLtSign(span); case '>': return createBoltGtSign(span); diff --git a/src/types.ts b/src/types.ts index 2a9a584a8..57259b0d4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -98,6 +98,10 @@ export class VariantType extends TypeBase { } +export function createVariantType(...elementTypes: Type[]): VariantType { + return new VariantType(elementTypes); +} + export function isVariantType(value: any): value is VariantType { return value instanceof VariantType; } @@ -140,6 +144,30 @@ export class TupleType extends TypeBase { } +export function createTupleType(...elementTypes: Type[]) { + return new TupleType(elementTypes); +} + +export function isTupleType(value: any): value is TupleType { + return value.kind === TypeKind.TupleType; +} + +export function createVoidType() { + return createTupleType(); +} + +export function isVoidType(value: any) { + return isTupleType(value) && value.elementTypes.length === 0; +} + +export function narrowType(outer: Type, inner: Type): Type { + if (isAnyType(outer) || isNeverType(inner)) { + return inner; + } + // TODO cover the other cases + return outer; +} + export function intersectTypes(a: Type, b: Type): Type { if (isNeverType(a) || isNeverType(b)) { return new NeverType(); diff --git a/src/util.ts b/src/util.ts index 9445eeea5..6c77a71f0 100644 --- a/src/util.ts +++ b/src/util.ts @@ -351,6 +351,8 @@ export function describeKind(kind: SyntaxKind): string { return "'for'"; case SyntaxKind.JSTryKeyword: return "'try'"; + case SyntaxKind.BoltRArrowAlt: + return "'=>'"; default: throw new Error(`failed to describe ${kindToString(kind)}`) }