Add missing analysis.ts
This commit is contained in:
parent
c1e17955cd
commit
21a05cf62d
1 changed files with 192 additions and 0 deletions
192
src/analysis.ts
Normal file
192
src/analysis.ts
Normal file
|
@ -0,0 +1,192 @@
|
|||
import { DirectedHashGraph, strongconnect } from "yagl";
|
||||
import { assert } from "./util";
|
||||
import { Syntax, LetDeclaration, Scope, SourceFile, SyntaxKind } from "./cst";
|
||||
|
||||
type NodeWithBlock
|
||||
= LetDeclaration
|
||||
| SourceFile
|
||||
|
||||
export class Analyser {
|
||||
|
||||
private referenceGraph = new DirectedHashGraph<NodeWithBlock>();
|
||||
|
||||
public addSourceFile(node: SourceFile): void {
|
||||
|
||||
const visit = (node: Syntax, source: NodeWithBlock) => {
|
||||
|
||||
const addReference = (scope: Scope, name: string) => {
|
||||
const target = scope.lookup(name);
|
||||
if (target === null || target.kind === SyntaxKind.Param) {
|
||||
return;
|
||||
}
|
||||
assert(target.kind === SyntaxKind.LetDeclaration || target.kind === SyntaxKind.SourceFile);
|
||||
this.referenceGraph.addEdge(source, target);
|
||||
}
|
||||
|
||||
switch (node.kind) {
|
||||
|
||||
case SyntaxKind.ConstantExpression:
|
||||
break;
|
||||
|
||||
case SyntaxKind.SourceFile:
|
||||
{
|
||||
for (const element of node.elements) {
|
||||
visit(element, source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ReferenceExpression:
|
||||
{
|
||||
if (node.name.kind === SyntaxKind.Identifier) {
|
||||
assert(node.modulePath.length === 0);
|
||||
addReference(node.getScope(), node.name.text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.MemberExpression:
|
||||
{
|
||||
visit(node.expression, source);
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.NamedTupleExpression:
|
||||
{
|
||||
for (const arg of node.elements) {
|
||||
visit(arg, source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.StructExpression:
|
||||
{
|
||||
for (const member of node.members) {
|
||||
switch (member.kind) {
|
||||
case SyntaxKind.PunnedStructExpressionField:
|
||||
{
|
||||
addReference(node.getScope(), member.name.text);
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.StructExpressionField:
|
||||
{
|
||||
visit(member.expression, source);
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.NestedExpression:
|
||||
{
|
||||
visit(node.expression, source);
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.InfixExpression:
|
||||
{
|
||||
visit(node.left, source);
|
||||
visit(node.right, source);
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.CallExpression:
|
||||
{
|
||||
visit(node.func, source);
|
||||
for (const arg of node.args) {
|
||||
visit(arg, source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.IfStatement:
|
||||
{
|
||||
for (const cs of node.cases) {
|
||||
if (cs.test !== null) {
|
||||
visit(cs.test, source);
|
||||
}
|
||||
for (const element of cs.elements) {
|
||||
visit(element, source);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
{
|
||||
visit(node.expression, source);
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.ReturnStatement:
|
||||
{
|
||||
if (node.expression !== null) {
|
||||
visit(node.expression, source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.LetDeclaration:
|
||||
{
|
||||
this.referenceGraph.addVertex(node);
|
||||
if (node.body !== null) {
|
||||
switch (node.body.kind) {
|
||||
case SyntaxKind.ExprBody:
|
||||
{
|
||||
visit(node.body.expression, node);
|
||||
break;
|
||||
}
|
||||
case SyntaxKind.BlockBody:
|
||||
{
|
||||
for (const element of node.body.elements) {
|
||||
visit(element, node);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SyntaxKind.TypeDeclaration:
|
||||
case SyntaxKind.EnumDeclaration:
|
||||
case SyntaxKind.StructDeclaration:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected ${node.constructor.name}`);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
visit(node, node);
|
||||
|
||||
}
|
||||
|
||||
public isReferencedInParentScope(node: LetDeclaration): boolean {
|
||||
const maxDepth = node.getScope().depth;
|
||||
for (const other of this.referenceGraph.getSourceVertices(node)) {
|
||||
if (other.getScope().depth < maxDepth) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a sorted list of collections where each collection contains
|
||||
* let-declarations that reference each other in some way or another.
|
||||
*
|
||||
* The declarations are sorted in such a way that declarations that reference
|
||||
* nothing come before declarations that reference another declaration. When
|
||||
* a let-declaration is not recusive, it will simply show up as a collection
|
||||
* with only one element.
|
||||
*/
|
||||
public getSortedDeclarations(): Iterable<NodeWithBlock[]> {
|
||||
return strongconnect(this.referenceGraph);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in a new issue