2020-02-25 17:55:17 +01:00
|
|
|
|
|
|
|
import {
|
|
|
|
Syntax,
|
2020-02-25 18:34:17 +01:00
|
|
|
SyntaxKind,
|
|
|
|
ImportDecl,
|
2020-02-26 18:53:28 +01:00
|
|
|
isNode,
|
2020-02-25 17:55:17 +01:00
|
|
|
} from "./ast"
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
import { FastStringMap } from "./util"
|
|
|
|
|
|
|
|
export class Type {
|
2020-02-25 17:55:17 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
export class PrimType extends Type {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export class VariantType extends Type {
|
|
|
|
|
|
|
|
constructor(public elementTypes: Type[]) {
|
|
|
|
super();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export const stringType = new PrimType()
|
|
|
|
export const intType = new PrimType()
|
|
|
|
export const boolType = new PrimType()
|
|
|
|
export const voidType = new PrimType()
|
|
|
|
|
|
|
|
export class RecordType {
|
|
|
|
|
|
|
|
fieldTypes: FastStringMap<Type> = Object.create(null);
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
iterable: IterableIterator<[string, Type]>,
|
|
|
|
) {
|
|
|
|
for (const [name, typ] of iterable) {
|
|
|
|
this.fieldTypes[name] = typ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hasField(name: string) {
|
|
|
|
return name in this.fieldTypes;
|
|
|
|
}
|
|
|
|
|
|
|
|
getTypeOfField(name: string) {
|
|
|
|
if (name in this.fieldTypes) {
|
|
|
|
return this.fieldTypes[name]
|
|
|
|
}
|
|
|
|
throw new Error(`Field '${name}' does not exist on this record type.`)
|
|
|
|
}
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export class Scope {
|
|
|
|
|
|
|
|
constructor(public origin: Syntax) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
function* map<T, R>(iterable: Iterable<T>, proc: (value: T) => R): IterableIterator<R> {
|
|
|
|
const iterator = iterable[Symbol.iterator]();
|
|
|
|
while (true) {
|
|
|
|
let { done, value }= iterator.next();
|
|
|
|
if (done) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
yield proc(value)
|
|
|
|
}
|
|
|
|
}
|
2020-02-25 17:55:17 +01:00
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
function getFullName(node: Syntax) {
|
|
|
|
let out = []
|
|
|
|
let curr: Syntax | null = node;
|
|
|
|
while (true) {
|
|
|
|
switch (curr.kind) {
|
|
|
|
case SyntaxKind.Module:
|
|
|
|
out.unshift(curr.name.fullText);
|
|
|
|
break;
|
|
|
|
case SyntaxKind.RecordDecl:
|
|
|
|
out.unshift(curr.name.fullText)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
curr = curr.parentNode;
|
|
|
|
if (curr === null) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out.join('.');
|
|
|
|
}
|
|
|
|
|
|
|
|
export class TypeChecker {
|
2020-02-25 17:55:17 +01:00
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
protected symbols: FastStringMap<Type> = Object.create(null)
|
|
|
|
protected types = new Map<Syntax, Type>();
|
2020-02-25 17:55:17 +01:00
|
|
|
protected scopes = new Map<Syntax, Scope>();
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
protected createType(node: Syntax): Type {
|
|
|
|
|
|
|
|
console.log(node)
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
switch (node.kind) {
|
2020-02-26 18:53:28 +01:00
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
case SyntaxKind.ConstExpr:
|
2020-02-26 18:53:28 +01:00
|
|
|
return node.value.type;
|
|
|
|
|
|
|
|
case SyntaxKind.RecordDecl:
|
|
|
|
|
|
|
|
const typ = new RecordType(map(node.fields, ([name, typ]) => ([name.text, typ])));
|
|
|
|
|
|
|
|
this.symbols[getFullName(node)] = typ;
|
|
|
|
|
|
|
|
return typ;
|
|
|
|
|
|
|
|
// if (typeof node.value === 'bigint') {
|
|
|
|
// return intType;
|
|
|
|
// } else if (typeof node.value === 'string') {
|
|
|
|
// return stringType;
|
|
|
|
// } else if (typeof node.value === 'boolean') {
|
|
|
|
// return boolType;
|
|
|
|
// } else if (isNode(node.value)) {
|
|
|
|
// return this.getTypeNamed(`Bolt.AST.${SyntaxKind[node.value.kind]}`)!
|
|
|
|
// } else {
|
|
|
|
// throw new Error(`Unrecognised kind of value associated with ConstExpr`)
|
|
|
|
// }
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`Could not derive type of ${SyntaxKind[node.kind]}`)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
getTypeNamed(name: string) {
|
|
|
|
return name in this.typeNames
|
|
|
|
? this.typeNames[name]
|
|
|
|
: null
|
|
|
|
}
|
|
|
|
|
|
|
|
getTypeOfNode(node: Syntax): Type {
|
|
|
|
if (this.types.has(node)) {
|
|
|
|
return this.types.get(node)!
|
|
|
|
}
|
|
|
|
const newType = this.createType(node)
|
|
|
|
this.types.set(node, newType)
|
|
|
|
return newType;
|
|
|
|
}
|
|
|
|
|
|
|
|
check(node: Syntax) {
|
|
|
|
|
|
|
|
switch (node.kind) {
|
|
|
|
|
|
|
|
case SyntaxKind.Sentence:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyntaxKind.RecordDecl:
|
|
|
|
this.getTypeOfNode(node);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SyntaxKind.Module:
|
|
|
|
case SyntaxKind.SourceFile:
|
|
|
|
for (const element of node.elements) {
|
|
|
|
this.check(element)
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`Could not type-check node ${SyntaxKind[node.kind]}`)
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
|
|
|
|
2020-02-25 18:34:17 +01:00
|
|
|
getImportedSymbols(node: ImportDecl) {
|
|
|
|
return [{ name: 'fac' }]
|
|
|
|
}
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
getScope(node: Syntax): Scope {
|
|
|
|
while (node.kind !== SyntaxKind.FuncDecl && node.kind !== SyntaxKind.SourceFile) {
|
|
|
|
node = node.parentNode!;
|
|
|
|
}
|
|
|
|
if (this.scopes.has(node)) {
|
|
|
|
return this.scopes.get(node)!
|
|
|
|
}
|
|
|
|
const scope = new Scope(node)
|
|
|
|
this.scopes.set(node, scope)
|
|
|
|
return scope
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
// getMapperForNode(target: string, node: Syntax): Mapper {
|
|
|
|
// return this.getScope(node).getMapper(target)
|
|
|
|
// }
|
2020-02-25 17:55:17 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|