Add experimental support for referencing module elements
This commit is contained in:
parent
9dc62d0836
commit
3e169e9ab9
4 changed files with 209 additions and 61 deletions
169
src/checker.ts
169
src/checker.ts
|
@ -1,14 +1,21 @@
|
||||||
import {
|
import {
|
||||||
|
CustomOperator,
|
||||||
EnumDeclaration,
|
EnumDeclaration,
|
||||||
Expression,
|
Expression,
|
||||||
|
ExprOperator,
|
||||||
|
Identifier,
|
||||||
|
IdentifierAlt,
|
||||||
LetDeclaration,
|
LetDeclaration,
|
||||||
|
ModuleDeclaration,
|
||||||
Pattern,
|
Pattern,
|
||||||
|
ReferenceExpression,
|
||||||
|
ReferenceTypeExpression,
|
||||||
SourceFile,
|
SourceFile,
|
||||||
StructDeclaration,
|
StructDeclaration,
|
||||||
Symkind,
|
Symkind,
|
||||||
Syntax,
|
Syntax,
|
||||||
SyntaxKind,
|
SyntaxKind,
|
||||||
TypeExpression
|
TypeExpression,
|
||||||
} from "./cst";
|
} from "./cst";
|
||||||
import {
|
import {
|
||||||
describeType,
|
describeType,
|
||||||
|
@ -18,6 +25,7 @@ import {
|
||||||
FieldMissingDiagnostic,
|
FieldMissingDiagnostic,
|
||||||
UnificationFailedDiagnostic,
|
UnificationFailedDiagnostic,
|
||||||
KindMismatchDiagnostic,
|
KindMismatchDiagnostic,
|
||||||
|
ModuleNotFoundDiagnostic,
|
||||||
} from "./diagnostics";
|
} from "./diagnostics";
|
||||||
import { assert, isEmpty, MultiMap } from "./util";
|
import { assert, isEmpty, MultiMap } from "./util";
|
||||||
import { Analyser } from "./analysis";
|
import { Analyser } from "./analysis";
|
||||||
|
@ -682,6 +690,13 @@ class Forall extends SchemeBase {
|
||||||
export type Scheme
|
export type Scheme
|
||||||
= Forall
|
= Forall
|
||||||
|
|
||||||
|
type NodeWithReference
|
||||||
|
= Identifier
|
||||||
|
| IdentifierAlt
|
||||||
|
| ExprOperator
|
||||||
|
| ReferenceExpression
|
||||||
|
| ReferenceTypeExpression
|
||||||
|
|
||||||
export class TypeEnv {
|
export class TypeEnv {
|
||||||
|
|
||||||
private mapping = new MultiMap<string, [Symkind, Scheme]>();
|
private mapping = new MultiMap<string, [Symkind, Scheme]>();
|
||||||
|
@ -694,16 +709,12 @@ export class TypeEnv {
|
||||||
this.mapping.add(name, [kind, scheme]);
|
this.mapping.add(name, [kind, scheme]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public lookup(name: string, expectedKind: Symkind): Scheme | null {
|
public get(name: string, expectedKind: Symkind): Scheme | null {
|
||||||
let curr: TypeEnv | null = this;
|
for (const [kind, scheme] of this.mapping.get(name)) {
|
||||||
do {
|
if (kind & expectedKind) {
|
||||||
for (const [kind, scheme] of curr.mapping.get(name)) {
|
return scheme;
|
||||||
if (kind & expectedKind) {
|
|
||||||
return scheme;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
curr = curr.parent;
|
}
|
||||||
} while(curr !== null);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,8 +816,64 @@ export class Checker {
|
||||||
this.contexts.pop();
|
this.contexts.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private lookup(name: string, kind: Symkind): Scheme | null {
|
private lookup(node: NodeWithReference, expectedKind: Symkind): Scheme | null {
|
||||||
return this.getContext().env.lookup(name, kind);
|
let modulePath: IdentifierAlt[];
|
||||||
|
let name: Identifier | IdentifierAlt | ExprOperator;
|
||||||
|
if (node.kind === SyntaxKind.ReferenceExpression || node.kind === SyntaxKind.ReferenceTypeExpression) {
|
||||||
|
modulePath = node.modulePath.map(([name, _dot]) => name);
|
||||||
|
name = node.name;
|
||||||
|
} else {
|
||||||
|
modulePath = [];
|
||||||
|
name = node;
|
||||||
|
}
|
||||||
|
if (modulePath.length > 0) {
|
||||||
|
let maxIndex = 0;
|
||||||
|
let currUp = node.getEnclosingModule();
|
||||||
|
outer: for (;;) {
|
||||||
|
let currDown: SourceFile | ModuleDeclaration = currUp;
|
||||||
|
for (let i = 0; i < modulePath.length; i++) {
|
||||||
|
const moduleName = modulePath[i];
|
||||||
|
const nextDown = currDown.resolveModule(moduleName.text);
|
||||||
|
if (nextDown === null) {
|
||||||
|
if (currUp.kind === SyntaxKind.SourceFile) {
|
||||||
|
this.diagnostics.add(
|
||||||
|
new ModuleNotFoundDiagnostic(
|
||||||
|
modulePath.slice(maxIndex).map(id => id.text),
|
||||||
|
modulePath[maxIndex],
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
currUp = currUp.getEnclosingModule();
|
||||||
|
continue outer;
|
||||||
|
}
|
||||||
|
maxIndex = Math.max(maxIndex, i+1);
|
||||||
|
currDown = nextDown;
|
||||||
|
}
|
||||||
|
const found = currDown.typeEnv!.get(name.text, expectedKind);
|
||||||
|
if (found !== null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
this.diagnostics.add(
|
||||||
|
new BindingNotFoudDiagnostic(
|
||||||
|
modulePath.map(id => id.text),
|
||||||
|
name.text,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let curr: TypeEnv | null = this.getContext().env;
|
||||||
|
do {
|
||||||
|
const found = curr.get(name.text, expectedKind);
|
||||||
|
if (found !== null) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
curr = curr.parent;
|
||||||
|
} while(curr !== null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getReturnType(): Type {
|
private getReturnType(): Type {
|
||||||
|
@ -834,8 +901,7 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private addBinding(name: string, scheme: Scheme, kind: Symkind): void {
|
private addBinding(name: string, scheme: Scheme, kind: Symkind): void {
|
||||||
const context = this.contexts[this.contexts.length-1];
|
this.getContext().env.add(name, scheme, kind);
|
||||||
context.env.add(name, scheme, kind);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferKindFromTypeExpression(node: TypeExpression, env: KindEnv): Kind {
|
private inferKindFromTypeExpression(node: TypeExpression, env: KindEnv): Kind {
|
||||||
|
@ -858,14 +924,27 @@ export class Checker {
|
||||||
kind = new KStar();
|
kind = new KStar();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SyntaxKind.VarTypeExpression:
|
|
||||||
case SyntaxKind.ReferenceTypeExpression:
|
case SyntaxKind.ReferenceTypeExpression:
|
||||||
{
|
{
|
||||||
const matchedKind = env.lookup(node.name.text);
|
const matchedKind = env.lookup(node.name.text);
|
||||||
if (matchedKind === null) {
|
if (matchedKind === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name));
|
||||||
// Create a filler kind variable that still will be able to catch other errors.
|
// Create a filler kind variable that still will be able to catch other errors.
|
||||||
kind = this.createKindVar();
|
kind = this.createKindVar();
|
||||||
|
kind.flags |= KindFlags.UnificationFailed;
|
||||||
|
} else {
|
||||||
|
kind = matchedKind;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SyntaxKind.VarTypeExpression:
|
||||||
|
{
|
||||||
|
const matchedKind = env.lookup(node.name.text);
|
||||||
|
if (matchedKind === null) {
|
||||||
|
this.diagnostics.add(new BindingNotFoudDiagnostic([], node.name.text, node.name));
|
||||||
|
// Create a filler kind variable that still will be able to catch other errors.
|
||||||
|
kind = this.createKindVar();
|
||||||
|
kind.flags |= KindFlags.UnificationFailed;
|
||||||
} else {
|
} else {
|
||||||
kind = matchedKind;
|
kind = matchedKind;
|
||||||
}
|
}
|
||||||
|
@ -932,9 +1011,15 @@ export class Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private forwardDeclareKind(node: Syntax, env: KindEnv): void {
|
private forwardDeclareKind(node: Syntax, env: KindEnv): void {
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.ModuleDeclaration:
|
||||||
|
{
|
||||||
|
const innerEnv = node.kindEnv = new KindEnv(env);
|
||||||
|
for (const element of node.elements) {
|
||||||
|
this.forwardDeclareKind(element, innerEnv);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SyntaxKind.SourceFile:
|
case SyntaxKind.SourceFile:
|
||||||
{
|
{
|
||||||
for (const element of node.elements) {
|
for (const element of node.elements) {
|
||||||
|
@ -970,13 +1055,19 @@ export class Checker {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inferKind(node: Syntax, env: KindEnv): void {
|
private inferKind(node: Syntax, env: KindEnv): void {
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.ModuleDeclaration:
|
||||||
|
{
|
||||||
|
const innerEnv = node.kindEnv!;
|
||||||
|
for (const element of node.elements) {
|
||||||
|
this.inferKind(element, innerEnv);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SyntaxKind.SourceFile:
|
case SyntaxKind.SourceFile:
|
||||||
{
|
{
|
||||||
for (const element of node.elements) {
|
for (const element of node.elements) {
|
||||||
|
@ -1264,15 +1355,14 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.ReferenceExpression:
|
case SyntaxKind.ReferenceExpression:
|
||||||
{
|
{
|
||||||
assert(node.modulePath.length === 0);
|
|
||||||
const scope = node.getScope();
|
const scope = node.getScope();
|
||||||
const target = scope.lookup(node.name.text);
|
const target = scope.lookup(node.name.text);
|
||||||
if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.active) {
|
if (target !== null && target.kind === SyntaxKind.LetDeclaration && target.activeCycle) {
|
||||||
return target.inferredType!;
|
return target.inferredType!;
|
||||||
}
|
}
|
||||||
const scheme = this.lookup(node.name.text, Symkind.Var);
|
const scheme = this.lookup(node, Symkind.Var);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
}
|
}
|
||||||
const type = this.instantiate(scheme, node);
|
const type = this.instantiate(scheme, node);
|
||||||
|
@ -1343,10 +1433,10 @@ export class Checker {
|
||||||
}
|
}
|
||||||
case SyntaxKind.PunnedStructExpressionField:
|
case SyntaxKind.PunnedStructExpressionField:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(member.name.text, Symkind.Var);
|
const scheme = this.lookup(member.name, Symkind.Var);
|
||||||
let fieldType;
|
let fieldType;
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(member.name.text, member.name));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(member.name.text, member.name));
|
||||||
fieldType = this.createTypeVar();
|
fieldType = this.createTypeVar();
|
||||||
} else {
|
} else {
|
||||||
fieldType = this.instantiate(scheme, member);
|
fieldType = this.instantiate(scheme, member);
|
||||||
|
@ -1363,9 +1453,9 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.InfixExpression:
|
case SyntaxKind.InfixExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.operator.text, Symkind.Var);
|
const scheme = this.lookup(node.operator, Symkind.Var);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.operator.text, node.operator));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.operator.text, node.operator));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
}
|
}
|
||||||
const opType = this.instantiate(scheme, node.operator);
|
const opType = this.instantiate(scheme, node.operator);
|
||||||
|
@ -1403,9 +1493,9 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.ReferenceTypeExpression:
|
case SyntaxKind.ReferenceTypeExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.name.text, Symkind.Type);
|
const scheme = this.lookup(node, Symkind.Type);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
}
|
}
|
||||||
type = this.instantiate(scheme, node.name);
|
type = this.instantiate(scheme, node.name);
|
||||||
|
@ -1430,10 +1520,10 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.VarTypeExpression:
|
case SyntaxKind.VarTypeExpression:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(node.name.text, Symkind.Type);
|
const scheme = this.lookup(node.name, Symkind.Type);
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
if (!introduceTypeVars) {
|
if (!introduceTypeVars) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
|
||||||
}
|
}
|
||||||
type = this.createTypeVar();
|
type = this.createTypeVar();
|
||||||
this.addBinding(node.name.text, new Forall([], [], type), Symkind.Type);
|
this.addBinding(node.name.text, new Forall([], [], type), Symkind.Type);
|
||||||
|
@ -1527,10 +1617,10 @@ export class Checker {
|
||||||
|
|
||||||
case SyntaxKind.StructPattern:
|
case SyntaxKind.StructPattern:
|
||||||
{
|
{
|
||||||
const scheme = this.lookup(pattern.name.text, Symkind.Type);
|
const scheme = this.lookup(pattern.name, Symkind.Type);
|
||||||
let recordType;
|
let recordType;
|
||||||
if (scheme === null) {
|
if (scheme === null) {
|
||||||
this.diagnostics.add(new BindingNotFoudDiagnostic(pattern.name.text, pattern.name));
|
// this.diagnostics.add(new BindingNotFoudDiagnostic(pattern.name.text, pattern.name));
|
||||||
recordType = this.createTypeVar();
|
recordType = this.createTypeVar();
|
||||||
} else {
|
} else {
|
||||||
recordType = this.instantiate(scheme, pattern.name);
|
recordType = this.instantiate(scheme, pattern.name);
|
||||||
|
@ -1734,8 +1824,9 @@ export class Checker {
|
||||||
kenv.setNamed('Int', new KStar());
|
kenv.setNamed('Int', new KStar());
|
||||||
kenv.setNamed('String', new KStar());
|
kenv.setNamed('String', new KStar());
|
||||||
kenv.setNamed('Bool', new KStar());
|
kenv.setNamed('Bool', new KStar());
|
||||||
this.forwardDeclareKind(node, kenv);
|
const skenv = new KindEnv(kenv);
|
||||||
this.inferKind(node, kenv);
|
this.forwardDeclareKind(node, skenv);
|
||||||
|
this.inferKind(node, skenv);
|
||||||
|
|
||||||
const typeVars = new TVSet();
|
const typeVars = new TVSet();
|
||||||
const constraints = new ConstraintSet();
|
const constraints = new ConstraintSet();
|
||||||
|
@ -1852,7 +1943,7 @@ export class Checker {
|
||||||
&& isFunctionDeclarationLike(element)) {
|
&& isFunctionDeclarationLike(element)) {
|
||||||
if (!this.analyser.isReferencedInParentScope(element)) {
|
if (!this.analyser.isReferencedInParentScope(element)) {
|
||||||
assert(element.pattern.kind === SyntaxKind.BindPattern);
|
assert(element.pattern.kind === SyntaxKind.BindPattern);
|
||||||
const scheme = this.lookup(element.pattern.name.text, Symkind.Var);
|
const scheme = this.lookup(element.pattern.name, Symkind.Var);
|
||||||
assert(scheme !== null);
|
assert(scheme !== null);
|
||||||
this.instantiate(scheme, null);
|
this.instantiate(scheme, null);
|
||||||
}
|
}
|
||||||
|
@ -1871,7 +1962,7 @@ export class Checker {
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
assert(node.kind === SyntaxKind.LetDeclaration);
|
assert(node.kind === SyntaxKind.LetDeclaration);
|
||||||
node.active = true;
|
node.activeCycle = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
|
@ -1912,7 +2003,7 @@ export class Checker {
|
||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
assert(node.kind === SyntaxKind.LetDeclaration);
|
assert(node.kind === SyntaxKind.LetDeclaration);
|
||||||
node.active = false;
|
node.activeCycle = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
53
src/cst.ts
53
src/cst.ts
|
@ -1,4 +1,4 @@
|
||||||
import { JSONObject, JSONValue, MultiMap } from "./util";
|
import { assert, JSONObject, JSONValue, MultiMap } from "./util";
|
||||||
import type { InferContext, Kind, Scheme, Type, TypeEnv } from "./checker"
|
import type { InferContext, Kind, Scheme, Type, TypeEnv } from "./checker"
|
||||||
|
|
||||||
export type TextSpan = [number, number];
|
export type TextSpan = [number, number];
|
||||||
|
@ -413,6 +413,17 @@ abstract class SyntaxBase {
|
||||||
throw new Error(`Could not find a scope for ${this}. Maybe the parent links are not set?`);
|
throw new Error(`Could not find a scope for ${this}. Maybe the parent links are not set?`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getEnclosingModule(): ModuleDeclaration | SourceFile {
|
||||||
|
let curr = this.parent!;
|
||||||
|
while (curr !== null) {
|
||||||
|
if (curr.kind === SyntaxKind.SourceFile || curr.kind === SyntaxKind.ModuleDeclaration) {
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
curr = curr.parent!;
|
||||||
|
}
|
||||||
|
throw new Error(`Unable to find an enclosing module for ${this.constructor.name}. Perhaps the parent-links are not set?`);
|
||||||
|
}
|
||||||
|
|
||||||
public setParents(): void {
|
public setParents(): void {
|
||||||
|
|
||||||
const visit = (value: any) => {
|
const visit = (value: any) => {
|
||||||
|
@ -468,6 +479,29 @@ abstract class SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public resolveModule(name: string): ModuleDeclaration | null {
|
||||||
|
const node = this as unknown as Syntax;
|
||||||
|
assert(node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile);
|
||||||
|
for (const element of node.elements) {
|
||||||
|
if (element.kind === SyntaxKind.ModuleDeclaration && element.name.text === name) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getModulePath(): string[] {
|
||||||
|
let curr = this.parent;
|
||||||
|
const modulePath = [];
|
||||||
|
while (curr !== null) {
|
||||||
|
if (curr.kind === SyntaxKind.ModuleDeclaration) {
|
||||||
|
modulePath.unshift(curr.name.text);
|
||||||
|
}
|
||||||
|
curr = curr.parent;
|
||||||
|
}
|
||||||
|
return modulePath;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forEachChild(node: Syntax, callback: (node: Syntax) => void): void {
|
export function forEachChild(node: Syntax, callback: (node: Syntax) => void): void {
|
||||||
|
@ -681,6 +715,15 @@ export class CustomOperator extends TokenBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ExprOperator
|
||||||
|
= CustomOperator
|
||||||
|
| VBar
|
||||||
|
|
||||||
|
export function isExprOperator(node: Syntax): node is ExprOperator {
|
||||||
|
return node.kind === SyntaxKind.CustomOperator
|
||||||
|
|| node.kind === SyntaxKind.VBar
|
||||||
|
}
|
||||||
|
|
||||||
export class Assignment extends TokenBase {
|
export class Assignment extends TokenBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.Assignment;
|
public readonly kind = SyntaxKind.Assignment;
|
||||||
|
@ -1655,7 +1698,7 @@ export class PrefixExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.PrefixExpression;
|
public readonly kind = SyntaxKind.PrefixExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public operator: Token,
|
public operator: ExprOperator,
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -1677,7 +1720,7 @@ export class PostfixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
public operator: Token,
|
public operator: ExprOperator,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
@ -1698,7 +1741,7 @@ export class InfixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public left: Expression,
|
public left: Expression,
|
||||||
public operator: Token,
|
public operator: ExprOperator,
|
||||||
public right: Expression,
|
public right: Expression,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -2116,7 +2159,7 @@ export class LetDeclaration extends SyntaxBase {
|
||||||
public scope?: Scope;
|
public scope?: Scope;
|
||||||
public typeEnv?: TypeEnv;
|
public typeEnv?: TypeEnv;
|
||||||
|
|
||||||
public active?: boolean;
|
public activeCycle?: boolean;
|
||||||
public context?: InferContext;
|
public context?: InferContext;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
|
|
@ -151,6 +151,7 @@ export class BindingNotFoudDiagnostic {
|
||||||
public readonly level = Level.Error;
|
public readonly level = Level.Error;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public modulePath: string[],
|
||||||
public name: string,
|
public name: string,
|
||||||
public node: Syntax,
|
public node: Syntax,
|
||||||
) {
|
) {
|
||||||
|
@ -159,7 +160,11 @@ export class BindingNotFoudDiagnostic {
|
||||||
|
|
||||||
public format(out: IndentWriter): void {
|
public format(out: IndentWriter): void {
|
||||||
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
|
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
|
||||||
out.write(`binding '${this.name}' was not found.\n\n`);
|
out.write(`binding '${this.name}' was not found`);
|
||||||
|
if (this.modulePath.length > 0) {
|
||||||
|
out.write(` in module ${ANSI_FG_BLUE + this.modulePath.join('.') + ANSI_RESET}`);
|
||||||
|
}
|
||||||
|
out.write(`.\n\n`);
|
||||||
out.write(printNode(this.node) + '\n');
|
out.write(printNode(this.node) + '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +354,25 @@ export class KindMismatchDiagnostic {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ModuleNotFoundDiagnostic {
|
||||||
|
|
||||||
|
public readonly level = Level.Error;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public modulePath: string[],
|
||||||
|
public node: Syntax,
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public format(out: IndentWriter): void {
|
||||||
|
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
|
||||||
|
out.write(`a module named ${ANSI_FG_BLUE + this.modulePath.join('.') + ANSI_RESET} was not found.\n\n`);
|
||||||
|
out.write(printNode(this.node) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export type Diagnostic
|
export type Diagnostic
|
||||||
= UnexpectedCharDiagnostic
|
= UnexpectedCharDiagnostic
|
||||||
| BindingNotFoudDiagnostic
|
| BindingNotFoudDiagnostic
|
||||||
|
@ -357,6 +381,7 @@ export type Diagnostic
|
||||||
| FieldMissingDiagnostic
|
| FieldMissingDiagnostic
|
||||||
| FieldDoesNotExistDiagnostic
|
| FieldDoesNotExistDiagnostic
|
||||||
| KindMismatchDiagnostic
|
| KindMismatchDiagnostic
|
||||||
|
| ModuleNotFoundDiagnostic
|
||||||
|
|
||||||
export interface Diagnostics {
|
export interface Diagnostics {
|
||||||
readonly hasError: boolean;
|
readonly hasError: boolean;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
|
|
||||||
import { warn } from "console";
|
|
||||||
import {
|
import {
|
||||||
ReferenceTypeExpression,
|
ReferenceTypeExpression,
|
||||||
SourceFile,
|
SourceFile,
|
||||||
|
@ -61,6 +60,7 @@ import {
|
||||||
DisjunctivePattern,
|
DisjunctivePattern,
|
||||||
TupleTypeExpression,
|
TupleTypeExpression,
|
||||||
ModuleDeclaration,
|
ModuleDeclaration,
|
||||||
|
isExprOperator,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Stream } from "./util";
|
import { Stream } from "./util";
|
||||||
|
|
||||||
|
@ -76,16 +76,6 @@ export class ParseError extends Error {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBinaryOperatorLike(token: Token): boolean {
|
|
||||||
return token.kind === SyntaxKind.CustomOperator
|
|
||||||
|| token.kind === SyntaxKind.VBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPrefixOperatorLike(token: Token): boolean {
|
|
||||||
return token.kind === SyntaxKind.CustomOperator
|
|
||||||
|| token.kind === SyntaxKind.VBar;
|
|
||||||
}
|
|
||||||
|
|
||||||
const enum OperatorMode {
|
const enum OperatorMode {
|
||||||
None = 0,
|
None = 0,
|
||||||
Prefix = 1,
|
Prefix = 1,
|
||||||
|
@ -442,8 +432,7 @@ export class Parser {
|
||||||
|| t1.kind === SyntaxKind.RParen
|
|| t1.kind === SyntaxKind.RParen
|
||||||
|| t1.kind === SyntaxKind.BlockStart
|
|| t1.kind === SyntaxKind.BlockStart
|
||||||
|| t1.kind === SyntaxKind.Comma
|
|| t1.kind === SyntaxKind.Comma
|
||||||
|| isBinaryOperatorLike(t1)
|
|| isExprOperator(t1)) {
|
||||||
|| isPrefixOperatorLike(t1)) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
args.push(this.tryParseMemberExpression());
|
args.push(this.tryParseMemberExpression());
|
||||||
|
@ -459,7 +448,7 @@ export class Parser {
|
||||||
const prefixes = [];
|
const prefixes = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken();
|
||||||
if (!isPrefixOperatorLike(t0)) {
|
if (!isExprOperator(t0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!this.prefixExprOperators.has(t0.text)) {
|
if (!this.prefixExprOperators.has(t0.text)) {
|
||||||
|
@ -478,7 +467,7 @@ export class Parser {
|
||||||
private parseBinaryOperatorAfterExpr(lhs: Expression, minPrecedence: number) {
|
private parseBinaryOperatorAfterExpr(lhs: Expression, minPrecedence: number) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken();
|
||||||
if (!isBinaryOperatorLike(t0)) {
|
if (!isExprOperator(t0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const info0 = this.binaryExprOperators.get(t0.text);
|
const info0 = this.binaryExprOperators.get(t0.text);
|
||||||
|
@ -489,7 +478,7 @@ export class Parser {
|
||||||
let rhs = this.parseUnaryExpression();
|
let rhs = this.parseUnaryExpression();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t1 = this.peekToken();
|
const t1 = this.peekToken();
|
||||||
if (!isBinaryOperatorLike(t1)) {
|
if (!isExprOperator(t1)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const info1 = this.binaryExprOperators.get(t1.text);
|
const info1 = this.binaryExprOperators.get(t1.text);
|
||||||
|
|
Loading…
Reference in a new issue