Re-write the type checker to be more robust

This commit is contained in:
Sam Vervaeck 2020-05-24 11:17:56 +02:00
parent 9ef9ef82d6
commit def4adba40
8 changed files with 483 additions and 215 deletions

View file

@ -36,16 +36,17 @@ node BoltAssignment > BoltToken {
operator: Option<String>, operator: Option<String>,
} }
node BoltComma > BoltToken; node BoltComma > BoltToken;
node BoltSemi > BoltToken; node BoltSemi > BoltToken;
node BoltColon > BoltToken; node BoltColon > BoltToken;
node BoltDot > BoltToken; node BoltDot > BoltToken;
node BoltDotDot > BoltToken; node BoltDotDot > BoltToken;
node BoltRArrow > BoltToken; node BoltRArrow > BoltToken;
node BoltLArrow > BoltToken; node BoltRArrowAlt > BoltToken;
node BoltEqSign > BoltToken; node BoltLArrow > BoltToken;
node BoltGtSign > BoltToken; node BoltEqSign > BoltToken;
node BoltLtSign > BoltToken; node BoltGtSign > BoltToken;
node BoltLtSign > BoltToken;
node BoltKeyword; node BoltKeyword;

252
src/ast.d.ts vendored
View file

@ -13,127 +13,128 @@ export const enum SyntaxKind {
BoltDot = 15, BoltDot = 15,
BoltDotDot = 16, BoltDotDot = 16,
BoltRArrow = 17, BoltRArrow = 17,
BoltLArrow = 18, BoltRArrowAlt = 18,
BoltEqSign = 19, BoltLArrow = 19,
BoltGtSign = 20, BoltEqSign = 20,
BoltLtSign = 21, BoltGtSign = 21,
BoltFnKeyword = 23, BoltLtSign = 22,
BoltForeignKeyword = 24, BoltFnKeyword = 24,
BoltForKeyword = 25, BoltForeignKeyword = 25,
BoltLetKeyword = 26, BoltForKeyword = 26,
BoltReturnKeyword = 27, BoltLetKeyword = 27,
BoltLoopKeyword = 28, BoltReturnKeyword = 28,
BoltYieldKeyword = 29, BoltLoopKeyword = 29,
BoltMatchKeyword = 30, BoltYieldKeyword = 30,
BoltImportKeyword = 31, BoltMatchKeyword = 31,
BoltPubKeyword = 32, BoltImportKeyword = 32,
BoltModKeyword = 33, BoltPubKeyword = 33,
BoltMutKeyword = 34, BoltModKeyword = 34,
BoltEnumKeyword = 35, BoltMutKeyword = 35,
BoltStructKeyword = 36, BoltEnumKeyword = 36,
BoltTypeKeyword = 37, BoltStructKeyword = 37,
BoltTraitKeyword = 38, BoltTypeKeyword = 38,
BoltImplKeyword = 39, BoltTraitKeyword = 39,
BoltParenthesized = 41, BoltImplKeyword = 40,
BoltBraced = 42, BoltParenthesized = 42,
BoltBracketed = 43, BoltBraced = 43,
BoltSourceFile = 44, BoltBracketed = 44,
BoltQualName = 45, BoltSourceFile = 45,
BoltReferenceTypeExpression = 47, BoltQualName = 46,
BoltTypeParameter = 48, BoltReferenceTypeExpression = 48,
BoltBindPattern = 50, BoltTypeParameter = 49,
BoltTypePattern = 51, BoltBindPattern = 51,
BoltExpressionPattern = 52, BoltTypePattern = 52,
BoltTuplePatternElement = 53, BoltExpressionPattern = 53,
BoltTuplePattern = 54, BoltTuplePatternElement = 54,
BoltRecordPatternField = 55, BoltTuplePattern = 55,
BoltRecordPattern = 56, BoltRecordPatternField = 56,
BoltReferenceExpression = 58, BoltRecordPattern = 57,
BoltCallExpression = 59, BoltReferenceExpression = 59,
BoltYieldExpression = 60, BoltCallExpression = 60,
BoltMatchArm = 61, BoltYieldExpression = 61,
BoltMatchExpression = 62, BoltMatchArm = 62,
BoltCase = 63, BoltMatchExpression = 63,
BoltCaseExpression = 64, BoltCase = 64,
BoltBlockExpression = 65, BoltCaseExpression = 65,
BoltConstantExpression = 66, BoltBlockExpression = 66,
BoltReturnStatement = 68, BoltConstantExpression = 67,
BoltResumeStatement = 69, BoltReturnStatement = 69,
BoltExpressionStatement = 70, BoltResumeStatement = 70,
BoltParameter = 71, BoltExpressionStatement = 71,
BoltModule = 75, BoltParameter = 72,
BoltFunctionDeclaration = 77, BoltModule = 76,
BoltVariableDeclaration = 78, BoltFunctionDeclaration = 78,
BoltPlainImportSymbol = 80, BoltVariableDeclaration = 79,
BoltImportDeclaration = 81, BoltPlainImportSymbol = 81,
BoltTraitDeclaration = 82, BoltImportDeclaration = 82,
BoltImplDeclaration = 83, BoltTraitDeclaration = 83,
BoltTypeAliasDeclaration = 84, BoltImplDeclaration = 84,
BoltRecordField = 86, BoltTypeAliasDeclaration = 85,
BoltRecordDeclaration = 87, BoltRecordField = 87,
BoltMacroCall = 89, BoltRecordDeclaration = 88,
JSOperator = 92, BoltMacroCall = 90,
JSIdentifier = 93, JSOperator = 93,
JSString = 94, JSIdentifier = 94,
JSInteger = 95, JSString = 95,
JSFromKeyword = 96, JSInteger = 96,
JSReturnKeyword = 97, JSFromKeyword = 97,
JSTryKeyword = 98, JSReturnKeyword = 98,
JSFinallyKeyword = 99, JSTryKeyword = 99,
JSCatchKeyword = 100, JSFinallyKeyword = 100,
JSImportKeyword = 101, JSCatchKeyword = 101,
JSAsKeyword = 102, JSImportKeyword = 102,
JSConstKeyword = 103, JSAsKeyword = 103,
JSLetKeyword = 104, JSConstKeyword = 104,
JSExportKeyword = 105, JSLetKeyword = 105,
JSFunctionKeyword = 106, JSExportKeyword = 106,
JSWhileKeyword = 107, JSFunctionKeyword = 107,
JSForKeyword = 108, JSWhileKeyword = 108,
JSCloseBrace = 109, JSForKeyword = 109,
JSCloseBracket = 110, JSCloseBrace = 110,
JSCloseParen = 111, JSCloseBracket = 111,
JSOpenBrace = 112, JSCloseParen = 112,
JSOpenBracket = 113, JSOpenBrace = 113,
JSOpenParen = 114, JSOpenBracket = 114,
JSSemi = 115, JSOpenParen = 115,
JSComma = 116, JSSemi = 116,
JSDot = 117, JSComma = 117,
JSDotDotDot = 118, JSDot = 118,
JSMulOp = 119, JSDotDotDot = 119,
JSAddOp = 120, JSMulOp = 120,
JSDivOp = 121, JSAddOp = 121,
JSSubOp = 122, JSDivOp = 122,
JSLtOp = 123, JSSubOp = 123,
JSGtOp = 124, JSLtOp = 124,
JSBOrOp = 125, JSGtOp = 125,
JSBXorOp = 126, JSBOrOp = 126,
JSBAndOp = 127, JSBXorOp = 127,
JSBNotOp = 128, JSBAndOp = 128,
JSNotOp = 129, JSBNotOp = 129,
JSBindPattern = 131, JSNotOp = 130,
JSConstantExpression = 133, JSBindPattern = 132,
JSMemberExpression = 134, JSConstantExpression = 134,
JSCallExpression = 135, JSMemberExpression = 135,
JSBinaryExpression = 136, JSCallExpression = 136,
JSUnaryExpression = 137, JSBinaryExpression = 137,
JSNewExpression = 138, JSUnaryExpression = 138,
JSSequenceExpression = 139, JSNewExpression = 139,
JSConditionalExpression = 140, JSSequenceExpression = 140,
JSLiteralExpression = 142, JSConditionalExpression = 141,
JSReferenceExpression = 143, JSLiteralExpression = 143,
JSCatchBlock = 146, JSReferenceExpression = 144,
JSTryCatchStatement = 147, JSCatchBlock = 147,
JSExpressionStatement = 148, JSTryCatchStatement = 148,
JSConditionalStatement = 149, JSExpressionStatement = 149,
JSReturnStatement = 150, JSConditionalStatement = 150,
JSParameter = 151, JSReturnStatement = 151,
JSImportStarBinding = 155, JSParameter = 152,
JSImportAsBinding = 156, JSImportStarBinding = 156,
JSImportDeclaration = 157, JSImportAsBinding = 157,
JSFunctionDeclaration = 158, JSImportDeclaration = 158,
JSArrowFunctionDeclaration = 159, JSFunctionDeclaration = 159,
JSLetDeclaration = 160, JSArrowFunctionDeclaration = 160,
JSSourceFile = 161, JSLetDeclaration = 161,
JSSourceFile = 162,
} }
@ -175,6 +176,7 @@ export type BoltToken
| BoltDot | BoltDot
| BoltDotDot | BoltDotDot
| BoltRArrow | BoltRArrow
| BoltRArrowAlt
| BoltLArrow | BoltLArrow
| BoltEqSign | BoltEqSign
| BoltGtSign | BoltGtSign
@ -255,6 +257,10 @@ export interface BoltRArrow extends SyntaxBase<SyntaxKind.BoltRArrow> {
kind: SyntaxKind.BoltRArrow; kind: SyntaxKind.BoltRArrow;
} }
export interface BoltRArrowAlt extends SyntaxBase<SyntaxKind.BoltRArrowAlt> {
kind: SyntaxKind.BoltRArrowAlt;
}
export interface BoltLArrow extends SyntaxBase<SyntaxKind.BoltLArrow> { export interface BoltLArrow extends SyntaxBase<SyntaxKind.BoltLArrow> {
kind: SyntaxKind.BoltLArrow; kind: SyntaxKind.BoltLArrow;
} }
@ -1085,6 +1091,7 @@ export type BoltSyntax
| BoltDot | BoltDot
| BoltDotDot | BoltDotDot
| BoltRArrow | BoltRArrow
| BoltRArrowAlt
| BoltLArrow | BoltLArrow
| BoltEqSign | BoltEqSign
| BoltGtSign | BoltGtSign
@ -1225,6 +1232,7 @@ export type Syntax
| BoltDot | BoltDot
| BoltDotDot | BoltDotDot
| BoltRArrow | BoltRArrow
| BoltRArrowAlt
| BoltLArrow | BoltLArrow
| BoltEqSign | BoltEqSign
| BoltGtSign | BoltGtSign
@ -1363,6 +1371,7 @@ export function createBoltColon(span?: TextSpan | null): BoltColon;
export function createBoltDot(span?: TextSpan | null): BoltDot; export function createBoltDot(span?: TextSpan | null): BoltDot;
export function createBoltDotDot(span?: TextSpan | null): BoltDotDot; export function createBoltDotDot(span?: TextSpan | null): BoltDotDot;
export function createBoltRArrow(span?: TextSpan | null): BoltRArrow; export function createBoltRArrow(span?: TextSpan | null): BoltRArrow;
export function createBoltRArrowAlt(span?: TextSpan | null): BoltRArrowAlt;
export function createBoltLArrow(span?: TextSpan | null): BoltLArrow; export function createBoltLArrow(span?: TextSpan | null): BoltLArrow;
export function createBoltEqSign(span?: TextSpan | null): BoltEqSign; export function createBoltEqSign(span?: TextSpan | null): BoltEqSign;
export function createBoltGtSign(span?: TextSpan | null): BoltGtSign; 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 isBoltDot(value: any): value is BoltDot;
export function isBoltDotDot(value: any): value is BoltDotDot; export function isBoltDotDot(value: any): value is BoltDotDot;
export function isBoltRArrow(value: any): value is BoltRArrow; 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 isBoltLArrow(value: any): value is BoltLArrow;
export function isBoltEqSign(value: any): value is BoltEqSign; export function isBoltEqSign(value: any): value is BoltEqSign;
export function isBoltGtSign(value: any): value is BoltGtSign; export function isBoltGtSign(value: any): value is BoltGtSign;

View file

@ -23,7 +23,25 @@
* Note that the `pub`-keyword is not present on `MyType1`. * 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 {FastStringMap, memoize, assert} from "./util";
import { import {
DiagnosticPrinter, DiagnosticPrinter,
@ -34,7 +52,8 @@ import {
E_DECLARATION_NOT_FOUND, E_DECLARATION_NOT_FOUND,
E_INVALID_ARGUMENTS E_INVALID_ARGUMENTS
} from "./diagnostics"; } 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 { interface SymbolInfo {
declarations: BoltDeclaration[]; declarations: BoltDeclaration[];
@ -78,74 +97,223 @@ export class TypeChecker {
public checkSourceFile(node: BoltSourceFile): void { public checkSourceFile(node: BoltSourceFile): void {
const refExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceExpression); const self = this;
for (const refExp of refExps) { for (const element of node.elements) {
if (this.resolveReferenceExpression(refExp) === null) { visitSourceElement(element);
this.diagnostics.add({
message: E_DECLARATION_NOT_FOUND,
args: { name: refExp.name.name.text },
severity: 'error',
node: refExp,
})
}
} }
const typeRefExps = node.findAllChildrenOfKind(SyntaxKind.BoltReferenceTypeExpression); function visitExpression(node: BoltExpression) {
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,
})
}
}
const callExps = node.findAllChildrenOfKind(SyntaxKind.BoltCallExpression); switch (node.kind) {
for (const callExp of callExps) { case SyntaxKind.BoltReferenceExpression:
{
const fnDecls = this.getAllFunctionsInExpression(callExp.operator); if (self.resolveReferenceExpression(node) === null) {
self.diagnostics.add({
for (const fnDecl of fnDecls) { message: E_DECLARATION_NOT_FOUND,
args: { name: node.name.name.text },
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,
severity: 'error', severity: 'error',
args: { name: fnDecl.name.text }, node: node,
node: arg, })
}
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; return type;
} }
case SyntaxKind.BoltReturnStatement:
{
if (node.value === null) {
return createVoidType();
}
return this.getTypeOfNode(node.value)
}
case SyntaxKind.BoltConstantExpression: case SyntaxKind.BoltConstantExpression:
{ {
let type; let type;
@ -200,6 +375,10 @@ export class TypeChecker {
assert(type !== null); assert(type !== null);
return type; return type;
} }
case SyntaxKind.BoltMatchExpression:
{
return createVariantType(...node.arms.map(arm => this.getTypeOfNode(arm.body)));
}
default: default:
throw new Error(`Could not derive type of node ${kindToString(node.kind)}.`); throw new Error(`Could not derive type of node ${kindToString(node.kind)}.`);
} }

View file

@ -7,7 +7,7 @@ import {
export type BoltFunctionBody = BoltFunctionBodyElement[]; export type BoltFunctionBody = BoltFunctionBodyElement[];
export function getAllReturnStatements(body: BoltFunctionBody): BoltReturnStatement[] { export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltReturnStatement[] {
const results: BoltReturnStatement[] = []; const results: BoltReturnStatement[] = [];

View file

@ -59,6 +59,11 @@ import {
createBoltSourceFile, createBoltSourceFile,
BoltRecordField, BoltRecordField,
setParents, setParents,
BoltMatchExpression,
createBoltMatchArm,
BoltMatchArm,
createBoltMatchExpression,
createBoltExpressionPattern,
} from "./ast" } from "./ast"
import { parseForeignLanguage } from "./foreign" import { parseForeignLanguage } from "./foreign"
@ -81,6 +86,13 @@ export function isModifierKeyword(kind: SyntaxKind) {
|| kind === SyntaxKind.BoltForeignKeyword; || 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 = [ const KIND_EXPRESSION_T0 = [
SyntaxKind.BoltStringLiteral, SyntaxKind.BoltStringLiteral,
SyntaxKind.BoltIntegerLiteral, 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 { public parse(kind: SyntaxKind, tokens: BoltTokenStream): BoltSyntax {
return (this as any)['parse' + kindToString(kind).substring('Bolt'.length)](tokens); return (this as any)['parse' + kindToString(kind).substring('Bolt'.length)](tokens);
} }
@ -204,6 +209,17 @@ export class Parser {
const t0 = tokens.peek(1); const t0 = tokens.peek(1);
if (t0.kind === SyntaxKind.BoltIdentifier) { if (t0.kind === SyntaxKind.BoltIdentifier) {
return this.parseBindPattern(tokens); 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 { } else {
throw new ParseError(t0, [SyntaxKind.BoltIdentifier]) throw new ParseError(t0, [SyntaxKind.BoltIdentifier])
} }
@ -328,9 +344,43 @@ export class Parser {
return node; 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 { protected parsePrimitiveExpression(tokens: BoltTokenStream): BoltExpression {
const t0 = tokens.peek(); 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); return this.parseConstantExpression(tokens);
} else if (t0.kind === SyntaxKind.BoltIdentifier) { } else if (t0.kind === SyntaxKind.BoltIdentifier) {
return this.parseReferenceExpression(tokens); return this.parseReferenceExpression(tokens);
@ -714,7 +764,7 @@ export class Parser {
tokens.get(); tokens.get();
const innerTokens = createTokenStream(t0); const innerTokens = createTokenStream(t0);
const param = this.parseParameter(innerTokens, i++) const param = this.parseParameter(innerTokens, i++)
this.assertEmpty(innerTokens); assertNoTokens(innerTokens);
return param return param
} else { } else {
throw new ParseError(t0, [SyntaxKind.BoltIdentifier, SyntaxKind.BoltParenthesized]) throw new ParseError(t0, [SyntaxKind.BoltIdentifier, SyntaxKind.BoltParenthesized])
@ -794,7 +844,7 @@ export class Parser {
tokens.get(); tokens.get();
switch (target) { switch (target) {
case "Bolt": case "Bolt":
body = this.parseStatements(tokens); body = this.parseStatements(createTokenStream(t3));
break; break;
default: default:
body = parseForeignLanguage(target, t3.text, t3.span!.file, t3.span!.start); body = parseForeignLanguage(target, t3.text, t3.span!.file, t3.span!.start);
@ -870,8 +920,7 @@ export class Parser {
let t0 = tokens.peek(1); let t0 = tokens.peek(1);
let i = 1; let i = 1;
if (t0.kind === SyntaxKind.BoltPubKeyword) { if (t0.kind === SyntaxKind.BoltPubKeyword) {
i += 1; t0 = tokens.peek(++i);
t0 = tokens.peek(i);
if (t0.kind !== SyntaxKind.BoltForeignKeyword) { if (t0.kind !== SyntaxKind.BoltForeignKeyword) {
if (KIND_DECLARATION_KEYWORD.indexOf(t0.kind) === -1) { if (KIND_DECLARATION_KEYWORD.indexOf(t0.kind) === -1) {
throw new ParseError(t0, KIND_DECLARATION_KEYWORD); throw new ParseError(t0, KIND_DECLARATION_KEYWORD);
@ -918,8 +967,8 @@ export class Parser {
} }
if (t0.kind === SyntaxKind.BoltForeignKeyword) { if (t0.kind === SyntaxKind.BoltForeignKeyword) {
mustBeFunctionOrVariable = true; mustBeFunctionOrVariable = true;
i++; i += 2;
t0 = tokens.peek(++i); t0 = tokens.peek(i);
} }
if (mustBeFunctionOrVariable if (mustBeFunctionOrVariable
&& t0.kind !== SyntaxKind.BoltStructKeyword && t0.kind !== SyntaxKind.BoltStructKeyword
@ -1015,11 +1064,11 @@ export class Parser {
const operator = this.parsePrimitiveExpression(tokens) const operator = this.parsePrimitiveExpression(tokens)
const t2 = tokens.get(); const t2 = tokens.peek();
if (t2.kind === SyntaxKind.EndOfFile) { if (t2.kind !== SyntaxKind.BoltParenthesized) {
return operator; return operator;
} }
assertToken(t2, SyntaxKind.BoltParenthesized); tokens.get();
const args: BoltExpression[] = [] const args: BoltExpression[] = []
const innerTokens = createTokenStream(t2); const innerTokens = createTokenStream(t2);
@ -1197,4 +1246,4 @@ export function parseSourceFile(filepath: string): BoltSourceFile {
setParents(sourceFile); setParents(sourceFile);
return sourceFile; return sourceFile;
} }
;

View file

@ -11,9 +11,8 @@ import {
setParents, setParents,
SyntaxKind, SyntaxKind,
BoltToken, BoltToken,
BoltSentence, createBoltRArrowAlt,
createEndOfFile, createEndOfFile,
createBoltSentence,
createBoltIdentifier, createBoltIdentifier,
createBoltRArrow, createBoltRArrow,
createBoltOperator, createBoltOperator,
@ -39,16 +38,14 @@ import {
createBoltFnKeyword, createBoltFnKeyword,
createBoltLArrow, createBoltLArrow,
createBoltDotDot, createBoltDotDot,
createJSIdentifier,
JSToken,
createBoltLtSign, createBoltLtSign,
createBoltGtSign, createBoltGtSign,
createBoltModKeyword, createBoltModKeyword,
createBoltTypeKeyword, createBoltTypeKeyword,
createBoltForKeyword, createBoltForKeyword,
createBoltTraitDeclaration,
createBoltTraitKeyword, createBoltTraitKeyword,
createBoltImplKeyword, createBoltImplKeyword,
createBoltMatchKeyword,
} from "./ast" } from "./ast"
export enum PunctType { export enum PunctType {
@ -297,6 +294,7 @@ export class Scanner {
case 'mod': return createBoltModKeyword(span); case 'mod': return createBoltModKeyword(span);
case 'fn': return createBoltFnKeyword(span); case 'fn': return createBoltFnKeyword(span);
case 'return': return createBoltReturnKeyword(span); case 'return': return createBoltReturnKeyword(span);
case 'match': return createBoltMatchKeyword(span);
case 'yield': return createBoltYieldKeyword(span); case 'yield': return createBoltYieldKeyword(span);
case 'for': return createBoltForKeyword(span); case 'for': return createBoltForKeyword(span);
case 'trait': return createBoltTraitKeyword(span); case 'trait': return createBoltTraitKeyword(span);
@ -318,6 +316,7 @@ export class Scanner {
switch (text) { switch (text) {
case '->': return createBoltRArrow(span); case '->': return createBoltRArrow(span);
case '=>': return createBoltRArrowAlt(span);
case '<-': return createBoltLArrow(span); case '<-': return createBoltLArrow(span);
case '<': return createBoltLtSign(span); case '<': return createBoltLtSign(span);
case '>': return createBoltGtSign(span); case '>': return createBoltGtSign(span);

View file

@ -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 { export function isVariantType(value: any): value is VariantType {
return value instanceof 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 { export function intersectTypes(a: Type, b: Type): Type {
if (isNeverType(a) || isNeverType(b)) { if (isNeverType(a) || isNeverType(b)) {
return new NeverType(); return new NeverType();

View file

@ -351,6 +351,8 @@ export function describeKind(kind: SyntaxKind): string {
return "'for'"; return "'for'";
case SyntaxKind.JSTryKeyword: case SyntaxKind.JSTryKeyword:
return "'try'"; return "'try'";
case SyntaxKind.BoltRArrowAlt:
return "'=>'";
default: default:
throw new Error(`failed to describe ${kindToString(kind)}`) throw new Error(`failed to describe ${kindToString(kind)}`)
} }