Major update to code and type checker

This commit is contained in:
Sam Vervaeck 2020-05-29 18:44:58 +02:00
parent 884be8f9ec
commit c2d101c25c
14 changed files with 1641 additions and 778 deletions

View file

@ -2,7 +2,8 @@
node EndOfFile > BoltToken, JSToken; node EndOfFile > BoltToken, JSToken;
node Token; node Token;
node SourceFile; node SourceFile;
node FunctionBody; node FunctionBodyElement;
node ReturnStatement;
// Bolt language AST definitions // Bolt language AST definitions
@ -217,7 +218,7 @@ node BoltConstantExpression > BoltExpression {
node BoltStatement > BoltSyntax, BoltFunctionBodyElement, BoltSourceElement; node BoltStatement > BoltSyntax, BoltFunctionBodyElement, BoltSourceElement;
node BoltReturnStatement > BoltStatement { node BoltReturnStatement > ReturnStatement, BoltStatement {
value: Option<BoltExpression>, value: Option<BoltExpression>,
} }
@ -262,7 +263,7 @@ node BoltModule > BoltSyntax, BoltSourceElement {
node BoltDeclarationLike; node BoltDeclarationLike;
node BoltFunctionBodyElement; node BoltFunctionBodyElement > FunctionBodyElement;
node BoltFunctionDeclaration > BoltFunctionBodyElement, BoltDeclaration, BoltDeclarationLike { node BoltFunctionDeclaration > BoltFunctionBodyElement, BoltDeclaration, BoltDeclarationLike {
modifiers: BoltModifiers, modifiers: BoltModifiers,
@ -467,9 +468,9 @@ node JSReferenceExpression > JSExpression {
node JSSourceElement; node JSSourceElement;
node JSFunctionBodyElement; node JSFunctionBodyElement > FunctionBodyElement;
node JSStatement > JSSourceElement, JSFunctionBodyElement; node JSStatement > JSSyntax, JSSourceElement, JSFunctionBodyElement;
node JSCatchBlock > JSSyntax { node JSCatchBlock > JSSyntax {
bindings: Option<JSPattern>, bindings: Option<JSPattern>,
@ -495,7 +496,7 @@ node JSConditionalStatement > JSStatement {
cases: Vec<JSConditionalCase>, cases: Vec<JSConditionalCase>,
} }
node JSReturnStatement > JSStatement { node JSReturnStatement > ReturnStatement, JSStatement {
value: Option<JSExpression>, value: Option<JSExpression>,
} }

344
src/ast.d.ts vendored
View file

@ -1,5 +1,5 @@
import { Type } from "./types" import { TypeRef } from "./types"
import { Diagnostic } from "./diagnostics" import { Diagnostic } from "./diagnostics"
import { Package } from "./common" import { Package } from "./common"
import { TextSpan } from "./text" import { TextSpan } from "./text"
@ -13,7 +13,7 @@ export function isSyntax(value: any): value is Syntax;
interface SyntaxBase { interface SyntaxBase {
id: number; id: number;
kind: SyntaxKind; kind: SyntaxKind;
type?: Type; type?: TypeRef;
errors: Diagnostic[] errors: Diagnostic[]
parentNode: Syntax | null; parentNode: Syntax | null;
span: TextSpan | null; span: TextSpan | null;
@ -30,7 +30,6 @@ export type ResolveSyntaxKind<K extends SyntaxKind> = Extract<Syntax, { kind: K
export class NodeVisitor { export class NodeVisitor {
public visit(node: Syntax): void; public visit(node: Syntax): void;
protected visitEndOfFile?(node: EndOfFile): void; protected visitEndOfFile?(node: EndOfFile): void;
protected visitFunctionBody?(node: FunctionBody): void;
protected visitBoltStringLiteral?(node: BoltStringLiteral): void; protected visitBoltStringLiteral?(node: BoltStringLiteral): void;
protected visitBoltIntegerLiteral?(node: BoltIntegerLiteral): void; protected visitBoltIntegerLiteral?(node: BoltIntegerLiteral): void;
protected visitBoltIdentifier?(node: BoltIdentifier): void; protected visitBoltIdentifier?(node: BoltIdentifier): void;
@ -187,158 +186,157 @@ export class NodeVisitor {
export const enum SyntaxKind { export const enum SyntaxKind {
EndOfFile = 0, EndOfFile = 0,
FunctionBody = 3, BoltStringLiteral = 7,
BoltStringLiteral = 6, BoltIntegerLiteral = 8,
BoltIntegerLiteral = 7, BoltIdentifier = 10,
BoltIdentifier = 9, BoltOperator = 12,
BoltOperator = 11, BoltAssignment = 13,
BoltAssignment = 12, BoltComma = 14,
BoltComma = 13, BoltSemi = 15,
BoltSemi = 14, BoltColon = 16,
BoltColon = 15, BoltColonColon = 17,
BoltColonColon = 16, BoltDot = 18,
BoltDot = 17, BoltDotDot = 19,
BoltDotDot = 18, BoltRArrow = 20,
BoltRArrow = 19, BoltRArrowAlt = 21,
BoltRArrowAlt = 20, BoltLArrow = 22,
BoltLArrow = 21, BoltEqSign = 23,
BoltEqSign = 22, BoltGtSign = 24,
BoltGtSign = 23, BoltExMark = 25,
BoltExMark = 24, BoltLtSign = 26,
BoltLtSign = 25, BoltVBar = 27,
BoltVBar = 26, BoltWhereKeyword = 29,
BoltWhereKeyword = 28, BoltQuoteKeyword = 30,
BoltQuoteKeyword = 29, BoltFnKeyword = 31,
BoltFnKeyword = 30, BoltForeignKeyword = 32,
BoltForeignKeyword = 31, BoltForKeyword = 33,
BoltForKeyword = 32, BoltLetKeyword = 34,
BoltLetKeyword = 33, BoltReturnKeyword = 35,
BoltReturnKeyword = 34, BoltLoopKeyword = 36,
BoltLoopKeyword = 35, BoltYieldKeyword = 37,
BoltYieldKeyword = 36, BoltMatchKeyword = 38,
BoltMatchKeyword = 37, BoltImportKeyword = 39,
BoltImportKeyword = 38, BoltExportKeyword = 40,
BoltExportKeyword = 39, BoltPubKeyword = 41,
BoltPubKeyword = 40, BoltModKeyword = 42,
BoltModKeyword = 41, BoltMutKeyword = 43,
BoltMutKeyword = 42, BoltEnumKeyword = 44,
BoltEnumKeyword = 43, BoltStructKeyword = 45,
BoltStructKeyword = 44, BoltTypeKeyword = 46,
BoltTypeKeyword = 45, BoltTraitKeyword = 47,
BoltTraitKeyword = 46, BoltImplKeyword = 48,
BoltImplKeyword = 47, BoltParenthesized = 50,
BoltParenthesized = 49, BoltBraced = 51,
BoltBraced = 50, BoltBracketed = 52,
BoltBracketed = 51, BoltSourceFile = 53,
BoltSourceFile = 52, BoltQualName = 54,
BoltQualName = 53, BoltTypeOfExpression = 56,
BoltTypeOfExpression = 55, BoltReferenceTypeExpression = 57,
BoltReferenceTypeExpression = 56, BoltFunctionTypeExpression = 58,
BoltFunctionTypeExpression = 57, BoltLiftedTypeExpression = 59,
BoltLiftedTypeExpression = 58, BoltTypeParameter = 60,
BoltTypeParameter = 59, BoltBindPattern = 62,
BoltBindPattern = 61, BoltTypePattern = 63,
BoltTypePattern = 62, BoltExpressionPattern = 64,
BoltExpressionPattern = 63, BoltTuplePatternElement = 65,
BoltTuplePatternElement = 64, BoltTuplePattern = 66,
BoltTuplePattern = 65, BoltRecordFieldPattern = 67,
BoltRecordFieldPattern = 66, BoltRecordPattern = 68,
BoltRecordPattern = 67, BoltQuoteExpression = 70,
BoltQuoteExpression = 69, BoltTupleExpression = 71,
BoltTupleExpression = 70, BoltReferenceExpression = 72,
BoltReferenceExpression = 71, BoltMemberExpression = 73,
BoltMemberExpression = 72, BoltFunctionExpression = 74,
BoltFunctionExpression = 73, BoltCallExpression = 75,
BoltCallExpression = 74, BoltYieldExpression = 76,
BoltYieldExpression = 75, BoltMatchArm = 77,
BoltMatchArm = 76, BoltMatchExpression = 78,
BoltMatchExpression = 77, BoltCase = 79,
BoltCase = 78, BoltCaseExpression = 80,
BoltCaseExpression = 79, BoltBlockExpression = 81,
BoltBlockExpression = 80, BoltConstantExpression = 82,
BoltConstantExpression = 81, BoltReturnStatement = 84,
BoltReturnStatement = 83, BoltConditionalCase = 85,
BoltConditionalCase = 84, BoltConditionalStatement = 86,
BoltConditionalStatement = 85, BoltResumeStatement = 87,
BoltResumeStatement = 86, BoltExpressionStatement = 88,
BoltExpressionStatement = 87, BoltParameter = 89,
BoltParameter = 88, BoltModule = 93,
BoltModule = 92, BoltFunctionDeclaration = 96,
BoltFunctionDeclaration = 95, BoltVariableDeclaration = 97,
BoltVariableDeclaration = 96, BoltPlainImportSymbol = 99,
BoltPlainImportSymbol = 98, BoltImportDirective = 100,
BoltImportDirective = 99, BoltExportSymbol = 101,
BoltExportSymbol = 100, BoltPlainExportSymbol = 102,
BoltPlainExportSymbol = 101, BoltExportDirective = 103,
BoltExportDirective = 102, BoltTraitDeclaration = 104,
BoltTraitDeclaration = 103, BoltImplDeclaration = 105,
BoltImplDeclaration = 104, BoltTypeAliasDeclaration = 106,
BoltTypeAliasDeclaration = 105, BoltRecordField = 108,
BoltRecordField = 107, BoltRecordDeclaration = 109,
BoltRecordDeclaration = 108, BoltMacroCall = 111,
BoltMacroCall = 110, JSIdentifier = 115,
JSIdentifier = 114, JSString = 116,
JSString = 115, JSInteger = 117,
JSInteger = 116, JSFromKeyword = 118,
JSFromKeyword = 117, JSReturnKeyword = 119,
JSReturnKeyword = 118, JSTryKeyword = 120,
JSTryKeyword = 119, JSFinallyKeyword = 121,
JSFinallyKeyword = 120, JSCatchKeyword = 122,
JSCatchKeyword = 121, JSImportKeyword = 123,
JSImportKeyword = 122, JSAsKeyword = 124,
JSAsKeyword = 123, JSConstKeyword = 125,
JSConstKeyword = 124, JSLetKeyword = 126,
JSLetKeyword = 125, JSExportKeyword = 127,
JSExportKeyword = 126, JSFunctionKeyword = 128,
JSFunctionKeyword = 127, JSWhileKeyword = 129,
JSWhileKeyword = 128, JSForKeyword = 130,
JSForKeyword = 129, JSCloseBrace = 132,
JSCloseBrace = 131, JSCloseBracket = 133,
JSCloseBracket = 132, JSCloseParen = 134,
JSCloseParen = 133, JSOpenBrace = 135,
JSOpenBrace = 134, JSOpenBracket = 136,
JSOpenBracket = 135, JSOpenParen = 137,
JSOpenParen = 136, JSSemi = 138,
JSSemi = 137, JSComma = 139,
JSComma = 138, JSDot = 140,
JSDot = 139, JSDotDotDot = 141,
JSDotDotDot = 140, JSMulOp = 142,
JSMulOp = 141, JSAddOp = 143,
JSAddOp = 142, JSDivOp = 144,
JSDivOp = 143, JSSubOp = 145,
JSSubOp = 144, JSLtOp = 146,
JSLtOp = 145, JSGtOp = 147,
JSGtOp = 146, JSBOrOp = 148,
JSBOrOp = 147, JSBXorOp = 149,
JSBXorOp = 148, JSBAndOp = 150,
JSBAndOp = 149, JSBNotOp = 151,
JSBNotOp = 150, JSNotOp = 152,
JSNotOp = 151, JSBindPattern = 154,
JSBindPattern = 153, JSConstantExpression = 156,
JSConstantExpression = 155, JSMemberExpression = 157,
JSMemberExpression = 156, JSCallExpression = 158,
JSCallExpression = 157, JSBinaryExpression = 159,
JSBinaryExpression = 158, JSUnaryExpression = 160,
JSUnaryExpression = 159, JSNewExpression = 161,
JSNewExpression = 160, JSSequenceExpression = 162,
JSSequenceExpression = 161, JSConditionalExpression = 163,
JSConditionalExpression = 162, JSLiteralExpression = 164,
JSLiteralExpression = 163, JSReferenceExpression = 165,
JSReferenceExpression = 164, JSCatchBlock = 169,
JSCatchBlock = 168, JSTryCatchStatement = 170,
JSTryCatchStatement = 169, JSExpressionStatement = 171,
JSExpressionStatement = 170, JSConditionalCase = 172,
JSConditionalCase = 171, JSConditionalStatement = 173,
JSConditionalStatement = 172, JSReturnStatement = 174,
JSReturnStatement = 173, JSParameter = 175,
JSParameter = 174, JSImportStarBinding = 179,
JSImportStarBinding = 178, JSImportAsBinding = 180,
JSImportAsBinding = 179, JSImportDeclaration = 181,
JSImportDeclaration = 180, JSFunctionDeclaration = 182,
JSFunctionDeclaration = 181, JSArrowFunctionDeclaration = 183,
JSArrowFunctionDeclaration = 182, JSLetDeclaration = 184,
JSLetDeclaration = 183, JSSourceFile = 185,
JSSourceFile = 184,
} }
export interface EndOfFile extends SyntaxBase { export interface EndOfFile extends SyntaxBase {
@ -479,20 +477,26 @@ export type SourceFile
| JSSourceFile | JSSourceFile
export interface FunctionBody extends SyntaxBase { export type FunctionBodyElement
kind: SyntaxKind.FunctionBody; = JSFunctionDeclaration
parentNode: FunctionBodyParent; | JSArrowFunctionDeclaration
getChildNodes(): IterableIterator<FunctionBodyChild> | JSLetDeclaration
} | JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
| BoltFunctionDeclaration
| BoltVariableDeclaration
| BoltReturnStatement
| BoltConditionalStatement
| BoltResumeStatement
| BoltExpressionStatement
| BoltMacroCall
export type FunctionBodyParent
= never
export type FunctionBodyAnyParent export type ReturnStatement
= never = BoltReturnStatement
| JSReturnStatement
export type FunctionBodyChild
= never
export type BoltSyntax export type BoltSyntax
= BoltSourceFile = BoltSourceFile
@ -4980,6 +4984,9 @@ export type JSSyntax
| JSFunctionDeclaration | JSFunctionDeclaration
| JSArrowFunctionDeclaration | JSArrowFunctionDeclaration
| JSLetDeclaration | JSLetDeclaration
| JSExpressionStatement
| JSConditionalStatement
| JSReturnStatement
| JSConstantExpression | JSConstantExpression
| JSMemberExpression | JSMemberExpression
| JSCallExpression | JSCallExpression
@ -8015,7 +8022,6 @@ export type JSSourceFileChild
export type Syntax export type Syntax
= EndOfFile = EndOfFile
| FunctionBody
| BoltStringLiteral | BoltStringLiteral
| BoltIntegerLiteral | BoltIntegerLiteral
| BoltIdentifier | BoltIdentifier
@ -8172,7 +8178,6 @@ export type Syntax
export function kindToString(kind: SyntaxKind): string; export function kindToString(kind: SyntaxKind): string;
export function createEndOfFile(span?: TextSpan | null): EndOfFile; export function createEndOfFile(span?: TextSpan | null): EndOfFile;
export function createFunctionBody(span?: TextSpan | null): FunctionBody;
export function createBoltStringLiteral(value: string, span?: TextSpan | null): BoltStringLiteral; export function createBoltStringLiteral(value: string, span?: TextSpan | null): BoltStringLiteral;
export function createBoltIntegerLiteral(value: bigint, span?: TextSpan | null): BoltIntegerLiteral; export function createBoltIntegerLiteral(value: bigint, span?: TextSpan | null): BoltIntegerLiteral;
export function createBoltIdentifier(text: string, span?: TextSpan | null): BoltIdentifier; export function createBoltIdentifier(text: string, span?: TextSpan | null): BoltIdentifier;
@ -8328,7 +8333,8 @@ export function createJSSourceFile(elements: JSSourceElement[], span?: TextSpan
export function isEndOfFile(value: any): value is EndOfFile; export function isEndOfFile(value: any): value is EndOfFile;
export function isToken(value: any): value is Token; export function isToken(value: any): value is Token;
export function isSourceFile(value: any): value is SourceFile; export function isSourceFile(value: any): value is SourceFile;
export function isFunctionBody(value: any): value is FunctionBody; export function isFunctionBodyElement(value: any): value is FunctionBodyElement;
export function isReturnStatement(value: any): value is ReturnStatement;
export function isBoltSyntax(value: any): value is BoltSyntax; export function isBoltSyntax(value: any): value is BoltSyntax;
export function isBoltToken(value: any): value is BoltToken; export function isBoltToken(value: any): value is BoltToken;
export function isBoltStringLiteral(value: any): value is BoltStringLiteral; export function isBoltStringLiteral(value: any): value is BoltStringLiteral;

View file

@ -1,7 +1,7 @@
import { BoltImportDirective, Syntax, BoltParameter, BoltReferenceExpression, BoltReferenceTypeExpression, BoltSourceFile, BoltCallExpression, BoltReturnKeyword, BoltReturnStatement, SyntaxKind, NodeVisitor, BoltSyntax, BoltIdentifier } from "./ast"; import { BoltImportDirective, Syntax, BoltParameter, BoltReferenceExpression, BoltReferenceTypeExpression, BoltSourceFile, BoltCallExpression, BoltReturnKeyword, BoltReturnStatement, SyntaxKind, NodeVisitor, BoltSyntax, BoltIdentifier } from "./ast";
import { Program } from "./program"; import { Program } from "./program";
import { DiagnosticPrinter, E_FILE_NOT_FOUND, E_TYPES_NOT_ASSIGNABLE, E_DECLARATION_NOT_FOUND, E_TYPE_DECLARATION_NOT_FOUND, E_MUST_RETURN_A_VALUE, E_MAY_NOT_RETURN_A_VALUE } from "./diagnostics"; import { DiagnosticPrinter, E_FILE_NOT_FOUND, E_TYPE_MISMATCH, E_DECLARATION_NOT_FOUND, E_TYPE_DECLARATION_NOT_FOUND, E_MUST_RETURN_A_VALUE, E_MAY_NOT_RETURN_A_VALUE } from "./diagnostics";
import { getSymbolPathFromNode } from "./resolver" import { convertNodeToSymbolPath } from "./resolver"
import { inject } from "./ioc"; import { inject } from "./ioc";
import { SymbolResolver, ScopeType } from "./resolver"; import { SymbolResolver, ScopeType } from "./resolver";
import { assert, every } from "./util"; import { assert, every } from "./util";
@ -103,7 +103,7 @@ export class CheckReferences extends NodeVisitor {
this.checkBoltModulePath(node.name, node.name.modulePath); this.checkBoltModulePath(node.name, node.name.modulePath);
const scope = this.resolver.getScopeSurroundingNode(node, ScopeType.Variable); const scope = this.resolver.getScopeSurroundingNode(node, ScopeType.Variable);
assert(scope !== null); assert(scope !== null);
const resolvedSym = this.resolver.resolveSymbolPath(getSymbolPathFromNode(node), scope!); const resolvedSym = this.resolver.resolveSymbolPath(convertNodeToSymbolPath(node), scope!);
if (resolvedSym === null) { if (resolvedSym === null) {
this.diagnostics.add({ this.diagnostics.add({
message: E_DECLARATION_NOT_FOUND, message: E_DECLARATION_NOT_FOUND,
@ -117,7 +117,7 @@ export class CheckReferences extends NodeVisitor {
protected visitBoltReferenceTypeExpression(node: BoltReferenceTypeExpression) { protected visitBoltReferenceTypeExpression(node: BoltReferenceTypeExpression) {
const scope = this.resolver.getScopeSurroundingNode(node, ScopeType.Type); const scope = this.resolver.getScopeSurroundingNode(node, ScopeType.Type);
assert(scope !== null); assert(scope !== null);
const symbolPath = getSymbolPathFromNode(node.name); const symbolPath = convertNodeToSymbolPath(node.name);
const resolvedSym = this.resolver.resolveSymbolPath(symbolPath, scope!); const resolvedSym = this.resolver.resolveSymbolPath(symbolPath, scope!);
if (resolvedSym === null) { if (resolvedSym === null) {
this.diagnostics.add({ this.diagnostics.add({

View file

@ -19,6 +19,7 @@ import {FastStringMap, enumerate, escapeChar, assert} from "./util";
import {TextSpan, TextPos, TextFile} from "./text"; import {TextSpan, TextPos, TextFile} from "./text";
import {Scanner} from "./scanner"; import {Scanner} from "./scanner";
import * as path from "path" import * as path from "path"
import { convertNodeToSymbolPath } from "./resolver";
export function getSourceFile(node: Syntax) { export function getSourceFile(node: Syntax) {
while (true) { while (true) {
@ -249,6 +250,23 @@ export function isExported(node: Syntax) {
} }
} }
export function getFullyQualifiedPathToNode(node: Syntax) {
const symbolPath = convertNodeToSymbolPath(node);
while (true) {
const parentNode = node.parentNode;
if (parentNode === null) {
break;
}
node = parentNode;
if (node.kind === SyntaxKind.BoltModule) {
for (const element of node.name) {
symbolPath.modulePath.unshift(element.text);
}
}
}
return symbolPath;
}
export function describeKind(kind: SyntaxKind): string { export function describeKind(kind: SyntaxKind): string {
switch (kind) { switch (kind) {
case SyntaxKind.BoltImportKeyword: case SyntaxKind.BoltImportKeyword:
@ -388,3 +406,39 @@ export function describeKind(kind: SyntaxKind): string {
} }
} }
export function *getAllReturnStatementsInFunctionBody(body: FunctionBody): IterableIterator<ReturnStatement> {
for (const element of body) {
switch (element.kind) {
case SyntaxKind.BoltReturnStatement:
case SyntaxKind.JSReturnStatement:
{
yield element;
break;
}
case SyntaxKind.BoltConditionalStatement:
{
for (const caseNode of element.cases) {
yield* getAllReturnStatementsInFunctionBody(caseNode.body);
}
break;
}
case SyntaxKind.JSTryCatchStatement:
{
yield* getAllReturnStatementsInFunctionBody(element.tryBlock)
if (element.catchBlock !== null) {
yield* getAllReturnStatementsInFunctionBody(element.catchBlock.elements)
}
if (element.finalBlock !== null) {
yield* getAllReturnStatementsInFunctionBody(element.finalBlock)
}
break;
}
case SyntaxKind.JSExpressionStatement:
case SyntaxKind.BoltExpressionStatement:
case SyntaxKind.JSImportDeclaration:
break;
default:
throw new Error(`I did not know how to find return statements in ${kindToString(element.kind)}`);
}
}
}

View file

@ -13,7 +13,7 @@ export const E_FIELD_NOT_PRESENT = "Field '{name}' is not present."
export const E_FIELD_MUST_BE_BOOLEAN = "Field '{name}' must be a either 'true' or 'false'." export const E_FIELD_MUST_BE_BOOLEAN = "Field '{name}' must be a either 'true' or 'false'."
export const E_TYPE_DECLARATION_NOT_FOUND = "A type declaration named '{name}' was not found." export const E_TYPE_DECLARATION_NOT_FOUND = "A type declaration named '{name}' was not found."
export const E_DECLARATION_NOT_FOUND = "Reference to an undefined declaration '{name}'."; export const E_DECLARATION_NOT_FOUND = "Reference to an undefined declaration '{name}'.";
export const E_TYPES_NOT_ASSIGNABLE = "Types {left} and {right} are not assignable."; export const E_TYPE_MISMATCH = "Types {left} and {right} are not semantically equivalent.";
export const E_TOO_FEW_ARGUMENTS_FOR_FUNCTION_CALL = "Too few arguments for function call. Expected {expected} but got {actual}."; export const E_TOO_FEW_ARGUMENTS_FOR_FUNCTION_CALL = "Too few arguments for function call. Expected {expected} but got {actual}.";
export const E_TOO_MANY_ARGUMENTS_FOR_FUNCTION_CALL = "Too many arguments for function call. Expected {expected} but got {actual}."; export const E_TOO_MANY_ARGUMENTS_FOR_FUNCTION_CALL = "Too many arguments for function call. Expected {expected} but got {actual}.";
export const E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER = "Candidate function requires this parameter." export const E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER = "Candidate function requires this parameter."
@ -24,6 +24,7 @@ export const E_TYPES_MISSING_MEMBER = "Not all types resolve to a record with th
export const E_NODE_DOES_NOT_CONTAIN_MEMBER = "This node does not contain the the member '{name}'." export const E_NODE_DOES_NOT_CONTAIN_MEMBER = "This node does not contain the the member '{name}'."
export const E_MAY_NOT_RETURN_BECAUSE_TYPE_RESOLVES_TO_VOID = "May not return a value because the function's return type resolves to '()'" export const E_MAY_NOT_RETURN_BECAUSE_TYPE_RESOLVES_TO_VOID = "May not return a value because the function's return type resolves to '()'"
export const E_MUST_RETURN_BECAUSE_TYPE_DOES_NOT_RESOLVE_TO_VOID = "Must return a value because the function's return type does not resolve to '()'" export const E_MUST_RETURN_BECAUSE_TYPE_DOES_NOT_RESOLVE_TO_VOID = "Must return a value because the function's return type does not resolve to '()'"
export const E_ARGUMENT_TYPE_NOT_ASSIGNABLE = "This argument's type is not assignable to the function's parameter type."
const BOLT_HARD_ERRORS = process.env['BOLT_HARD_ERRORS'] const BOLT_HARD_ERRORS = process.env['BOLT_HARD_ERRORS']

View file

@ -147,7 +147,7 @@ export class Evaluator {
case SyntaxKind.BoltTypePattern: case SyntaxKind.BoltTypePattern:
{ {
const expectedType = this.checker.getTypeOfNode(node.type); const expectedType = this.checker.getTypeOfNode(node.type);
if (!this.checker.isTypeAssignableTo(expectedType, this.checker.getTypeOfValue(value))) { if (!this.checker.isTypeAssignableTo(expectedType, this.checker.createTypeForValue(value))) {
return false; return false;
} }
return false; return false;

View file

@ -98,6 +98,7 @@ export class Frontend {
for (const sourceFile of program.getAllSourceFiles()) { for (const sourceFile of program.getAllSourceFiles()) {
checker.registerSourceFile(sourceFile); checker.registerSourceFile(sourceFile);
} }
checker.solve(program.getAllSourceFiles());
for (const pkg of program.getAllPackages()) { for (const pkg of program.getAllPackages()) {
if (!pkg.isDependency) { if (!pkg.isDependency) {
for (const sourceFile of pkg.getAllSourceFiles()) { for (const sourceFile of pkg.getAllSourceFiles()) {

View file

@ -9,25 +9,49 @@ const GLOBAL_SCOPE_ID = 'global';
export class SymbolPath { export class SymbolPath {
constructor( constructor(
private parents: string[], public modulePath: string[],
public isAbsolute: boolean, public isAbsolute: boolean,
public name: string public name: string
) { ) {
} }
public hasParents(): boolean { public encode(): string {
return this.parents.length > 0; let out = '';
if (this.isAbsolute) {
out += '::'
}
for (const element of this.modulePath) {
out += element + '::'
}
out += this.name;
return out;
} }
public getParents() { public hasModulePath(): boolean {
return this.parents; return this.modulePath.length > 0;
}
public getModulePath() {
return this.modulePath;
} }
} }
export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath { export function convertNodeToSymbolPath(node: Syntax): SymbolPath {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltRecordDeclaration:
return new SymbolPath(
[],
false,
node.name.text,
);
case SyntaxKind.BoltFunctionDeclaration:
return new SymbolPath(
[],
false,
emitNode(node.name),
);
case SyntaxKind.BoltReferenceExpression: case SyntaxKind.BoltReferenceExpression:
return new SymbolPath( return new SymbolPath(
node.name.modulePath.map(id => id.text), node.name.modulePath.map(id => id.text),
@ -42,14 +66,8 @@ export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath {
return new SymbolPath([], false, name); return new SymbolPath([], false, name);
} }
return new SymbolPath(node.modulePath.map(id => id.text), false, name); return new SymbolPath(node.modulePath.map(id => id.text), false, name);
//case SyntaxKind.BoltModulePath:
// return new SymbolPath(
// node.elements.slice(0, -1).map(el => el.text),
// node.isAbsolute,
// node.elements[node.elements.length-1].text
// );
default: default:
throw new Error(`Could not extract a symbol path from the given node.`); throw new Error(`Could not extract a symbol path from node ${kindToString(node.kind)}.`);
} }
} }
@ -388,7 +406,7 @@ export class SymbolResolver {
for (const scopeType of getAllSymbolKinds()) { for (const scopeType of getAllSymbolKinds()) {
const scope = this.getScopeForNode(importDir, scopeType); const scope = this.getScopeForNode(importDir, scopeType);
assert(scope !== null); assert(scope !== null);
const exported = this.resolveSymbolPath(getSymbolPathFromNode(importSymbol), scope!); const exported = this.resolveSymbolPath(convertNodeToSymbolPath(importSymbol), scope!);
if (exported !== null) { if (exported !== null) {
for (const decl of exported.declarations) { for (const decl of exported.declarations) {
scope!.addNodeAsSymbol(this.strategy.getSymbolName(decl), decl); scope!.addNodeAsSymbol(this.strategy.getSymbolName(decl), decl);
@ -491,16 +509,17 @@ export class SymbolResolver {
return scope.getSymbol(this.strategy.getSymbolName(node)); return scope.getSymbol(this.strategy.getSymbolName(node));
} }
public resolveGlobalSymbol(name: string, kind: ScopeType) { public resolveGlobalSymbol(path: SymbolPath, kind: ScopeType) {
const symbolPath = new SymbolPath([], true, name);
for (const sourceFile of this.program.getAllGloballyDeclaredSourceFiles()) { for (const sourceFile of this.program.getAllGloballyDeclaredSourceFiles()) {
const scope = this.getScopeForNode(sourceFile, kind); const scope = this.getScopeForNode(sourceFile, kind);
if (scope === null) { assert(scope !== null);
const modScope = this.resolveModulePath(path.getModulePath(), scope!);
if (modScope === null) {
continue; continue;
} }
const sym = scope.getLocalSymbol(name); const sym = modScope?.getLocalSymbol(path.name)
if (sym !== null) { if (sym !== null) {
return sym return sym;
} }
} }
return null; return null;
@ -508,7 +527,7 @@ export class SymbolResolver {
public resolveSymbolPath(path: SymbolPath, scope: Scope): SymbolInfo | null { public resolveSymbolPath(path: SymbolPath, scope: Scope): SymbolInfo | null {
if (path.hasParents()) { if (path.hasModulePath()) {
if (path.isAbsolute) { if (path.isAbsolute) {
@ -517,7 +536,7 @@ export class SymbolResolver {
} else { } else {
// Perform the acutal module resolution. // Perform the acutal module resolution.
const resolvedScope = this.resolveModulePath(path.getParents(), scope); const resolvedScope = this.resolveModulePath(path.getModulePath(), scope);
// Failing to find any module means that we cannot continue, because // Failing to find any module means that we cannot continue, because
// it does not make sense to get the symbol of a non-existent module. // it does not make sense to get the symbol of a non-existent module.
@ -533,7 +552,11 @@ export class SymbolResolver {
// Once we've handled any module path that might have been present, // Once we've handled any module path that might have been present,
// we resolve the actual symbol using a helper method. // we resolve the actual symbol using a helper method.
return scope.getSymbol(path.name); const sym = scope.getSymbol(path.name);
if (sym !== null) {
return sym
}
return this.resolveGlobalSymbol(path, scope.kind);
} }
} }

18
src/test/types.ts Normal file
View file

@ -0,0 +1,18 @@
import { assert } from "chai"
import { AnyType, simplifyType, UnionType } from "../types"
import { createBoltIdentifier } from "../ast";
import { type } from "os";
describe('a function that merges two equivalent types', () => {
it('can merge two any types', () =>{
const type1 = new AnyType;
type1.node = createBoltIdentifier('a');
const type2 = new AnyType;
type2.node = createBoltIdentifier('b');
const types = new UnionType([type1 type2]);
mergeTypes(types);
})
})

View file

@ -18,7 +18,7 @@ const nodeProto = {
*getChildNodes() { *getChildNodes() {
for (const key of Object.keys(this)) { for (const key of Object.keys(this)) {
if (key === 'span' || key === 'parentNode') { if (key === 'span' || key === 'parentNode' || key === 'type') {
continue continue
} }
const value = this[key]; const value = this[key];

View file

@ -1,5 +1,5 @@
import { Type } from "./types" import { TypeRef } from "./types"
import { Diagnostic } from "./diagnostics" import { Diagnostic } from "./diagnostics"
import { Package } from "./common" import { Package } from "./common"
import { TextSpan } from "./text" import { TextSpan } from "./text"
@ -13,7 +13,7 @@ export function isSyntax(value: any): value is Syntax;
interface SyntaxBase { interface SyntaxBase {
id: number; id: number;
kind: SyntaxKind; kind: SyntaxKind;
type?: Type; type?: TypeRef;
errors: Diagnostic[] errors: Diagnostic[]
parentNode: Syntax | null; parentNode: Syntax | null;
span: TextSpan | null; span: TextSpan | null;

View file

@ -58,7 +58,7 @@ export function generateAST(decls: Declaration[]) {
jsFile.write(`index: ${decl.index},\n`); jsFile.write(`index: ${decl.index},\n`);
jsFile.write(`parents: [`); jsFile.write(`parents: [`);
for (const parentName of getParentChain(decl.name)) { for (const parentName of getParentChain(decl.name)) {
jsFile.write(`'${decl.name}', `) jsFile.write(`'${parentName}', `)
} }
jsFile.write(`'Syntax'],\n`); jsFile.write(`'Syntax'],\n`);
jsFile.write(`fields: new Map([\n`); jsFile.write(`fields: new Map([\n`);
@ -359,7 +359,7 @@ export function generateAST(decls: Declaration[]) {
return children; return children;
} }
function *getParentChain(nodeName: string) { function *getParentChain(nodeName: string): IterableIterator<string> {
const stack = [ nodeName ]; const stack = [ nodeName ];
while (stack.length > 0) { while (stack.length > 0) {
const nodeDecl = getDeclarationNamed(stack.pop()!) as NodeDeclaration; const nodeDecl = getDeclarationNamed(stack.pop()!) as NodeDeclaration;

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@ import * as os from "os"
import moment from "moment" import moment from "moment"
import chalk from "chalk" import chalk from "chalk"
import { LOG_DATETIME_FORMAT } from "./constants" import { LOG_DATETIME_FORMAT } from "./constants"
import { E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER } from "./diagnostics"
export function isPowerOf(x: number, n: number):boolean { export function isPowerOf(x: number, n: number):boolean {
const a = Math.log(x) / Math.log(n); const a = Math.log(x) / Math.log(n);
@ -39,6 +40,53 @@ export function every<T>(iterator: Iterator<T>, pred: (value: T) => boolean): bo
return true; return true;
} }
export function* filter<T>(iterator: Iterator<T>, pred: (value: T) => boolean): IterableIterator<T> {
while (true) {
const { value, done } = iterator.next();
if (done) {
break;
}
if (pred(value)) {
yield value;
}
}
}
export function* map<T, R>(iterator: Iterator<T>, func: (value: T) => R): IterableIterator<R> {
while (true) {
const { value, done } = iterator.next();
if (done) {
break;
}
yield func(value);
}
}
export function* flatMap<T, R>(iterator: Iterator<T>, func: (value: T) => IterableIterator<R>): IterableIterator<R> {
while (true) {
const { value, done } = iterator.next();
if (done) {
break;
}
for (const element of func(value)) {
yield element;
}
}
}
export function comparator<T>(pred: (a: T, b: T) => boolean): (a: T, b: T) => number {
return function (a, b) {
if (pred(a, b)) {
return 1;
} else if (pred(b, a)) {
return -1;
} else {
return 0;
}
}
}
export function assert(test: boolean): void { export function assert(test: boolean): void {
if (!test) { if (!test) {
throw new Error(`Invariant violation: an internal sanity check failed.`); throw new Error(`Invariant violation: an internal sanity check failed.`);
@ -131,6 +179,102 @@ export function prettyPrint(value: any): string {
return value.toString(); return value.toString();
} }
export type TransparentProxy<T> = T & { updateHandle(value: T): void }
export function createTransparentProxy<T extends object>(value: T): TransparentProxy<T> {
const handlerObject = {
__HANDLE: value,
__IS_HANDLE: true,
updateHandle(newValue: T) {
if (newValue.__IS_HANDLE) {
newValue = newValue.__HANDLE;
}
value = newValue;
handlerObject.__HANDLE = newValue;
}
};
return new Proxy<any>({}, {
getPrototypeOf(target: T): object | null {
return Reflect.getPrototypeOf(value);
},
setPrototypeOf(target: T, v: any): boolean {
return Reflect.setPrototypeOf(value, v);
},
isExtensible(target: T): boolean {
return Reflect.isExtensible(value);
},
preventExtensions(target: T): boolean {
return Reflect.preventExtensions(value);
},
getOwnPropertyDescriptor(target: T, p: PropertyKey): PropertyDescriptor | undefined {
return Reflect.getOwnPropertyDescriptor(value, p);
},
has(target: T, p: PropertyKey): boolean {
return Reflect.has(value, p);
},
get(target: T, p: PropertyKey, receiver: any): any {
if (hasOwnProperty(handlerObject, p)) {
return Reflect.get(handlerObject, p);
}
return Reflect.get(value, p, receiver)
},
set(target: T, p: PropertyKey, value: any, receiver: any): boolean {
return Reflect.set(value, p, value);
},
deleteProperty(target: T, p: PropertyKey): boolean {
return Reflect.deleteProperty(value, p);
},
defineProperty(target: T, p: PropertyKey, attributes: PropertyDescriptor): boolean {
return Reflect.defineProperty(value, p, attributes);
},
enumerate(target: T): PropertyKey[] {
return [...Reflect.enumerate(value)];
},
ownKeys(target: T): PropertyKey[] {
return Reflect.ownKeys(value);
},
apply(target: T, thisArg: any, argArray?: any): any {
return Reflect.apply(value as any, thisArg, argArray);
},
construct(target: T, argArray: any, newTarget?: any): object {
return Reflect.construct(value as any, argArray, newTarget);
}
});
}
export const getKeyTag = Symbol('get key of object');
function getKey(value: any): string {
if (typeof(value) === 'string') {
return value;
} else if (typeof(value) === 'number') {
return value.toString();
} else if (isObjectLike(value) && hasOwnProperty(value, getKeyTag)) {
return value[getKeyTag]();
} else {
throw new Error(`Could not calculate a key for ${value}`);
}
}
export class FastMultiMap<K, V> {
private mapping = Object.create(null);
public clear(): void {
this.mapping = Object.create(null);
}
public add(key: K, value: V) {
const keyStr = getKey(key);
if (keyStr in this.mapping) {
this.mapping[keyStr].push(keyStr);
} else {
this.mapping[keyStr] = [ value ]
}
}
}
export class FastStringMap<K extends PropertyKey, V> { export class FastStringMap<K extends PropertyKey, V> {
private mapping = Object.create(null); private mapping = Object.create(null);