Make error checking on stdlib work again

This commit is contained in:
Sam Vervaeck 2020-05-26 22:58:31 +02:00
parent 8cb16db91a
commit b0649fd8ed
8 changed files with 256 additions and 143 deletions

View file

@ -91,7 +91,8 @@ node BoltSourceFile > SourceFile {
} }
node BoltQualName { node BoltQualName {
modulePath: Option<Vec<BoltIdentifier>>, modulePath: Option<BoltModulePath>,
name: BoltSymbol,
} }
node BoltModulePath { node BoltModulePath {

41
src/ast.d.ts vendored
View file

@ -631,13 +631,15 @@ export interface BoltIdentifier extends SyntaxBase {
} }
export type BoltIdentifierParent export type BoltIdentifierParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltIdentifierAnyParent export type BoltIdentifierAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -683,13 +685,15 @@ export interface BoltOperator extends SyntaxBase {
} }
export type BoltOperatorParent export type BoltOperatorParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltOperatorAnyParent export type BoltOperatorAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -1179,13 +1183,15 @@ export interface BoltGtSign extends SyntaxBase {
} }
export type BoltGtSignParent export type BoltGtSignParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltGtSignAnyParent export type BoltGtSignAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -1223,13 +1229,15 @@ export interface BoltExMark extends SyntaxBase {
} }
export type BoltExMarkParent export type BoltExMarkParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltExMarkAnyParent export type BoltExMarkAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -1267,13 +1275,15 @@ export interface BoltLtSign extends SyntaxBase {
} }
export type BoltLtSignParent export type BoltLtSignParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltLtSignAnyParent export type BoltLtSignAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -1311,13 +1321,15 @@ export interface BoltVBar extends SyntaxBase {
} }
export type BoltVBarParent export type BoltVBarParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| never | never
export type BoltVBarAnyParent export type BoltVBarAnyParent
= BoltQuoteExpression = BoltQualName
| BoltQuoteExpression
| BoltReferenceExpression | BoltReferenceExpression
| BoltFunctionDeclaration | BoltFunctionDeclaration
| BoltSourceFile | BoltSourceFile
@ -2342,7 +2354,8 @@ export type BoltSourceFileChild
export interface BoltQualName extends SyntaxBase { export interface BoltQualName extends SyntaxBase {
kind: SyntaxKind.BoltQualName; kind: SyntaxKind.BoltQualName;
modulePath: BoltIdentifier[] | null; modulePath: BoltModulePath | null;
name: BoltSymbol;
parentNode: BoltQualNameParent; parentNode: BoltQualNameParent;
getChildNodes(): IterableIterator<BoltQualNameChild> getChildNodes(): IterableIterator<BoltQualNameChild>
} }
@ -6936,7 +6949,7 @@ export function createBoltParenthesized(text: string, span?: TextSpan | null): B
export function createBoltBraced(text: string, span?: TextSpan | null): BoltBraced; export function createBoltBraced(text: string, span?: TextSpan | null): BoltBraced;
export function createBoltBracketed(text: string, span?: TextSpan | null): BoltBracketed; export function createBoltBracketed(text: string, span?: TextSpan | null): BoltBracketed;
export function createBoltSourceFile(elements: BoltSourceElement[], package: Package, span?: TextSpan | null): BoltSourceFile; export function createBoltSourceFile(elements: BoltSourceElement[], package: Package, span?: TextSpan | null): BoltSourceFile;
export function createBoltQualName(modulePath: BoltIdentifier[] | null, span?: TextSpan | null): BoltQualName; export function createBoltQualName(modulePath: BoltModulePath | null, name: BoltSymbol, span?: TextSpan | null): BoltQualName;
export function createBoltModulePath(isAbsolute: boolean, elements: BoltIdentifier[], span?: TextSpan | null): BoltModulePath; export function createBoltModulePath(isAbsolute: boolean, elements: BoltIdentifier[], span?: TextSpan | null): BoltModulePath;
export function createBoltReferenceTypeExpression(path: BoltModulePath, arguments: BoltTypeExpression[] | null, span?: TextSpan | null): BoltReferenceTypeExpression; export function createBoltReferenceTypeExpression(path: BoltModulePath, arguments: BoltTypeExpression[] | null, span?: TextSpan | null): BoltReferenceTypeExpression;
export function createBoltFunctionTypeExpression(params: BoltParameter[], returnType: BoltTypeExpression | null, span?: TextSpan | null): BoltFunctionTypeExpression; export function createBoltFunctionTypeExpression(params: BoltParameter[], returnType: BoltTypeExpression | null, span?: TextSpan | null): BoltFunctionTypeExpression;

View file

@ -32,7 +32,7 @@ export class CheckInvalidFilePaths extends NodeVisitor {
} }
export class CheckReference extends NodeVisitor { export class CheckReferences extends NodeVisitor {
constructor( constructor(
@inject private diagnostics: DiagnosticPrinter, @inject private diagnostics: DiagnosticPrinter,
@ -75,7 +75,7 @@ export class CheckReference 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 resolvedSym = this.resolver.resolveSymbolPath(getSymbolPathFromNode(node), scope!); const resolvedSym = this.resolver.resolveSymbolPath(getSymbolPathFromNode(node.path), 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,

View file

@ -4,44 +4,39 @@ import { FastStringMap, assert } from "./util"
import { emitNode } from "./emitter"; import { emitNode } from "./emitter";
import { Type, TypeChecker, RecordType } from "./types"; import { Type, TypeChecker, RecordType } from "./types";
export interface Value { //export class Record {
readonly type?: Type;
data: ValueData;
}
class Record { // private fields: Map<string, Value>;
private fields: Map<string, Value>; // constructor(fields: Iterable<[string, Value]>) {
// this.fields = new Map(fields);
// }
constructor(fields: Iterable<[string, Value]>) { // public clone(): Record {
this.fields = new Map(fields); // return new Record(this.fields);
} // }
public clone(): Record { // public addField(name: string, value: Value): void {
return new Record(this.fields); // this.fields.set(name, value);
} // }
public addField(name: string, value: Value): void { // public deleteField(name: string): void {
this.fields.set(name, value); // this.fields.delete(name);
} // }
public deleteField(name: string): void { // public clear(): void {
this.fields.delete(name); // this.fields.clear();
} // }
public clear(): void { //}
this.fields.clear();
}
} export type Value
type ValueData
= string = string
| undefined | undefined
| boolean | boolean
| number | number
| bigint | bigint
| Record | object
class Environment { class Environment {

View file

@ -6,7 +6,7 @@ import { EventEmitter } from "events"
import { Program } from "./program" import { Program } from "./program"
import { emitNode } from "./emitter" import { emitNode } from "./emitter"
import { Syntax, BoltSourceFile, SourceFile, NodeVisitor } from "./ast" import { Syntax, BoltSourceFile, SourceFile, NodeVisitor, createBoltConditionalCase } from "./ast"
import { getFileStem, MapLike } from "./util" import { getFileStem, MapLike } from "./util"
import { verbose, memoize } from "./util" import { verbose, memoize } from "./util"
import { Container, Newable } from "./di" import { Container, Newable } from "./di"
@ -17,7 +17,7 @@ import { TransformManager } from "./transforms/index"
import {DiagnosticPrinter} from "./diagnostics" import {DiagnosticPrinter} from "./diagnostics"
import { TypeChecker } from "./types" import { TypeChecker } from "./types"
import { checkServerIdentity } from "tls" import { checkServerIdentity } from "tls"
import { CheckInvalidFilePaths, CheckTypeAssignments } from "./checks" import { CheckInvalidFilePaths, CheckTypeAssignments, CheckReferences } from "./checks"
import { SymbolResolver, BoltSymbolResolutionStrategy } from "./resolver" import { SymbolResolver, BoltSymbolResolutionStrategy } from "./resolver"
const targetExtensions: MapLike<string> = { const targetExtensions: MapLike<string> = {
@ -84,6 +84,7 @@ export class Frontend {
public check(program: Program) { public check(program: Program) {
const resolver = new SymbolResolver(program, new BoltSymbolResolutionStrategy); const resolver = new SymbolResolver(program, new BoltSymbolResolutionStrategy);
const checker = new TypeChecker(resolver); const checker = new TypeChecker(resolver);
@ -91,9 +92,11 @@ export class Frontend {
container.bindSelf(program); container.bindSelf(program);
container.bindSelf(resolver); container.bindSelf(resolver);
container.bindSelf(checker); container.bindSelf(checker);
container.bindSelf(this.diagnostics);
const checks: Newable<NodeVisitor>[] = [ const checks: Newable<NodeVisitor>[] = [
CheckInvalidFilePaths, CheckInvalidFilePaths,
CheckReferences,
CheckTypeAssignments, CheckTypeAssignments,
]; ];

View file

@ -28,6 +28,12 @@ export class SymbolPath {
export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath { export function getSymbolPathFromNode(node: BoltSyntax): SymbolPath {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltReferenceExpression:
return new SymbolPath(
node.modulePath === null ? [] : node.modulePath.elements.map(id => id.text),
node.modulePath !== null && node.modulePath.isAbsolute,
emitNode(node.name),
);
case SyntaxKind.BoltIdentifier: case SyntaxKind.BoltIdentifier:
return new SymbolPath([], false, emitNode(node)); return new SymbolPath([], false, emitNode(node));
case SyntaxKind.BoltQualName: case SyntaxKind.BoltQualName:
@ -160,7 +166,7 @@ export class BoltSymbolResolutionStrategy implements ResolutionStrategy {
} }
} }
public introducesNewScope(node: BoltSyntax, kind: ScopeType): boolean { public introducesNewScope(node: Syntax, kind: ScopeType): boolean {
switch (kind) { switch (kind) {
case ScopeType.Variable: case ScopeType.Variable:
return node.kind === SyntaxKind.BoltSourceFile return node.kind === SyntaxKind.BoltSourceFile
@ -185,6 +191,11 @@ export class BoltSymbolResolutionStrategy implements ResolutionStrategy {
public *getNextScopeSources(source: ScopeSource, kind: ScopeType): IterableIterator<ScopeSource> { public *getNextScopeSources(source: ScopeSource, kind: ScopeType): IterableIterator<ScopeSource> {
// If we are in the global scope, there is no scope above it.
if (source instanceof GlobalScopeSource) {
return;
}
// If we are at a scope that was created by an AST node, we // If we are at a scope that was created by an AST node, we
// search the nearest parent that introduces a new scope of // search the nearest parent that introduces a new scope of
// the requested kind. If no such scope was found, then we // the requested kind. If no such scope was found, then we
@ -196,12 +207,13 @@ export class BoltSymbolResolutionStrategy implements ResolutionStrategy {
yield new PackageScopeSource(currNode.package); yield new PackageScopeSource(currNode.package);
return; return;
} }
if (this.introducesNewScope(currNode, kind)) { const nextNode = currNode.parentNode;
yield source; assert(nextNode !== null);
if (this.introducesNewScope(nextNode, kind)) {
yield new NodeScopeSource(nextNode);
return; return;
} }
assert(currNode.parentNode !== null); currNode = nextNode;
currNode = currNode.parentNode;
} }
} }
@ -212,15 +224,14 @@ export class BoltSymbolResolutionStrategy implements ResolutionStrategy {
return; return;
} }
// If we are in the global scope, there is no scope above it. throw new Error(`Unknown scope source provided.`)
if (source instanceof GlobalScopeSource) {
return;
}
} }
} }
let nextSymbolId = 1;
class Scope { class Scope {
private static scopeCache = new FastStringMap<string, Scope>(); private static scopeCache = new FastStringMap<string, Scope>();
@ -245,7 +256,7 @@ class Scope {
return Scope.scopeCache.get(this.globallyUniqueKey); return Scope.scopeCache.get(this.globallyUniqueKey);
} }
const newScope = new Scope(this.resolver, kind, this.source); const newScope = new Scope(this.resolver, kind, this.source);
Scope.scopeCache.set(this.globallyUniqueKey, newScope); Scope.scopeCache.set(newScope.globallyUniqueKey, newScope);
return newScope; return newScope;
} }
@ -254,10 +265,10 @@ class Scope {
for (const nextSource of this.resolver.strategy.getNextScopeSources(this.source, this.kind)) { for (const nextSource of this.resolver.strategy.getNextScopeSources(this.source, this.kind)) {
const key = `${this.kind}:${nextSource.id}`; const key = `${this.kind}:${nextSource.id}`;
if (Scope.scopeCache.has(key)) { if (Scope.scopeCache.has(key)) {
yield Scope.scopeCache.get(this.globallyUniqueKey); yield Scope.scopeCache.get(key);
} else { } else {
const newScope = new Scope(this.resolver, this.kind, nextSource); const newScope = new Scope(this.resolver, this.kind, nextSource);
Scope.scopeCache.set(this.globallyUniqueKey, newScope); Scope.scopeCache.set(key, newScope);
yield newScope; yield newScope;
} }
} }
@ -304,6 +315,7 @@ class Scope {
} }
} else { } else {
const sym = { const sym = {
id: nextSymbolId++,
name, name,
scope: this, scope: this,
declarations: new Set([ node ]), declarations: new Set([ node ]),
@ -315,7 +327,8 @@ class Scope {
} }
interface SymbolInfo { export interface SymbolInfo {
id: number;
name: string; name: string;
scope: Scope; scope: Scope;
isExported: boolean; isExported: boolean;
@ -409,22 +422,16 @@ export class SymbolResolver {
// We will keep looping until we are at the topmost module of // We will keep looping until we are at the topmost module of
// the package corresponding to `node`. // the package corresponding to `node`.
while (true) { while (stack.length > 0) {
// An empty stack means we've looked everywhere but did not find a scope that contained
// the given module path.
if (stack.length === 0) {
return null;
}
let shouldSearchNextScopes = false; let shouldSearchNextScopes = false;
let currScope = stack.pop()!; let scope = stack.pop()!;
let currScope = scope;
// Go through each of the parent names in normal order, resolving to the module // 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. // that declared the name, and mark when we failed to look up the inner module.
for (const name of path) { for (const name of path) {
const sym = currScope.getSymbol(name); const sym = currScope.getLocalSymbol(name);
if (sym === null) { if (sym === null) {
shouldSearchNextScopes = true; shouldSearchNextScopes = true;
break; break;
@ -435,19 +442,26 @@ export class SymbolResolver {
// If the previous loop did not fail, we are done. // If the previous loop did not fail, we are done.
if (!shouldSearchNextScopes) { if (!shouldSearchNextScopes) {
scope = currScope; return currScope;
break;
} }
// We continue the outer loop by getting the parent module, which should be // We continue the outer loop by going up one scope.
// equivalent to getting the parent module scope.
for (const nextScope of scope.getNextScopes()) { for (const nextScope of scope.getNextScopes()) {
stack.push(nextScope); stack.push(nextScope);
} }
} }
return scope; return null;
}
public getSymbolForNode(node: Syntax) {
assert(this.strategy.hasSymbol(node));
const scope = this.getScopeForNode(node, this.strategy.getScopeType(node));
if (scope === null) {
return null;
}
return scope.getSymbol(this.strategy.getSymbolName(node));
} }
public resolveSymbolPath(path: SymbolPath, scope: Scope): SymbolInfo | null { public resolveSymbolPath(path: SymbolPath, scope: Scope): SymbolInfo | null {
@ -486,12 +500,4 @@ export class SymbolResolver {
return sym; return sym;
} }
//public resolveTypeName(name: string, node: Syntax): Type | null {
// const sym = this.findSymbolInScopeOf(name, this.getScopeSurroundingNode(node));
// if (sym === null) {
// return null;
// }
// return this.getTypeOfNode(sym.declarations[0]);
//}
} }

View file

@ -1,7 +1,8 @@
import { FastStringMap, assert } 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 } from "./ast";
import { getSymbolPathFromNode, ScopeType, SymbolResolver } from "./resolver"; import { getSymbolPathFromNode, ScopeType, SymbolResolver, SymbolInfo } from "./resolver";
import { Value } from "./evaluator";
enum TypeKind { enum TypeKind {
OpaqueType, OpaqueType,
@ -9,6 +10,7 @@ enum TypeKind {
NeverType, NeverType,
FunctionType, FunctionType,
RecordType, RecordType,
PlainRecordFieldType,
VariantType, VariantType,
UnionType, UnionType,
TupleType, TupleType,
@ -24,24 +26,32 @@ export type Type
| TupleType | TupleType
abstract class TypeBase { abstract class TypeBase {
abstract kind: TypeKind;
public abstract kind: TypeKind;
constructor(public symbol?: SymbolInfo) {
}
} }
export class OpaqueType extends TypeBase { export class OpaqueType extends TypeBase {
kind: TypeKind.OpaqueType = TypeKind.OpaqueType;
public kind: TypeKind.OpaqueType = TypeKind.OpaqueType;
} }
export class AnyType extends TypeBase { export class AnyType extends TypeBase {
kind: TypeKind.AnyType = TypeKind.AnyType; public kind: TypeKind.AnyType = TypeKind.AnyType;
} }
export class NeverType extends TypeBase { export class NeverType extends TypeBase {
kind: TypeKind.NeverType = TypeKind.NeverType; public kind: TypeKind.NeverType = TypeKind.NeverType;
} }
export class FunctionType extends TypeBase { export class FunctionType extends TypeBase {
kind: TypeKind.FunctionType = TypeKind.FunctionType; public kind: TypeKind.FunctionType = TypeKind.FunctionType;
constructor( constructor(
public paramTypes: Type[], public paramTypes: Type[],
@ -54,7 +64,7 @@ export class FunctionType extends TypeBase {
return this.paramTypes.length; return this.paramTypes.length;
} }
public getParamTypeAtIndex(index: number) { public getTypeAtParameterIndex(index: number) {
if (index < 0 || index >= this.paramTypes.length) { if (index < 0 || index >= this.paramTypes.length) {
throw new Error(`Could not get the parameter type at index ${index} because the index was out of bounds.`); throw new Error(`Could not get the parameter type at index ${index} because the index was out of bounds.`);
} }
@ -65,7 +75,7 @@ export class FunctionType extends TypeBase {
export class VariantType extends TypeBase { export class VariantType extends TypeBase {
kind: TypeKind.VariantType = TypeKind.VariantType; public kind: TypeKind.VariantType = TypeKind.VariantType;
constructor(public elementTypes: Type[]) { constructor(public elementTypes: Type[]) {
super(); super();
@ -77,36 +87,57 @@ export class VariantType extends TypeBase {
} }
export function isVariantType(value: any): value is VariantType { export class UnionType extends TypeBase {
return value instanceof VariantType;
public kind: TypeKind.UnionType = TypeKind.UnionType;
}
export type RecordFieldType
= PlainRecordFieldType
class PlainRecordFieldType extends TypeBase {
public kind: TypeKind.PlainRecordFieldType = TypeKind.PlainRecordFieldType;
constructor(public type: Type) {
super();
}
} }
export class RecordType { export class RecordType {
kind: TypeKind.RecordType = TypeKind.RecordType; public kind: TypeKind.RecordType = TypeKind.RecordType;
private fieldTypes = new FastStringMap<string, Type>(); private fieldTypes = new FastStringMap<string, RecordFieldType>();
constructor( constructor(
iterable: IterableIterator<[string, Type]>, iterable?: Iterable<[string, RecordFieldType]>,
) { ) {
if (iterable !== undefined) {
for (const [name, type] of iterable) { for (const [name, type] of iterable) {
this.fieldTypes.set(name, type); this.fieldTypes.set(name, type);
} }
} }
}
public addField(name: string, type: RecordFieldType): void {
this.fieldTypes.set(name, type);
}
public hasField(name: string) { public hasField(name: string) {
return name in this.fieldTypes; return name in this.fieldTypes;
} }
public getTypeOfField(name: string) { public getFieldType(name: string) {
return this.fieldTypes.get(name); return this.fieldTypes.get(name);
} }
public clear(): void {
this.fieldTypes.clear();
} }
export function isRecordType(value: any): value is RecordType {
return value.kind === TypeKind.RecordType;
} }
export class TupleType extends TypeBase { export class TupleType extends TypeBase {
@ -119,45 +150,35 @@ export class TupleType extends TypeBase {
} }
export function isTupleType(value: any): value is TupleType { //export function narrowType(outer: Type, inner: Type): Type {
return value.kind === TypeKind.TupleType; // if (isAnyType(outer) || isNeverType(inner)) {
} // return inner;
// }
// // TODO cover the other cases
// return outer;
//}
export function isVoidType(value: any) { //export function intersectTypes(a: Type, b: Type): Type {
return isTupleType(value) && value.elementTypes.length === 0; // if (a.kind === TypeKind.NeverType && b.kind === TypeKind.NeverType)
} // return new NeverType();
// }
export function narrowType(outer: Type, inner: Type): Type { // if (a.kind == TypeKind.AnyType) {
if (isAnyType(outer) || isNeverType(inner)) { // return a
return inner; // }
} // if (isAnyType(a)) {
// TODO cover the other cases // return b;
return outer; // }
} // if (a.kind === TypeKind.FunctionType && b.kind === TypeKind.FunctionType) {
// if (a.paramTypes.length !== b.paramTypes.length) {
export function intersectTypes(a: Type, b: Type): Type { // return new NeverType();
if (isNeverType(a) || isNeverType(b)) { // }
return new NeverType(); // const returnType = intersectTypes(a.returnType, b.returnType);
} // const paramTypes = a.paramTypes
if (isAnyType(b)) { // .map((_, i) => intersectTypes(a.paramTypes[i], b.paramTypes[i]));
return a // return new FunctionType(paramTypes, returnType)
} // }
if (isAnyType(a)) { // return new NeverType();
return b; //}
}
if (isFunctionType(a) && isFunctionType(b)) {
if (a.paramTypes.length !== b.paramTypes.length) {
return new NeverType();
}
const returnType = intersectTypes(a.returnType, b.returnType);
const paramTypes = a.paramTypes
.map((_, i) => intersectTypes(a.paramTypes[i], b.paramTypes[i]));
return new FunctionType(paramTypes, returnType)
}
return new NeverType();
}
export type TypeInfo = never;
interface AssignmentError { interface AssignmentError {
node: Syntax; node: Syntax;
@ -165,10 +186,57 @@ interface AssignmentError {
export class TypeChecker { export class TypeChecker {
private opaqueTypes = new FastStringMap<number, OpaqueType>();
private stringType = new OpaqueType();
private intType = new OpaqueType();
private floatType = new OpaqueType();
constructor(private resolver: SymbolResolver) { constructor(private resolver: SymbolResolver) {
} }
public getTypeOfValue(value: Value): Type {
if (typeof(value) === 'string') {
return this.stringType;
} else if (typeof(value) === 'bigint') {
return this.intType;
} else if (typeof(value) === 'number') {
return this.floatType;
} else if (isPlainObject(value)) {
const recordType = new RecordType()
for (const key of Object.keys(value)) {
recordType.addField(key, new PlainRecordFieldType(this.getTypeOfValue(value[key])));
}
return recordType;
} else {
throw new Error(`Could not determine type of given value.`);
}
}
public getTypeOfNode(node: Syntax) {
switch (node.kind) {
case SyntaxKind.BoltRecordDeclaration:
{
const recordSym = this.resolver.getSymbolForNode(node);
assert(recordSym !== null);
if (this.opaqueTypes.has(recordSym!.id)) {
return this.opaqueTypes.get(recordSym!.id);
}
const opaqueType = new OpaqueType(recordSym!);
this.opaqueTypes.set(recordSym!.id, opaqueType);
break;
}
case SyntaxKind.BoltConstantExpression:
return this.getTypeOfValue(node.value);
}
}
public isVoid(node: Syntax): boolean { public isVoid(node: Syntax): boolean {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltTupleExpression: case SyntaxKind.BoltTupleExpression:
@ -186,7 +254,6 @@ export class TypeChecker {
} }
public getCallableFunctions(node: BoltExpression): BoltFunctionDeclaration[] { public getCallableFunctions(node: BoltExpression): BoltFunctionDeclaration[] {
const resolver = this.resolver; const resolver = this.resolver;

View file

@ -72,11 +72,39 @@ export function uniq<T>(elements: T[]): T[] {
return out; return out;
} }
function getTag(value: any) {
if (value == null) {
return value === undefined ? '[object Undefined]' : '[object Null]'
}
return toString.call(value)
}
function isObjectLike(value: any) {
return typeof value === 'object' && value !== null;
}
export function isPlainObject(value: any): value is object {
if (!isObjectLike(value) || getTag(value) != '[object Object]') {
return false
}
if (Object.getPrototypeOf(value) === null) {
return true
}
let proto = value
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(value) === proto
}
export class FastStringMap<K extends PropertyKey, V> { export class FastStringMap<K extends PropertyKey, V> {
private mapping = Object.create(null); private mapping = Object.create(null);
public clear(): void {
this.mapping.clear();
}
public get(key: K): V { public get(key: K): V {
if (!(key in this.mapping)) { if (!(key in this.mapping)) {
throw new Error(`No value found for key '${key}'.`); throw new Error(`No value found for key '${key}'.`);