bolt/src/scope.ts

182 lines
4.4 KiB
TypeScript
Raw Normal View History

2023-02-03 17:51:27 +01:00
import { LetDeclaration, Pattern, SourceFile, Syntax, SyntaxKind } from "./cst";
import { MultiMap } from "./util";
export type NodeWithScope
= SourceFile
| LetDeclaration
export function isNodeWithScope(node: Syntax): node is NodeWithScope {
return node.kind === SyntaxKind.SourceFile
|| node.kind === SyntaxKind.LetDeclaration;
}
export const enum Symkind {
Var = 1,
Type = 2,
Module = 4,
Typeclass = 8,
Any = Var | Type | Module
}
export class Scope {
private mapping = new MultiMap<string, [Symkind, Syntax]>();
public constructor(
public node: NodeWithScope,
) {
this.scan(node);
}
public get depth(): number {
let out = 0;
let curr = this.getParent();
while (curr !== null) {
out++;
curr = curr.getParent();
}
return out;
}
private getParent(): Scope | null {
let curr = this.node.parent;
while (curr !== null) {
if (isNodeWithScope(curr)) {
return curr.getScope();
}
curr = curr.parent;
}
return null;
}
private add(name: string, node: Syntax, kind: Symkind): void {
this.mapping.add(name, [kind, node]);
}
private scan(node: Syntax): void {
switch (node.kind) {
case SyntaxKind.ClassDeclaration:
{
this.add(node.constraint.name.text, node, Symkind.Typeclass);
}
case SyntaxKind.InstanceDeclaration:
case SyntaxKind.SourceFile:
{
for (const element of node.elements) {
this.scan(element);
}
break;
}
case SyntaxKind.ModuleDeclaration:
{
this.add(node.name.text, node, Symkind.Module);
for (const element of node.elements) {
this.scan(element);
}
break;
}
case SyntaxKind.ExpressionStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.IfStatement:
break;
case SyntaxKind.TypeDeclaration:
{
this.add(node.name.text, node, Symkind.Type);
break;
}
case SyntaxKind.EnumDeclaration:
{
this.add(node.name.text, node, Symkind.Type);
if (node.members !== null) {
for (const member of node.members) {
this.add(member.name.text, member, Symkind.Var);
}
}
}
case SyntaxKind.StructDeclaration:
{
this.add(node.name.text, node, Symkind.Type);
this.add(node.name.text, node, Symkind.Var);
break;
}
case SyntaxKind.LetDeclaration:
{
for (const param of node.params) {
this.scanPattern(param.pattern, param);
}
if (node === this.node) {
if (node.body !== null && node.body.kind === SyntaxKind.BlockBody) {
for (const element of node.body.elements) {
this.scan(element);
}
}
} else {
if (node.pattern.kind === SyntaxKind.WrappedOperator) {
this.add(node.pattern.operator.text, node, Symkind.Var);
} else {
this.scanPattern(node.pattern, node);
}
}
break;
}
default:
throw new Error(`Unexpected ${node.constructor.name}`);
}
}
private scanPattern(node: Pattern, decl: Syntax): void {
switch (node.kind) {
case SyntaxKind.LiteralPattern:
break;
case SyntaxKind.BindPattern:
{
this.add(node.name.text, decl, Symkind.Var);
break;
}
case SyntaxKind.NamedTuplePattern:
{
for (const element of node.elements) {
this.scanPattern(element, decl);
}
break;
}
case SyntaxKind.StructPattern:
{
for (const member of node.members) {
switch (member.kind) {
case SyntaxKind.StructPatternField:
{
this.scanPattern(member.pattern, decl);
break;
}
case SyntaxKind.PunnedStructPatternField:
{
this.add(node.name.text, decl, Symkind.Var);
break;
}
}
}
break;
}
default:
console.log(node);
throw new Error(`Unexpected ${node}`);
}
}
public lookup(name: string, expectedKind: Symkind = Symkind.Any): Syntax | null {
let curr: Scope | null = this;
do {
for (const [kind, decl] of curr.mapping.get(name)) {
if (kind & expectedKind) {
return decl;
}
}
curr = curr.getParent();
} while (curr !== null);
return null;
}
}