Make error checking on stdlib work again
This commit is contained in:
parent
8cb16db91a
commit
b0649fd8ed
8 changed files with 256 additions and 143 deletions
|
@ -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
41
src/ast.d.ts
vendored
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -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]);
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
}
|
179
src/types.ts
179
src/types.ts
|
@ -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;
|
||||||
|
|
28
src/util.ts
28
src/util.ts
|
@ -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}'.`);
|
||||||
|
|
Loading…
Reference in a new issue