Update code base
- Fix some issues in the parser and the AST spec - Fix invalid union types emitted by treegen - Fix and extend the Evaluator a bit - Swich API of type checker and make it store checking errors on the Syntax object itself
This commit is contained in:
parent
f765ba6115
commit
05b024c3f4
13 changed files with 1461 additions and 328 deletions
|
@ -91,7 +91,8 @@ node BoltSourceFile > SourceFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
node BoltQualName {
|
node BoltQualName {
|
||||||
modulePath: Option<BoltModulePath>,
|
isAbsolute: bool,
|
||||||
|
modulePath: Vec<BoltIdentifier>,
|
||||||
name: BoltSymbol,
|
name: BoltSymbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,8 +104,7 @@ node BoltModulePath {
|
||||||
node BoltTypeExpression;
|
node BoltTypeExpression;
|
||||||
|
|
||||||
node BoltReferenceTypeExpression > BoltTypeExpression {
|
node BoltReferenceTypeExpression > BoltTypeExpression {
|
||||||
modulePath: Option<BoltModulePath>,
|
name: BoltQualName,
|
||||||
name: BoltIdentifier,
|
|
||||||
arguments: Option<Vec<BoltTypeExpression>>,
|
arguments: Option<Vec<BoltTypeExpression>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +113,10 @@ node BoltFunctionTypeExpression > BoltTypeExpression {
|
||||||
returnType: Option<BoltTypeExpression>,
|
returnType: Option<BoltTypeExpression>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node BoltLiftedTypeExpression > BoltTypeExpression {
|
||||||
|
expression: BoltExpression,
|
||||||
|
}
|
||||||
|
|
||||||
node BoltTypeParameter {
|
node BoltTypeParameter {
|
||||||
index: usize,
|
index: usize,
|
||||||
name: BoltIdentifier,
|
name: BoltIdentifier,
|
||||||
|
@ -166,8 +170,7 @@ node BoltTupleExpression > BoltExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
node BoltReferenceExpression > BoltExpression {
|
node BoltReferenceExpression > BoltExpression {
|
||||||
modulePath: Option<BoltModulePath>,
|
name: BoltQualName,
|
||||||
name: BoltSymbol,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node BoltMemberExpression > BoltExpression {
|
node BoltMemberExpression > BoltExpression {
|
||||||
|
@ -284,7 +287,8 @@ node BoltVariableDeclaration > BoltFunctionBodyElement, BoltDeclaration {
|
||||||
node BoltImportSymbol;
|
node BoltImportSymbol;
|
||||||
|
|
||||||
node BoltPlainImportSymbol > BoltImportSymbol {
|
node BoltPlainImportSymbol > BoltImportSymbol {
|
||||||
name: BoltQualName,
|
remote: BoltQualName,
|
||||||
|
local: BoltSymbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
node BoltImportDirective > BoltSourceElement {
|
node BoltImportDirective > BoltSourceElement {
|
||||||
|
@ -296,7 +300,8 @@ node BoltImportDirective > BoltSourceElement {
|
||||||
node BoltExportSymbol;
|
node BoltExportSymbol;
|
||||||
|
|
||||||
node BoltPlainExportSymbol {
|
node BoltPlainExportSymbol {
|
||||||
name: BoltQualName,
|
local: BoltQualName,
|
||||||
|
remote: BoltSymbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
node BoltExportDirective > BoltSourceElement {
|
node BoltExportDirective > BoltSourceElement {
|
||||||
|
|
1250
src/ast.d.ts
vendored
1250
src/ast.d.ts
vendored
File diff suppressed because it is too large
Load diff
197
src/checks.ts
197
src/checks.ts
|
@ -1,13 +1,14 @@
|
||||||
import { BoltImportDirective, Syntax, BoltParameter, BoltModulePath, BoltReferenceExpression, BoltReferenceTypeExpression, BoltSourceFile, BoltCallExpression, BoltReturnKeyword, BoltReturnStatement, SyntaxKind, NodeVisitor } from "./ast";
|
import { BoltImportDirective, Syntax, BoltParameter, BoltModulePath, 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 } from "./diagnostics";
|
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 { getSymbolPathFromNode } from "./resolver"
|
import { getSymbolPathFromNode } from "./resolver"
|
||||||
import { inject } from "./di";
|
import { inject } from "./ioc";
|
||||||
import { SymbolResolver, ScopeType } from "./resolver";
|
import { SymbolResolver, ScopeType } from "./resolver";
|
||||||
import { assert } from "./util";
|
import { assert, every } from "./util";
|
||||||
import { emitNode } from "./emitter";
|
import { emitNode } from "./emitter";
|
||||||
import { TypeChecker, Type } from "./types";
|
import { TypeChecker, Type, ErrorType } from "./types";
|
||||||
import { getReturnStatementsInFunctionBody } from "./common";
|
import { getReturnStatementsInFunctionBody } from "./common";
|
||||||
|
import { errorMonitor } from "events";
|
||||||
|
|
||||||
export class CheckInvalidFilePaths extends NodeVisitor {
|
export class CheckInvalidFilePaths extends NodeVisitor {
|
||||||
|
|
||||||
|
@ -41,24 +42,65 @@ export class CheckReferences extends NodeVisitor {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkBoltModulePath(node: BoltModulePath, symbolKind: ScopeType) {
|
private checkBoltModulePath(node: BoltSyntax, elements: BoltIdentifier[]) {
|
||||||
const scope = this.resolver.getScopeForNode(node, symbolKind);
|
|
||||||
assert(scope !== null);
|
let modScope = this.resolver.getScopeForNode(node, ScopeType.Module);
|
||||||
const sym = this.resolver.resolveModulePath(node.elements.map(el => el.text), scope!);
|
assert(modScope !== null);
|
||||||
if (sym === null) {
|
let foundModule = false;
|
||||||
|
let partiallyMatchingModules = [];
|
||||||
|
|
||||||
|
// We will keep looping until we are at the topmost module of
|
||||||
|
// the package corresponding to `node`.
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
let failedToFindScope = false;
|
||||||
|
let currScope = modScope;
|
||||||
|
|
||||||
|
// Go through each of the parent names in normal order, resolving to the module
|
||||||
|
// that declared the name, and mark when we failed to look up the inner module.
|
||||||
|
for (const name of elements) {
|
||||||
|
const sym = currScope!.getLocalSymbol(name.text);;
|
||||||
|
if (sym === null) {
|
||||||
|
failedToFindScope = true;
|
||||||
|
partiallyMatchingModules.push((currScope!.source) as NodeScopeSource).node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(every(sym.declarations.values(), decl => decl.kind === SyntaxKind.BoltModule));
|
||||||
|
currScope = sym.scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the previous loop did not fail, that means we found a module.
|
||||||
|
if (!failedToFindScope) {
|
||||||
|
foundModule = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We continue the outer loop by going up one scope.
|
||||||
|
const nextScope = modScope!.getNextScope();
|
||||||
|
|
||||||
|
// If we are here and there are no scopes left to search in, then no scope had the given module.
|
||||||
|
if (nextScope === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
modScope = nextScope;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundModule) {
|
||||||
this.diagnostics.add({
|
this.diagnostics.add({
|
||||||
message: E_DECLARATION_NOT_FOUND,
|
message: E_DECLARATION_NOT_FOUND,
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
args: { name: emitNode(node) },
|
args: { name: emitNode(node) },
|
||||||
node,
|
node,
|
||||||
});
|
});
|
||||||
|
// TODO add informational diagnostics about the modules that provided a partial match
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected visitBoltReferenceExpression(node: BoltReferenceExpression) {
|
protected visitBoltReferenceExpression(node: BoltReferenceExpression) {
|
||||||
if (node.modulePath !== null) {
|
this.checkBoltModulePath(node.name, node.name.modulePath);
|
||||||
this.checkBoltModulePath(node.modulePath, ScopeType.Variable);
|
|
||||||
}
|
|
||||||
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(getSymbolPathFromNode(node), scope!);
|
||||||
|
@ -75,14 +117,14 @@ export class CheckReferences extends NodeVisitor {
|
||||||
protected visitBoltReferenceTypeExpression(node: BoltReferenceTypeExpression) {
|
protected visitBoltReferenceTypeExpression(node: BoltReferenceTypeExpression) {
|
||||||
const scope = this.resolver.getScopeForNode(node, ScopeType.Type);
|
const scope = this.resolver.getScopeForNode(node, ScopeType.Type);
|
||||||
assert(scope !== null);
|
assert(scope !== null);
|
||||||
const symbolPath = getSymbolPathFromNode(node.path);
|
const symbolPath = getSymbolPathFromNode(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({
|
||||||
message: E_TYPE_DECLARATION_NOT_FOUND,
|
message: E_TYPE_DECLARATION_NOT_FOUND,
|
||||||
args: { name: emitNode(node.path) },
|
args: { name: emitNode(node.name) },
|
||||||
severity: 'error',
|
severity: 'error',
|
||||||
node: node.path,
|
node: node.name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,59 +134,86 @@ export class CheckReferences extends NodeVisitor {
|
||||||
|
|
||||||
export class CheckTypeAssignments extends NodeVisitor {
|
export class CheckTypeAssignments extends NodeVisitor {
|
||||||
|
|
||||||
constructor(
|
constructor(@inject private diagnostics: DiagnosticPrinter) {
|
||||||
@inject private diagnostics: DiagnosticPrinter,
|
|
||||||
@inject private checker: TypeChecker,
|
|
||||||
) {
|
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected visitBoltReturnStatement(node: BoltReturnStatement) {
|
protected visitSyntax(node: Syntax) {
|
||||||
|
for (const error of node.errors) {
|
||||||
const fnDecl = node.getParentOfKind(SyntaxKind.BoltFunctionDeclaration)!;
|
switch (error.type) {
|
||||||
|
case ErrorType.AssignmentError:
|
||||||
if (node.value === null) {
|
this.diagnostics.add({
|
||||||
if (fnDecl.returnType !== null && this.checker.isVoid(fnDecl.returnType)) {
|
message: E_TYPES_NOT_ASSIGNABLE,
|
||||||
this.diagnostics.add({
|
severity: 'error',
|
||||||
message: E_MUST_RETURN_A_VALUE,
|
node: error.left,
|
||||||
node,
|
});
|
||||||
severity: 'error',
|
default:
|
||||||
});
|
throw new Error(`Could not add a diagnostic message for the error ${ErrorType[error.type]}`)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const error of this.checker.getAssignmentErrors(fnDecl.returnType, node.value)) {
|
|
||||||
this.diagnostics.add({
|
|
||||||
message: E_MUST_RETURN_A_VALUE,
|
|
||||||
node: node,
|
|
||||||
severity: 'error',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected visitBoltParameter(node: BoltParameter) {
|
|
||||||
if (node.defaultValue !== null) {
|
|
||||||
for (const error of this.checker.getAssignmentErrors(node.bindings, node.defaultValue)) {
|
|
||||||
this.diagnostics.add({
|
|
||||||
severity: 'error',
|
|
||||||
message: E_TYPES_NOT_ASSIGNABLE,
|
|
||||||
args: { node: error.node }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected visitBoltCallExpression(node: BoltCallExpression) {
|
|
||||||
for (const fnDecl of this.checker.getCallableFunctions(node)) {
|
|
||||||
for (const error of this.checker.getAssignmentErrors(fnDecl, node)) {
|
|
||||||
this.diagnostics.add({
|
|
||||||
severity: 'error',
|
|
||||||
message: E_TYPES_NOT_ASSIGNABLE,
|
|
||||||
args: { node: error.node },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export class CheckTypeAssignments extends NodeVisitor {
|
||||||
|
|
||||||
|
// constructor(
|
||||||
|
// @inject private diagnostics: DiagnosticPrinter,
|
||||||
|
// @inject private checker: TypeChecker,
|
||||||
|
// ) {
|
||||||
|
// super();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected visitBoltReturnStatement(node: BoltReturnStatement) {
|
||||||
|
|
||||||
|
// const fnDecl = node.getParentOfKind(SyntaxKind.BoltFunctionDeclaration)!;
|
||||||
|
|
||||||
|
// if ((this.checker.isVoidType(node) && !this.checker.isVoidType(fnDecl)) {
|
||||||
|
// this.diagnostics.add({
|
||||||
|
// message: E_MUST_RETURN_A_VALUE,
|
||||||
|
// node: node.value !== null ? node.value : node,
|
||||||
|
// severity: 'error',
|
||||||
|
// });
|
||||||
|
// } else if (!this.checker.isVoidType(node) && this.checker.isVoidType(fnDecl)) {
|
||||||
|
// this.diagnostics.add({
|
||||||
|
// message: E_MAY_NOT_RETURN_A_VALUE,
|
||||||
|
// node: node.value !== null ? node.value : node,
|
||||||
|
// severity: 'error',
|
||||||
|
// })
|
||||||
|
// } else {
|
||||||
|
// for (const error of this.checker.checkAssignment(fnDecl, node)) {
|
||||||
|
// this.diagnostics.add({
|
||||||
|
// message: E_TYPES_NOT_ASSIGNABLE,
|
||||||
|
// node: error.node,
|
||||||
|
// severity: 'error',
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected visitBoltParameter(node: BoltParameter) {
|
||||||
|
// if (node.defaultValue !== null) {
|
||||||
|
// for (const error of this.checker.checkAssignment(node.bindings, node.defaultValue)) {
|
||||||
|
// this.diagnostics.add({
|
||||||
|
// severity: 'error',
|
||||||
|
// message: E_TYPES_NOT_ASSIGNABLE,
|
||||||
|
// node: error.node,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// protected visitBoltCallExpression(node: BoltCallExpression) {
|
||||||
|
// for (const fnDecl of this.checker.getCallableFunctions(node)) {
|
||||||
|
// for (const error of this.checker.checkAssignment(fnDecl, node)) {
|
||||||
|
// this.diagnostics.add({
|
||||||
|
// severity: 'error',
|
||||||
|
// message: E_TYPES_NOT_ASSIGNABLE,
|
||||||
|
// node: error.node,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import chalk from "chalk"
|
||||||
import {Syntax} from "./ast";
|
import {Syntax} from "./ast";
|
||||||
import {format, MapLike, FormatArg, countDigits} from "./util";
|
import {format, MapLike, FormatArg, countDigits} from "./util";
|
||||||
|
|
||||||
|
export const E_MAY_NOT_RETURN_A_VALUE = "Returning a value inside a function that does not return values."
|
||||||
export const E_MUST_RETURN_A_VALUE = "The function must return a value on all control paths.";;;;
|
export const E_MUST_RETURN_A_VALUE = "The function must return a value on all control paths.";;;;
|
||||||
export const E_FILE_NOT_FOUND = "A file named {filename} was not found.";
|
export const E_FILE_NOT_FOUND = "A file named {filename} was not found.";
|
||||||
export const E_FIELD_HAS_INVALID_VERSION_NUMBER = "Field '{name}' contains an invalid version nunmber."
|
export const E_FIELD_HAS_INVALID_VERSION_NUMBER = "Field '{name}' contains an invalid version nunmber."
|
||||||
|
|
|
@ -15,11 +15,11 @@ export class Emitter {
|
||||||
|
|
||||||
case SyntaxKind.BoltQualName:
|
case SyntaxKind.BoltQualName:
|
||||||
if (node.modulePath !== null) {
|
if (node.modulePath !== null) {
|
||||||
if (node.modulePath.isAbsolute) {
|
if (node.isAbsolute) {
|
||||||
out += '::'
|
out += '::'
|
||||||
}
|
}
|
||||||
for (const element of node.modulePath.elements) {
|
for (const element of node.modulePath) {
|
||||||
out += '::' + element.text;
|
out += element.text + '::';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out += this.emit(node.name);
|
out += this.emit(node.name);
|
||||||
|
|
|
@ -12,6 +12,10 @@ export class Record {
|
||||||
this.fields = new Map(fields);
|
this.fields = new Map(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getFields(): IterableIterator<[string, Value]> {
|
||||||
|
return this.fields[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
public clone(): Record {
|
public clone(): Record {
|
||||||
return new Record(this.fields);
|
return new Record(this.fields);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,8 @@ export class Frontend {
|
||||||
const checkers = checks.map(check => container.createInstance(check));
|
const checkers = checks.map(check => container.createInstance(check));
|
||||||
|
|
||||||
for (const sourceFile of program.getAllSourceFiles()) {
|
for (const sourceFile of program.getAllSourceFiles()) {
|
||||||
resolver.registerSourceFile(sourceFile as BoltSourceFile);
|
checker.registerSourceFile(sourceFile);
|
||||||
|
resolver.registerSourceFile(sourceFile);
|
||||||
}
|
}
|
||||||
for (const sourceFile of program.getAllSourceFiles()) {
|
for (const sourceFile of program.getAllSourceFiles()) {
|
||||||
sourceFile.visit(checkers)
|
sourceFile.visit(checkers)
|
||||||
|
|
129
src/parser.ts
129
src/parser.ts
|
@ -80,6 +80,7 @@ import {
|
||||||
createBoltModulePath,
|
createBoltModulePath,
|
||||||
BoltModulePath,
|
BoltModulePath,
|
||||||
isBoltSymbol,
|
isBoltSymbol,
|
||||||
|
BoltIdentifierChild,
|
||||||
} from "./ast"
|
} from "./ast"
|
||||||
|
|
||||||
import { parseForeignLanguage } from "./foreign"
|
import { parseForeignLanguage } from "./foreign"
|
||||||
|
@ -109,8 +110,7 @@ function assertNoTokens(tokens: BoltTokenStream) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIND_SYMBOL = [
|
const KIND_OPERATOR = [
|
||||||
SyntaxKind.BoltIdentifier,
|
|
||||||
SyntaxKind.BoltOperator,
|
SyntaxKind.BoltOperator,
|
||||||
SyntaxKind.BoltVBar,
|
SyntaxKind.BoltVBar,
|
||||||
SyntaxKind.BoltLtSign,
|
SyntaxKind.BoltLtSign,
|
||||||
|
@ -124,7 +124,8 @@ const KIND_EXPRESSION_T0 = uniq([
|
||||||
SyntaxKind.BoltMatchKeyword,
|
SyntaxKind.BoltMatchKeyword,
|
||||||
SyntaxKind.BoltQuoteKeyword,
|
SyntaxKind.BoltQuoteKeyword,
|
||||||
SyntaxKind.BoltYieldKeyword,
|
SyntaxKind.BoltYieldKeyword,
|
||||||
...KIND_SYMBOL,
|
SyntaxKind.BoltIdentifier,
|
||||||
|
SyntaxKind.BoltParenthesized,
|
||||||
])
|
])
|
||||||
|
|
||||||
const KIND_STATEMENT_T0 = uniq([
|
const KIND_STATEMENT_T0 = uniq([
|
||||||
|
@ -239,17 +240,41 @@ export class Parser {
|
||||||
|
|
||||||
public parseQualName(tokens: BoltTokenStream): BoltQualName {
|
public parseQualName(tokens: BoltTokenStream): BoltQualName {
|
||||||
|
|
||||||
let modulePath = null;
|
const firstToken = tokens.peek();
|
||||||
if (tokens.peek(2).kind === SyntaxKind.BoltDot) {
|
let isAbsolute = false;
|
||||||
modulePath = this.parseModulePath(tokens);
|
let modulePath = [];
|
||||||
|
let name;
|
||||||
|
if (tokens.peek().kind === SyntaxKind.BoltColonColon) {
|
||||||
|
isAbsolute = true;
|
||||||
|
tokens.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const name = tokens.get();
|
while (true) {
|
||||||
assertToken(name, SyntaxKind.BoltIdentifier);
|
const t1 = tokens.get();
|
||||||
const startNode = modulePath !== null ? modulePath : name;
|
if (tokens.peek().kind === SyntaxKind.BoltColonColon) {
|
||||||
const endNode = name;
|
assertToken(t1, SyntaxKind.BoltIdentifier);
|
||||||
const node = createBoltQualName(modulePath, name as BoltIdentifier, null);
|
modulePath.push(t1 as BoltIdentifier);
|
||||||
setOrigNodeRange(node, startNode, endNode);
|
tokens.get();
|
||||||
|
} else {
|
||||||
|
if (t1.kind === SyntaxKind.BoltParenthesized) {
|
||||||
|
const innerTokens = createTokenStream(t1);
|
||||||
|
const t2 = innerTokens.get();
|
||||||
|
if (!isBoltOperatorLike(t2)) {
|
||||||
|
throw new ParseError(t2, KIND_OPERATOR);
|
||||||
|
}
|
||||||
|
assertNoTokens(innerTokens);
|
||||||
|
name = t2;
|
||||||
|
} else if (t1.kind === SyntaxKind.BoltIdentifier) {
|
||||||
|
name = t1;
|
||||||
|
} else {
|
||||||
|
throw new ParseError(t1, [SyntaxKind.BoltParenthesized, SyntaxKind.BoltIdentifier]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = createBoltQualName(isAbsolute, modulePath, name as BoltIdentifier, null);
|
||||||
|
setOrigNodeRange(node, firstToken, name);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,15 +420,29 @@ export class Parser {
|
||||||
public parseReferenceTypeExpression(tokens: BoltTokenStream): BoltReferenceTypeExpression {
|
public parseReferenceTypeExpression(tokens: BoltTokenStream): BoltReferenceTypeExpression {
|
||||||
|
|
||||||
const firstToken = tokens.peek();
|
const firstToken = tokens.peek();
|
||||||
let modulePath = null;
|
let isAbsolute = false;
|
||||||
if (tokens.peek(2).kind === SyntaxKind.BoltColonColon) {
|
let modulePath = [];
|
||||||
modulePath = this.parseModulePath(tokens)
|
let name;
|
||||||
|
|
||||||
|
if (tokens.peek().kind === SyntaxKind.BoltColonColon) {
|
||||||
|
isAbsolute = true;
|
||||||
|
tokens.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
const t1 = tokens.get();
|
while (true) {
|
||||||
assertToken(t1, SyntaxKind.BoltIdentifier);
|
const t1 = tokens.get();
|
||||||
let lastToken = t1;
|
if (tokens.peek().kind === SyntaxKind.BoltColonColon) {
|
||||||
|
assertToken(t1, SyntaxKind.BoltIdentifier);
|
||||||
|
modulePath.push(t1 as BoltIdentifier);
|
||||||
|
tokens.get();
|
||||||
|
} else {
|
||||||
|
assertToken(t1, SyntaxKind.BoltIdentifier);
|
||||||
|
name = t1 as BoltIdentifier;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastToken: BoltToken = name;
|
||||||
let typeArgs: BoltTypeExpression[] | null = null;
|
let typeArgs: BoltTypeExpression[] | null = null;
|
||||||
|
|
||||||
if (tokens.peek().kind === SyntaxKind.BoltLtSign) {
|
if (tokens.peek().kind === SyntaxKind.BoltLtSign) {
|
||||||
|
@ -428,7 +467,9 @@ export class Parser {
|
||||||
lastToken = t4;
|
lastToken = t4;
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = createBoltReferenceTypeExpression(modulePath, t1 as BoltIdentifier, typeArgs);
|
const qualName = createBoltQualName(isAbsolute, modulePath, name);
|
||||||
|
setOrigNodeRange(qualName, firstToken, modulePath.length > 0 ? modulePath[modulePath.length-1] : firstToken)
|
||||||
|
const node = createBoltReferenceTypeExpression(qualName, typeArgs);
|
||||||
setOrigNodeRange(node, firstToken, lastToken);
|
setOrigNodeRange(node, firstToken, lastToken);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
@ -536,35 +577,10 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseReferenceExpression(tokens: BoltTokenStream): BoltReferenceExpression {
|
public parseReferenceExpression(tokens: BoltTokenStream): BoltReferenceExpression {
|
||||||
|
|
||||||
const firstToken = tokens.peek();
|
const firstToken = tokens.peek();
|
||||||
let isAbsolute = false;
|
const name = this.parseQualName(tokens);
|
||||||
let elements = [];
|
const node = createBoltReferenceExpression(name);
|
||||||
|
setOrigNodeRange(node, firstToken, name);
|
||||||
while (true) {
|
|
||||||
const t2 = tokens.peek(2);
|
|
||||||
if (t2.kind !== SyntaxKind.BoltColonColon) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const t1 = tokens.get();
|
|
||||||
assertToken(t1, SyntaxKind.BoltIdentifier);
|
|
||||||
elements.push(t1 as BoltIdentifier)
|
|
||||||
tokens.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = null;
|
|
||||||
if (elements.length > 0) {
|
|
||||||
path = createBoltModulePath(isAbsolute, elements);
|
|
||||||
setOrigNodeRange(path, elements[0], elements[elements.length-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const t3 = tokens.get();
|
|
||||||
assertToken(t3, SyntaxKind.BoltIdentifier);
|
|
||||||
|
|
||||||
// TODO Add support for parsing parenthesized operators
|
|
||||||
|
|
||||||
const node = createBoltReferenceExpression(path, t3 as BoltIdentifier);
|
|
||||||
setOrigNodeRange(node, firstToken, t3);
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -631,7 +647,7 @@ export class Parser {
|
||||||
}
|
}
|
||||||
scanned.push(t2);
|
scanned.push(t2);
|
||||||
}
|
}
|
||||||
const result = createBoltQuoteExpression(scanned);
|
const result = createBoltQuoteExpression(scanned as Token[]);
|
||||||
setOrigNodeRange(result, t0, t1);
|
setOrigNodeRange(result, t0, t1);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -967,6 +983,7 @@ export class Parser {
|
||||||
public parseModuleDeclaration(tokens: BoltTokenStream): BoltModule {
|
public parseModuleDeclaration(tokens: BoltTokenStream): BoltModule {
|
||||||
|
|
||||||
let modifiers = 0;
|
let modifiers = 0;
|
||||||
|
let pathElements = [];
|
||||||
|
|
||||||
let t0 = tokens.get();
|
let t0 = tokens.get();
|
||||||
const firstToken = t0;
|
const firstToken = t0;
|
||||||
|
@ -979,16 +996,24 @@ export class Parser {
|
||||||
throw new ParseError(t0, [SyntaxKind.BoltModKeyword])
|
throw new ParseError(t0, [SyntaxKind.BoltModKeyword])
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME should fail to parse absolute paths
|
while (true) {
|
||||||
const name = this.parseModulePath(tokens);
|
const t1 = tokens.get();
|
||||||
|
assertToken(t1, SyntaxKind.BoltIdentifier)
|
||||||
|
pathElements.push(t1 as BoltIdentifier)
|
||||||
|
const t2 = tokens.peek();
|
||||||
|
if (t2.kind !== SyntaxKind.BoltColonColon) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tokens.get();
|
||||||
|
}
|
||||||
|
|
||||||
const t1 = tokens.get();
|
const t1 = tokens.get();
|
||||||
if (t1.kind !== SyntaxKind.BoltBraced) {
|
if (t1.kind !== SyntaxKind.BoltBraced) {
|
||||||
throw new ParseError(t1, [SyntaxKind.BoltBraced])
|
throw new ParseError(t1, [SyntaxKind.BoltBraced])
|
||||||
}
|
}
|
||||||
const sentences = this.parseSourceElements(createTokenStream(t1));
|
const elements = this.parseSourceElements(createTokenStream(t1));
|
||||||
|
|
||||||
const node = createBoltModule(modifiers, name.elements, sentences);
|
const node = createBoltModule(modifiers, pathElements, elements);
|
||||||
setOrigNodeRange(node, firstToken, t1);
|
setOrigNodeRange(node, firstToken, t1);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,8 @@ export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath {
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
case SyntaxKind.BoltReferenceExpression:
|
case SyntaxKind.BoltReferenceExpression:
|
||||||
return new SymbolPath(
|
return new SymbolPath(
|
||||||
node.modulePath === null ? [] : node.modulePath.elements.map(id => id.text),
|
node.name.modulePath.map(id => id.text),
|
||||||
node.modulePath !== null && node.modulePath.isAbsolute,
|
node.name.isAbsolute,
|
||||||
emitNode(node.name),
|
emitNode(node.name),
|
||||||
);
|
);
|
||||||
case SyntaxKind.BoltIdentifier:
|
case SyntaxKind.BoltIdentifier:
|
||||||
|
@ -41,7 +41,7 @@ export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath {
|
||||||
if (node.modulePath === null) {
|
if (node.modulePath === null) {
|
||||||
return new SymbolPath([], false, name);
|
return new SymbolPath([], false, name);
|
||||||
}
|
}
|
||||||
return new SymbolPath(node.modulePath.elements.map(id => id.text), false, name);
|
return new SymbolPath(node.modulePath.map(id => id.text), false, name);
|
||||||
case SyntaxKind.BoltModulePath:
|
case SyntaxKind.BoltModulePath:
|
||||||
return new SymbolPath(
|
return new SymbolPath(
|
||||||
node.elements.slice(0, -1).map(el => el.text),
|
node.elements.slice(0, -1).map(el => el.text),
|
||||||
|
@ -104,7 +104,7 @@ class NodeScopeSource implements ScopeSource {
|
||||||
|
|
||||||
interface ResolutionStrategy {
|
interface ResolutionStrategy {
|
||||||
getSymbolName(node: Syntax): string;
|
getSymbolName(node: Syntax): string;
|
||||||
getScopeType(node: Syntax): ScopeType;
|
getScopeTypes(node: Syntax): ScopeType[];
|
||||||
getNextScopeSource(source: ScopeSource, kind: ScopeType): ScopeSource | null;
|
getNextScopeSource(source: ScopeSource, kind: ScopeType): ScopeSource | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,12 +425,12 @@ export class SymbolResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getScopeSurroundingNode(node: Syntax, kind: ScopeType = this.strategy.getScopeType(node)): Scope | null {
|
public getScopeSurroundingNode(node: Syntax, kind: ScopeType): Scope | null {
|
||||||
assert(node.parentNode !== null);
|
assert(node.parentNode !== null);
|
||||||
return this.getScopeForNode(node.parentNode!, kind);
|
return this.getScopeForNode(node.parentNode!, kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getScopeForNode(node: Syntax, kind: ScopeType = this.strategy.getScopeType(node)): Scope | null {
|
public getScopeForNode(node: Syntax, kind: ScopeType): Scope | null {
|
||||||
let source: ScopeSource = new NodeScopeSource(node);
|
let source: ScopeSource = new NodeScopeSource(node);
|
||||||
if (!this.strategy.introducesNewScope(source, kind)) {
|
if (!this.strategy.introducesNewScope(source, kind)) {
|
||||||
const nextSource = this.strategy.getNextScopeSource(source, kind);
|
const nextSource = this.strategy.getNextScopeSource(source, kind);
|
||||||
|
@ -482,7 +482,7 @@ export class SymbolResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSymbolForNode(node: Syntax, kind: ScopeType = this.strategy.getScopeType(node)) {
|
public getSymbolForNode(node: Syntax, kind: ScopeType) {
|
||||||
assert(this.strategy.hasSymbol(node));
|
assert(this.strategy.hasSymbol(node));
|
||||||
const scope = this.getScopeForNode(node, kind);
|
const scope = this.getScopeForNode(node, kind);
|
||||||
if (scope === null) {
|
if (scope === null) {
|
||||||
|
|
|
@ -117,6 +117,11 @@ function createNode(nodeType) {
|
||||||
return this.__NODE_TYPE.index;
|
return this.__NODE_TYPE.index;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Object.defineProperty(obj, 'errors', {
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
value: [],
|
||||||
|
})
|
||||||
Object.defineProperty(obj, 'id', {
|
Object.defineProperty(obj, 'id', {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
import { TypeInfo } from "./types"
|
import { Type } from "./checker"
|
||||||
import { Package } from "./common"
|
import { Package } from "./common"
|
||||||
import { TextSpan } from "./text"
|
import { TextSpan } from "./text"
|
||||||
|
|
||||||
|
@ -12,7 +12,8 @@ export function isSyntax(value: any): value is Syntax;
|
||||||
interface SyntaxBase {
|
interface SyntaxBase {
|
||||||
id: number;
|
id: number;
|
||||||
kind: SyntaxKind;
|
kind: SyntaxKind;
|
||||||
_typeInfo: TypeInfo;
|
type?: Type;
|
||||||
|
errors: CompileError[]
|
||||||
parentNode: Syntax | null;
|
parentNode: Syntax | null;
|
||||||
span: TextSpan | null;
|
span: TextSpan | null;
|
||||||
visit(visitors: NodeVisitor[]): void;
|
visit(visitors: NodeVisitor[]): void;
|
||||||
|
|
|
@ -356,7 +356,7 @@ export function generateAST(decls: Declaration[]) {
|
||||||
throw new Error(`Could not emit TypeScript type for reference type node named ${typeNode.name}`);
|
throw new Error(`Could not emit TypeScript type for reference type node named ${typeNode.name}`);
|
||||||
}
|
}
|
||||||
} else if (typeNode.type === 'UnionTypeNode') {
|
} else if (typeNode.type === 'UnionTypeNode') {
|
||||||
return typeNode.elements.map(emitTypeScriptType).join(' | ');
|
return '(' + typeNode.elements.map(emitTypeScriptType).join(' | ') + ')';
|
||||||
}
|
}
|
||||||
throw new Error(`Could not emit TypeScript type for type node ${typeNode}`);
|
throw new Error(`Could not emit TypeScript type for type node ${typeNode}`);
|
||||||
}
|
}
|
||||||
|
|
152
src/types.ts
152
src/types.ts
|
@ -1,8 +1,12 @@
|
||||||
|
|
||||||
import { FastStringMap, assert, isPlainObject } from "./util";
|
import { FastStringMap, assert, isPlainObject } from "./util";
|
||||||
import { SyntaxKind, Syntax, isBoltTypeExpression, BoltExpression, BoltFunctionDeclaration, BoltFunctionBodyElement, kindToString } from "./ast";
|
import { SyntaxKind, Syntax, isBoltTypeExpression, BoltExpression, BoltFunctionDeclaration, BoltFunctionBodyElement, kindToString, SourceFile, isBoltExpression, isBoltMacroCall, BoltTypeExpression } from "./ast";
|
||||||
import { getSymbolPathFromNode, ScopeType, SymbolResolver, SymbolInfo } from "./resolver";
|
import { getSymbolPathFromNode, ScopeType, SymbolResolver, SymbolInfo } from "./resolver";
|
||||||
import { Value } from "./evaluator";
|
import { Value, Record } from "./evaluator";
|
||||||
|
|
||||||
|
// TODO For function bodies, we can do something special.
|
||||||
|
// 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`.
|
||||||
|
|
||||||
enum TypeKind {
|
enum TypeKind {
|
||||||
OpaqueType,
|
OpaqueType,
|
||||||
|
@ -24,6 +28,7 @@ export type Type
|
||||||
| RecordType
|
| RecordType
|
||||||
| VariantType
|
| VariantType
|
||||||
| TupleType
|
| TupleType
|
||||||
|
| UnionType
|
||||||
|
|
||||||
abstract class TypeBase {
|
abstract class TypeBase {
|
||||||
|
|
||||||
|
@ -91,6 +96,18 @@ export class UnionType extends TypeBase {
|
||||||
|
|
||||||
public kind: TypeKind.UnionType = TypeKind.UnionType;
|
public kind: TypeKind.UnionType = TypeKind.UnionType;
|
||||||
|
|
||||||
|
constructor(private elements: Type[] = []) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public addElement(element: Type): void {
|
||||||
|
this.elements.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getElements(): IterableIterator<Type> {
|
||||||
|
return this.elements[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RecordFieldType
|
export type RecordFieldType
|
||||||
|
@ -140,6 +157,19 @@ export class RecordType {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ErrorType {
|
||||||
|
AssignmentError,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AssignmentError {
|
||||||
|
type: ErrorType.AssignmentError;
|
||||||
|
left: Syntax;
|
||||||
|
right: Syntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CompileError
|
||||||
|
= AssignmentError
|
||||||
|
|
||||||
export class TupleType extends TypeBase {
|
export class TupleType extends TypeBase {
|
||||||
|
|
||||||
kind: TypeKind.TupleType = TypeKind.TupleType;
|
kind: TypeKind.TupleType = TypeKind.TupleType;
|
||||||
|
@ -180,17 +210,17 @@ export class TupleType extends TypeBase {
|
||||||
// return new NeverType();
|
// return new NeverType();
|
||||||
//}
|
//}
|
||||||
|
|
||||||
interface AssignmentError {
|
|
||||||
node: Syntax;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class TypeChecker {
|
export class TypeChecker {
|
||||||
|
|
||||||
private opaqueTypes = new FastStringMap<number, OpaqueType>();
|
private opaqueTypes = new FastStringMap<number, OpaqueType>();
|
||||||
|
|
||||||
|
private anyType = new AnyType();
|
||||||
private stringType = new OpaqueType();
|
private stringType = new OpaqueType();
|
||||||
private intType = new OpaqueType();
|
private intType = new OpaqueType();
|
||||||
private floatType = new OpaqueType();
|
private floatType = new OpaqueType();
|
||||||
|
private voidType = new OpaqueType();
|
||||||
|
|
||||||
|
private syntaxType = new UnionType(); // FIXME
|
||||||
|
|
||||||
constructor(private resolver: SymbolResolver) {
|
constructor(private resolver: SymbolResolver) {
|
||||||
|
|
||||||
|
@ -203,10 +233,10 @@ export class TypeChecker {
|
||||||
return this.intType;
|
return this.intType;
|
||||||
} else if (typeof(value) === 'number') {
|
} else if (typeof(value) === 'number') {
|
||||||
return this.floatType;
|
return this.floatType;
|
||||||
} else if (isPlainObject(value)) {
|
} else if (value instanceof Record) {
|
||||||
const recordType = new RecordType()
|
const recordType = new RecordType()
|
||||||
for (const key of Object.keys(value)) {
|
for (const [fieldName, fieldValue] of value.getFields()) {
|
||||||
recordType.addField(key, new PlainRecordFieldType(this.getTypeOfValue(value[key])));
|
recordType.addField(name, new PlainRecordFieldType(this.getTypeOfValue(fieldValue)));
|
||||||
}
|
}
|
||||||
return recordType;
|
return recordType;
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,44 +244,106 @@ export class TypeChecker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTypeOfNode(node: Syntax) {
|
public registerSourceFile(sourceFile: SourceFile): void {
|
||||||
|
for (const node of sourceFile.preorder()) {
|
||||||
|
if (isBoltMacroCall(node)) {
|
||||||
|
continue; // FIXME only continue when we're not in an expression context
|
||||||
|
}
|
||||||
|
if (isBoltExpression(node)) {
|
||||||
|
node.type = this.createInitialTypeForExpression(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInitialTypeForExpression(node: Syntax): Type {
|
||||||
|
|
||||||
|
if (node.type !== undefined) {
|
||||||
|
return node.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resultType;
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
|
||||||
|
case SyntaxKind.BoltMatchExpression:
|
||||||
|
{
|
||||||
|
const unionType = new UnionType();
|
||||||
|
for (const matchArm of node.arms) {
|
||||||
|
unionType.addElement(this.createInitialTypeForExpression(matchArm.body));
|
||||||
|
}
|
||||||
|
resultType = unionType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SyntaxKind.BoltRecordDeclaration:
|
case SyntaxKind.BoltRecordDeclaration:
|
||||||
{
|
{
|
||||||
const recordSym = this.resolver.getSymbolForNode(node);
|
const recordSym = this.resolver.getSymbolForNode(node, ScopeType.Type);
|
||||||
assert(recordSym !== null);
|
assert(recordSym !== null);
|
||||||
if (this.opaqueTypes.has(recordSym!.id)) {
|
if (this.opaqueTypes.has(recordSym!.id)) {
|
||||||
return this.opaqueTypes.get(recordSym!.id);
|
resultType = this.opaqueTypes.get(recordSym!.id);
|
||||||
|
} else {
|
||||||
|
const opaqueType = new OpaqueType(recordSym!);
|
||||||
|
this.opaqueTypes.set(recordSym!.id, opaqueType);
|
||||||
|
resultType = opaqueType;
|
||||||
}
|
}
|
||||||
const opaqueType = new OpaqueType(recordSym!);
|
break;
|
||||||
this.opaqueTypes.set(recordSym!.id, opaqueType);
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.BoltFunctionExpression:
|
||||||
|
{
|
||||||
|
const paramTypes = node.params.map(param => {
|
||||||
|
if (param.type === null) {
|
||||||
|
return this.anyType;
|
||||||
|
}
|
||||||
|
return this.createInitialTypeForTypeExpression(param.type);
|
||||||
|
});
|
||||||
|
let returnType = node.returnType === null
|
||||||
|
? this.anyType
|
||||||
|
: this.createInitialTypeForTypeExpression(node.returnType);
|
||||||
|
const funcType = new FunctionType(paramTypes, returnType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.BoltQuoteExpression:
|
||||||
|
return this.syntaxType;
|
||||||
|
|
||||||
|
case SyntaxKind.BoltMemberExpression:
|
||||||
|
case SyntaxKind.BoltReferenceExpression:
|
||||||
|
case SyntaxKind.BoltCallExpression:
|
||||||
|
case SyntaxKind.BoltBlockExpression:
|
||||||
|
{
|
||||||
|
resultType = this.anyType;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.BoltConstantExpression:
|
case SyntaxKind.BoltConstantExpression:
|
||||||
return this.getTypeOfValue(node.value);
|
{
|
||||||
|
resultType = this.getTypeOfValue(node.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public isVoid(node: Syntax): boolean {
|
|
||||||
switch (node.kind) {
|
|
||||||
case SyntaxKind.BoltTupleExpression:
|
|
||||||
return node.elements.length === 0;
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Could not determine whether the given type resolves to the void type.`)
|
throw new Error(`Could not create a type for node ${kindToString(node.kind)}.`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
node.type = resultType;
|
||||||
|
|
||||||
|
return resultType;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private createInitialTypeForTypeExpression(node: BoltTypeExpression): Type {
|
||||||
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.BoltLiftedTypeExpression:
|
||||||
|
return this.createInitialTypeForExpression(node.expression);
|
||||||
|
default:
|
||||||
|
throw new Error(`Could not create a type for node ${kindToString(node.kind)}.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public *getAssignmentErrors(left: Syntax, right: Syntax): IterableIterator<AssignmentError> {
|
public isVoidType(type: Type): boolean {
|
||||||
|
return type === this.voidType;
|
||||||
// TODO For function bodies, we can do something special.
|
|
||||||
// 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`.
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCallableFunctions(node: BoltExpression): BoltFunctionDeclaration[] {
|
public getCallableFunctions(node: BoltExpression): BoltFunctionDeclaration[] {
|
||||||
|
|
Loading…
Reference in a new issue