Add a 'verify' command, ...
- Add a command to check the integrity of the compiler - Add a diagnostic when a tuple index is out of bounds - Make 'build' the default command - ...
This commit is contained in:
parent
d194ff9b2e
commit
7a0cb6753a
8 changed files with 452 additions and 133 deletions
|
@ -15,6 +15,10 @@ import BoltToJS from "../passes/BoltToJS"
|
||||||
import { stripExtension } from "../util"
|
import { stripExtension } from "../util"
|
||||||
import { sync as which } from "which"
|
import { sync as which } from "which"
|
||||||
import { spawnSync } from "child_process"
|
import { spawnSync } from "child_process"
|
||||||
|
import { ConsoleDiagnostics, DiagnosticStore, TypeMismatchDiagnostic } from "../diagnostics"
|
||||||
|
import { Syntax, SyntaxKind, TextFile, isExpression, visitEachChild } from "../cst"
|
||||||
|
import { Analyser, Checker, parseSourceFile } from ".."
|
||||||
|
import { typesEqual } from "../types"
|
||||||
|
|
||||||
function debug(value: any) {
|
function debug(value: any) {
|
||||||
console.error(util.inspect(value, { colors: true, depth: Infinity }));
|
console.error(util.inspect(value, { colors: true, depth: Infinity }));
|
||||||
|
@ -73,15 +77,17 @@ program
|
||||||
.version('0.0.1')
|
.version('0.0.1')
|
||||||
.option('-C, --work-dir', 'Act as if run from this directory', '.');
|
.option('-C, --work-dir', 'Act as if run from this directory', '.');
|
||||||
|
|
||||||
program.command('build', 'Build a set of Bolt sources')
|
program.command('build', { isDefault: true })
|
||||||
|
.description('Build a set of Bolt sources')
|
||||||
.argument('<file>', 'Path to the Bolt program to compile')
|
.argument('<file>', 'Path to the Bolt program to compile')
|
||||||
|
.option('-C, --work-dir', 'Act as if run from this directory', '.')
|
||||||
.option('--no-typecheck', 'Skip type-checking')
|
.option('--no-typecheck', 'Skip type-checking')
|
||||||
.option('--no-emit', 'Do not output compiled files')
|
.option('--no-emit', 'Do not output compiled files')
|
||||||
.option('-t, --target <target-id>', 'What to compile to', 'c')
|
.option('-t, --target <target-id>', 'What to compile to', 'c')
|
||||||
.action((file, opts) => {
|
.action((fileName, opts) => {
|
||||||
|
|
||||||
const cwd = opts.workDir;
|
const cwd = opts.workDir;
|
||||||
const filename = path.resolve(cwd, file);
|
const filePath = path.resolve(cwd, fileName);
|
||||||
const shouldTypecheck = opts.typecheck;
|
const shouldTypecheck = opts.typecheck;
|
||||||
const shouldEmit = opts.emit;
|
const shouldEmit = opts.emit;
|
||||||
|
|
||||||
|
@ -101,7 +107,7 @@ program.command('build', 'Build a set of Bolt sources')
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const program = new Program([ filename ]);
|
const program = new Program([ filePath ]);
|
||||||
if (program.diagnostics.hasError) {
|
if (program.diagnostics.hasError) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
@ -148,5 +154,39 @@ program.command('build', 'Build a set of Bolt sources')
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program.command('verify', { hidden: true })
|
||||||
|
.description('Run verification tests')
|
||||||
|
.argument('<file>', 'File with verification source')
|
||||||
|
.action((fileName, _opts) => {
|
||||||
|
|
||||||
|
const diagnostics = new DiagnosticStore();
|
||||||
|
const realPath = path.resolve(fileName);
|
||||||
|
const text = fs.readFileSync(realPath, 'utf-8');
|
||||||
|
const file = new TextFile(fileName, text);
|
||||||
|
const sourceFile = parseSourceFile(file, diagnostics);
|
||||||
|
if (!sourceFile) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const analyser = new Analyser();
|
||||||
|
const checker = new Checker(analyser, diagnostics);
|
||||||
|
checker.check(sourceFile);
|
||||||
|
const realDiagnostics = new ConsoleDiagnostics();
|
||||||
|
const visit = (node: Syntax) => {
|
||||||
|
if (isExpression(node)) {
|
||||||
|
for (const annotation of node.annotations) {
|
||||||
|
if (annotation.kind === SyntaxKind.TypeAnnotation) {
|
||||||
|
const actual = checker.getTypeOfNode(node);
|
||||||
|
const expected = checker.getTypeOfNode(annotation.typeExpr);
|
||||||
|
if (!typesEqual(actual, expected)) {
|
||||||
|
realDiagnostics.add(new TypeMismatchDiagnostic(actual, expected, [ node ], []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
visitEachChild(node, visit);
|
||||||
|
}
|
||||||
|
visit(sourceFile);
|
||||||
|
});
|
||||||
|
|
||||||
program.parse();
|
program.parse();
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,12 @@ import {
|
||||||
TypeclassDeclaredTwiceDiagnostic,
|
TypeclassDeclaredTwiceDiagnostic,
|
||||||
FieldNotFoundDiagnostic,
|
FieldNotFoundDiagnostic,
|
||||||
TypeMismatchDiagnostic,
|
TypeMismatchDiagnostic,
|
||||||
|
TupleIndexOutOfRangeDiagnostic,
|
||||||
} from "./diagnostics";
|
} from "./diagnostics";
|
||||||
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn, implementationLimitation } from "./util";
|
import { assert, assertNever, isEmpty, MultiMap, toStringTag, InspectFn, implementationLimitation } from "./util";
|
||||||
import { Analyser } from "./analysis";
|
import { Analyser } from "./analysis";
|
||||||
import { InspectOptions } from "util";
|
import { InspectOptions } from "util";
|
||||||
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TNominal, TPresent, TTuple, TUniVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar } from "./types";
|
import { TypeKind, TApp, TArrow, TCon, TField, TNil, TNominal, TPresent, TTuple, TUniVar, TVSet, TVSub, Type, TypeBase, TAbsent, TRigidVar, TVar, TTupleIndex } from "./types";
|
||||||
import { CClass, CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
|
import { CClass, CEmpty, CEqual, CMany, Constraint, ConstraintKind, ConstraintSet } from "./constraints";
|
||||||
|
|
||||||
// export class Qual {
|
// export class Qual {
|
||||||
|
@ -691,7 +692,10 @@ export class Checker {
|
||||||
{
|
{
|
||||||
const tupleType = this.simplifyType(type.tupleType);
|
const tupleType = this.simplifyType(type.tupleType);
|
||||||
if (tupleType.kind === TypeKind.Tuple) {
|
if (tupleType.kind === TypeKind.Tuple) {
|
||||||
// TODO check bounds and add diagnostic
|
if (type.index >= tupleType.elementTypes.length) {
|
||||||
|
this.diagnostics.add(new TupleIndexOutOfRangeDiagnostic(type.index, tupleType));
|
||||||
|
return type;
|
||||||
|
}
|
||||||
const newType = tupleType.elementTypes[type.index];
|
const newType = tupleType.elementTypes[type.index];
|
||||||
type.set(newType);
|
type.set(newType);
|
||||||
return newType;
|
return newType;
|
||||||
|
@ -1295,10 +1299,19 @@ export class Checker {
|
||||||
|
|
||||||
public inferExpression(node: Expression): Type {
|
public inferExpression(node: Expression): Type {
|
||||||
|
|
||||||
|
for (const annotation of node.annotations) {
|
||||||
|
if (annotation.kind === SyntaxKind.TypeAnnotation) {
|
||||||
|
this.inferTypeExpression(annotation.typeExpr, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let type: Type;
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
|
||||||
case SyntaxKind.NestedExpression:
|
case SyntaxKind.NestedExpression:
|
||||||
return this.inferExpression(node.expression);
|
type = this.inferExpression(node.expression);
|
||||||
|
break;
|
||||||
|
|
||||||
case SyntaxKind.MatchExpression:
|
case SyntaxKind.MatchExpression:
|
||||||
{
|
{
|
||||||
|
@ -1308,7 +1321,7 @@ export class Checker {
|
||||||
} else {
|
} else {
|
||||||
exprType = this.createTypeVar();
|
exprType = this.createTypeVar();
|
||||||
}
|
}
|
||||||
let resultType: Type = this.createTypeVar();
|
type = this.createTypeVar();
|
||||||
for (const arm of node.arms) {
|
for (const arm of node.arms) {
|
||||||
const context = this.getContext();
|
const context = this.getContext();
|
||||||
const newEnv = new TypeEnv(context.env);
|
const newEnv = new TypeEnv(context.env);
|
||||||
|
@ -1330,7 +1343,7 @@ export class Checker {
|
||||||
);
|
);
|
||||||
this.addConstraint(
|
this.addConstraint(
|
||||||
new CEqual(
|
new CEqual(
|
||||||
resultType,
|
type,
|
||||||
this.inferExpression(arm.expression),
|
this.inferExpression(arm.expression),
|
||||||
arm.expression
|
arm.expression
|
||||||
)
|
)
|
||||||
|
@ -1338,13 +1351,14 @@ export class Checker {
|
||||||
this.popContext(newContext);
|
this.popContext(newContext);
|
||||||
}
|
}
|
||||||
if (node.expression === null) {
|
if (node.expression === null) {
|
||||||
resultType = new TArrow(exprType, resultType);
|
type = new TArrow(exprType, type);
|
||||||
}
|
}
|
||||||
return resultType;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.TupleExpression:
|
case SyntaxKind.TupleExpression:
|
||||||
return new TTuple(node.elements.map(el => this.inferExpression(el)), node);
|
type = new TTuple(node.elements.map(el => this.inferExpression(el)), node);
|
||||||
|
break;
|
||||||
|
|
||||||
case SyntaxKind.ReferenceExpression:
|
case SyntaxKind.ReferenceExpression:
|
||||||
{
|
{
|
||||||
|
@ -1360,36 +1374,48 @@ export class Checker {
|
||||||
}
|
}
|
||||||
const scheme = this.lookup(node, 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();
|
type = this.createTypeVar();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
const type = this.instantiate(scheme, node);
|
type = this.instantiate(scheme, node);
|
||||||
type.node = node;
|
type.node = node;
|
||||||
return type;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.MemberExpression:
|
case SyntaxKind.MemberExpression:
|
||||||
{
|
{
|
||||||
let type = this.inferExpression(node.expression);
|
type = this.inferExpression(node.expression);
|
||||||
for (const [_dot, name] of node.path) {
|
for (const [_dot, name] of node.path) {
|
||||||
const newFieldType = this.createTypeVar(name);
|
switch (name.kind) {
|
||||||
const newRestType = this.createTypeVar();
|
case SyntaxKind.Identifier:
|
||||||
this.addConstraint(
|
{
|
||||||
new CEqual(
|
const newFieldType = this.createTypeVar(name);
|
||||||
type,
|
const newRestType = this.createTypeVar();
|
||||||
new TField(name.text, new TPresent(newFieldType), newRestType, name),
|
this.addConstraint(
|
||||||
node,
|
new CEqual(
|
||||||
)
|
type,
|
||||||
);
|
new TField(name.text, new TPresent(newFieldType), newRestType, name),
|
||||||
type = newFieldType;
|
node,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
type = newFieldType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SyntaxKind.Integer:
|
||||||
|
type = new TTupleIndex(type, Number(name.value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assertNever(name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return type;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.CallExpression:
|
case SyntaxKind.CallExpression:
|
||||||
{
|
{
|
||||||
const opType = this.inferExpression(node.func);
|
const opType = this.inferExpression(node.func);
|
||||||
const retType = this.createTypeVar(node);
|
type = this.createTypeVar(node);
|
||||||
const paramTypes = [];
|
const paramTypes = [];
|
||||||
for (const arg of node.args) {
|
for (const arg of node.args) {
|
||||||
paramTypes.push(this.inferExpression(arg));
|
paramTypes.push(this.inferExpression(arg));
|
||||||
|
@ -1397,32 +1423,31 @@ export class Checker {
|
||||||
this.addConstraint(
|
this.addConstraint(
|
||||||
new CEqual(
|
new CEqual(
|
||||||
opType,
|
opType,
|
||||||
TArrow.build(paramTypes, retType),
|
TArrow.build(paramTypes, type),
|
||||||
node
|
node
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return retType;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.ConstantExpression:
|
case SyntaxKind.ConstantExpression:
|
||||||
{
|
{
|
||||||
let ty;
|
|
||||||
switch (node.token.kind) {
|
switch (node.token.kind) {
|
||||||
case SyntaxKind.StringLiteral:
|
case SyntaxKind.StringLiteral:
|
||||||
ty = this.getStringType();
|
type = this.getStringType();
|
||||||
break;
|
break;
|
||||||
case SyntaxKind.Integer:
|
case SyntaxKind.Integer:
|
||||||
ty = this.getIntType();
|
type = this.getIntType();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ty = ty.shallowClone();
|
type = type.shallowClone();
|
||||||
ty.node = node;
|
type.node = node;
|
||||||
return ty;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.StructExpression:
|
case SyntaxKind.StructExpression:
|
||||||
{
|
{
|
||||||
let type: Type = new TNil(node);
|
type = new TNil(node);
|
||||||
for (const member of node.members) {
|
for (const member of node.members) {
|
||||||
switch (member.kind) {
|
switch (member.kind) {
|
||||||
case SyntaxKind.StructExpressionField:
|
case SyntaxKind.StructExpressionField:
|
||||||
|
@ -1447,7 +1472,9 @@ export class Checker {
|
||||||
throw new Error(`Unexpected ${member}`);
|
throw new Error(`Unexpected ${member}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TField.sort(type);
|
// FIXME build a type rather than sorting it
|
||||||
|
type = TField.sort(type);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SyntaxKind.InfixExpression:
|
case SyntaxKind.InfixExpression:
|
||||||
|
@ -1458,17 +1485,17 @@ export class Checker {
|
||||||
return this.createTypeVar();
|
return this.createTypeVar();
|
||||||
}
|
}
|
||||||
const opType = this.instantiate(scheme, node.operator);
|
const opType = this.instantiate(scheme, node.operator);
|
||||||
const retType = this.createTypeVar();
|
|
||||||
const leftType = this.inferExpression(node.left);
|
const leftType = this.inferExpression(node.left);
|
||||||
const rightType = this.inferExpression(node.right);
|
const rightType = this.inferExpression(node.right);
|
||||||
|
type = this.createTypeVar();
|
||||||
this.addConstraint(
|
this.addConstraint(
|
||||||
new CEqual(
|
new CEqual(
|
||||||
new TArrow(leftType, new TArrow(rightType, retType)),
|
new TArrow(leftType, new TArrow(rightType, type)),
|
||||||
opType,
|
opType,
|
||||||
node,
|
node,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return retType;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1476,13 +1503,17 @@ export class Checker {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.inferredType = type;
|
||||||
|
|
||||||
|
return type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public inferTypeExpression(node: TypeExpression, introduceTypeVars = false): Type {
|
public inferTypeExpression(node: TypeExpression, introduceTypeVars = false, checkKind = true): Type {
|
||||||
|
|
||||||
let type;
|
let type;
|
||||||
|
|
||||||
if (!node.inferredKind) {
|
if (checkKind && !node.inferredKind) {
|
||||||
|
|
||||||
type = this.createTypeVar();
|
type = this.createTypeVar();
|
||||||
|
|
||||||
|
@ -1496,16 +1527,17 @@ export class Checker {
|
||||||
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));
|
||||||
type = this.createTypeVar();
|
type = this.createTypeVar();
|
||||||
} else {
|
break;
|
||||||
type = this.instantiate(scheme, node.name);
|
|
||||||
// It is not guaranteed that `type` is copied during instantiation,
|
|
||||||
// so the following check ensures that we really are holding a copy
|
|
||||||
// that we can mutate.
|
|
||||||
if (type === scheme.type) {
|
|
||||||
type = type.shallowClone();
|
|
||||||
}
|
|
||||||
type.node = node;
|
|
||||||
}
|
}
|
||||||
|
type = this.instantiate(scheme, node.name);
|
||||||
|
// It is not guaranteed that `type` is copied during instantiation,
|
||||||
|
// so the following check ensures that we really are holding a copy
|
||||||
|
// that we can mutate.
|
||||||
|
if (type === scheme.type) {
|
||||||
|
type = type.shallowClone();
|
||||||
|
}
|
||||||
|
// Mutate the type
|
||||||
|
type.node = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2281,6 +2313,11 @@ export class Checker {
|
||||||
return this.classDecls.get(name) ?? null;
|
return this.classDecls.get(name) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getTypeOfNode(node: Syntax): Type {
|
||||||
|
assert(node.inferredType !== undefined);
|
||||||
|
return this.simplifyType(node.inferredType);
|
||||||
|
}
|
||||||
|
|
||||||
// private *findInstanceContext(type: TCon, clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
|
// private *findInstanceContext(type: TCon, clazz: ClassDeclaration): Iterable<ClassDeclaration[]> {
|
||||||
// for (const instance of clazz.getInstances()) {
|
// for (const instance of clazz.getInstances()) {
|
||||||
// assert(instance.types.length === 1);
|
// assert(instance.types.length === 1);
|
||||||
|
|
|
@ -97,6 +97,7 @@ export const enum SyntaxKind {
|
||||||
VBar,
|
VBar,
|
||||||
Dot,
|
Dot,
|
||||||
DotDot,
|
DotDot,
|
||||||
|
At,
|
||||||
Comma,
|
Comma,
|
||||||
Colon,
|
Colon,
|
||||||
Equals,
|
Equals,
|
||||||
|
@ -125,6 +126,10 @@ export const enum SyntaxKind {
|
||||||
BlockStart,
|
BlockStart,
|
||||||
EndOfFile,
|
EndOfFile,
|
||||||
|
|
||||||
|
// Annotations
|
||||||
|
TypeAnnotation,
|
||||||
|
ExpressionAnnotation,
|
||||||
|
|
||||||
// Type expressions
|
// Type expressions
|
||||||
ReferenceTypeExpression,
|
ReferenceTypeExpression,
|
||||||
ArrowTypeExpression,
|
ArrowTypeExpression,
|
||||||
|
@ -225,6 +230,7 @@ export type Syntax
|
||||||
| StructDeclarationField
|
| StructDeclarationField
|
||||||
| EnumDeclarationElement
|
| EnumDeclarationElement
|
||||||
| TypeAssert
|
| TypeAssert
|
||||||
|
| Annotation
|
||||||
| Declaration
|
| Declaration
|
||||||
| Statement
|
| Statement
|
||||||
| Expression
|
| Expression
|
||||||
|
@ -824,6 +830,20 @@ export class Dot extends TokenBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class At extends TokenBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.At;
|
||||||
|
|
||||||
|
public get text(): string {
|
||||||
|
return '@';
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): At {
|
||||||
|
return new At(this.startPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class Comma extends TokenBase {
|
export class Comma extends TokenBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.Comma;
|
public readonly kind = SyntaxKind.Comma;
|
||||||
|
@ -1190,6 +1210,7 @@ export type Token
|
||||||
| CustomOperator
|
| CustomOperator
|
||||||
| Integer
|
| Integer
|
||||||
| StringLiteral
|
| StringLiteral
|
||||||
|
| At
|
||||||
| Comma
|
| Comma
|
||||||
| Dot
|
| Dot
|
||||||
| DotDot
|
| DotDot
|
||||||
|
@ -1777,11 +1798,47 @@ export type Pattern
|
||||||
| DisjunctivePattern
|
| DisjunctivePattern
|
||||||
| LiteralPattern
|
| LiteralPattern
|
||||||
|
|
||||||
|
export class TypeAnnotation extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.TypeAnnotation;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public at: At,
|
||||||
|
public colon: Colon,
|
||||||
|
public typeExpr: TypeExpression,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): TypeAnnotation {
|
||||||
|
return new TypeAnnotation(
|
||||||
|
this.at.clone(),
|
||||||
|
this.colon.clone(),
|
||||||
|
this.typeExpr.clone()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.typeExpr.getLastToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Annotation
|
||||||
|
= TypeAnnotation
|
||||||
|
|
||||||
|
export type Annotations = Annotation[];
|
||||||
|
|
||||||
export class TupleExpression extends SyntaxBase {
|
export class TupleExpression extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.TupleExpression;
|
public readonly kind = SyntaxKind.TupleExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public lparen: LParen,
|
public lparen: LParen,
|
||||||
public elements: Expression[],
|
public elements: Expression[],
|
||||||
public rparen: RParen,
|
public rparen: RParen,
|
||||||
|
@ -1791,6 +1848,7 @@ export class TupleExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): TupleExpression {
|
public clone(): TupleExpression {
|
||||||
return new TupleExpression(
|
return new TupleExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.lparen.clone(),
|
this.lparen.clone(),
|
||||||
this.elements.map(element => element.clone()),
|
this.elements.map(element => element.clone()),
|
||||||
this.rparen.clone()
|
this.rparen.clone()
|
||||||
|
@ -1812,6 +1870,7 @@ export class NestedExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.NestedExpression;
|
public readonly kind = SyntaxKind.NestedExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public lparen: LParen,
|
public lparen: LParen,
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
public rparen: RParen,
|
public rparen: RParen,
|
||||||
|
@ -1821,6 +1880,7 @@ export class NestedExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): NestedExpression {
|
public clone(): NestedExpression {
|
||||||
return new NestedExpression(
|
return new NestedExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.lparen.clone(),
|
this.lparen.clone(),
|
||||||
this.expression.clone(),
|
this.expression.clone(),
|
||||||
this.rparen.clone(),
|
this.rparen.clone(),
|
||||||
|
@ -1842,13 +1902,17 @@ export class ConstantExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.ConstantExpression;
|
public readonly kind = SyntaxKind.ConstantExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public token: Integer | StringLiteral,
|
public token: Integer | StringLiteral,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clone(): ConstantExpression {
|
public clone(): ConstantExpression {
|
||||||
return new ConstantExpression( this.token.clone() );
|
return new ConstantExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
|
this.token.clone()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFirstToken(): Token {
|
public getFirstToken(): Token {
|
||||||
|
@ -1866,6 +1930,7 @@ export class CallExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.CallExpression;
|
public readonly kind = SyntaxKind.CallExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public func: Expression,
|
public func: Expression,
|
||||||
public args: Expression[],
|
public args: Expression[],
|
||||||
) {
|
) {
|
||||||
|
@ -1874,6 +1939,7 @@ export class CallExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): CallExpression {
|
public clone(): CallExpression {
|
||||||
return new CallExpression(
|
return new CallExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.func.clone(),
|
this.func.clone(),
|
||||||
this.args.map(arg => arg.clone()),
|
this.args.map(arg => arg.clone()),
|
||||||
);
|
);
|
||||||
|
@ -1955,6 +2021,7 @@ export class StructExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.StructExpression;
|
public readonly kind = SyntaxKind.StructExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public lbrace: LBrace,
|
public lbrace: LBrace,
|
||||||
public members: StructExpressionElement[],
|
public members: StructExpressionElement[],
|
||||||
public rbrace: RBrace,
|
public rbrace: RBrace,
|
||||||
|
@ -1964,6 +2031,7 @@ export class StructExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): StructExpression {
|
public clone(): StructExpression {
|
||||||
return new StructExpression(
|
return new StructExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.lbrace.clone(),
|
this.lbrace.clone(),
|
||||||
this.members.map(member => member.clone()),
|
this.members.map(member => member.clone()),
|
||||||
this.rbrace.clone(),
|
this.rbrace.clone(),
|
||||||
|
@ -1980,6 +2048,38 @@ export class StructExpression extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class FunctionExpression extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.FunctionExpression;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
|
public backslash: Backslash,
|
||||||
|
public params: Param[],
|
||||||
|
public body: Body,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public clone(): FunctionExpression {
|
||||||
|
return new FunctionExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
|
this.backslash.clone(),
|
||||||
|
this.params.map(param => param.clone()),
|
||||||
|
this.body.clone(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.backslash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.body.getLastToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class MatchArm extends SyntaxBase {
|
export class MatchArm extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.MatchArm;
|
public readonly kind = SyntaxKind.MatchArm;
|
||||||
|
@ -2010,42 +2110,13 @@ export class MatchArm extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FunctionExpression extends SyntaxBase {
|
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.FunctionExpression;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
public backslash: Backslash,
|
|
||||||
public params: Param[],
|
|
||||||
public body: Body,
|
|
||||||
) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public clone(): FunctionExpression {
|
|
||||||
return new FunctionExpression(
|
|
||||||
this.backslash.clone(),
|
|
||||||
this.params.map(param => param.clone()),
|
|
||||||
this.body.clone(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getFirstToken(): Token {
|
|
||||||
return this.backslash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getLastToken(): Token {
|
|
||||||
return this.body.getLastToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MatchExpression extends SyntaxBase {
|
export class MatchExpression extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.MatchExpression;
|
public readonly kind = SyntaxKind.MatchExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public matchKeyword: MatchKeyword,
|
public matchKeyword: MatchKeyword,
|
||||||
public expression: Expression | null,
|
public expression: Expression | null,
|
||||||
public arms: MatchArm[],
|
public arms: MatchArm[],
|
||||||
|
@ -2055,6 +2126,7 @@ export class MatchExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): MatchExpression {
|
public clone(): MatchExpression {
|
||||||
return new MatchExpression(
|
return new MatchExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.matchKeyword.clone(),
|
this.matchKeyword.clone(),
|
||||||
this.expression !== null ? this.expression.clone() : null,
|
this.expression !== null ? this.expression.clone() : null,
|
||||||
this.arms.map(arm => arm.clone()),
|
this.arms.map(arm => arm.clone()),
|
||||||
|
@ -2062,6 +2134,9 @@ export class MatchExpression extends SyntaxBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getFirstToken(): Token {
|
public getFirstToken(): Token {
|
||||||
|
if (this.annotations.length > 0) {
|
||||||
|
return this.annotations[0].getFirstToken();
|
||||||
|
}
|
||||||
return this.matchKeyword;
|
return this.matchKeyword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2082,6 +2157,7 @@ export class ReferenceExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.ReferenceExpression;
|
public readonly kind = SyntaxKind.ReferenceExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public modulePath: Array<[IdentifierAlt, Dot]>,
|
public modulePath: Array<[IdentifierAlt, Dot]>,
|
||||||
public name: Identifier | IdentifierAlt,
|
public name: Identifier | IdentifierAlt,
|
||||||
) {
|
) {
|
||||||
|
@ -2090,6 +2166,7 @@ export class ReferenceExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): ReferenceExpression {
|
public clone(): ReferenceExpression {
|
||||||
return new ReferenceExpression(
|
return new ReferenceExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.modulePath.map(([name, dot]) => [name.clone(), dot.clone()]),
|
this.modulePath.map(([name, dot]) => [name.clone(), dot.clone()]),
|
||||||
this.name.clone(),
|
this.name.clone(),
|
||||||
);
|
);
|
||||||
|
@ -2113,14 +2190,16 @@ export class MemberExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.MemberExpression;
|
public readonly kind = SyntaxKind.MemberExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
public path: [Dot, Identifier][],
|
public path: [Dot, Identifier | Integer][],
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public clone(): MemberExpression {
|
public clone(): MemberExpression {
|
||||||
return new MemberExpression(
|
return new MemberExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.expression.clone(),
|
this.expression.clone(),
|
||||||
this.path.map(([dot, name]) => [dot.clone(), name.clone()]),
|
this.path.map(([dot, name]) => [dot.clone(), name.clone()]),
|
||||||
);
|
);
|
||||||
|
@ -2141,6 +2220,7 @@ export class PrefixExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.PrefixExpression;
|
public readonly kind = SyntaxKind.PrefixExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public operator: ExprOperator,
|
public operator: ExprOperator,
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
) {
|
) {
|
||||||
|
@ -2149,6 +2229,7 @@ export class PrefixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): PrefixExpression {
|
public clone(): PrefixExpression {
|
||||||
return new PrefixExpression(
|
return new PrefixExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.operator.clone(),
|
this.operator.clone(),
|
||||||
this.expression.clone(),
|
this.expression.clone(),
|
||||||
);
|
);
|
||||||
|
@ -2169,6 +2250,7 @@ export class PostfixExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.PostfixExpression;
|
public readonly kind = SyntaxKind.PostfixExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public expression: Expression,
|
public expression: Expression,
|
||||||
public operator: ExprOperator,
|
public operator: ExprOperator,
|
||||||
) {
|
) {
|
||||||
|
@ -2177,6 +2259,7 @@ export class PostfixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): PostfixExpression {
|
public clone(): PostfixExpression {
|
||||||
return new PostfixExpression(
|
return new PostfixExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.expression.clone(),
|
this.expression.clone(),
|
||||||
this.operator.clone(),
|
this.operator.clone(),
|
||||||
);
|
);
|
||||||
|
@ -2197,6 +2280,7 @@ export class InfixExpression extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.InfixExpression;
|
public readonly kind = SyntaxKind.InfixExpression;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
public annotations: Annotations,
|
||||||
public left: Expression,
|
public left: Expression,
|
||||||
public operator: ExprOperator,
|
public operator: ExprOperator,
|
||||||
public right: Expression,
|
public right: Expression,
|
||||||
|
@ -2206,6 +2290,7 @@ export class InfixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public clone(): InfixExpression {
|
public clone(): InfixExpression {
|
||||||
return new InfixExpression(
|
return new InfixExpression(
|
||||||
|
this.annotations.map(a => a.clone()),
|
||||||
this.left.clone(),
|
this.left.clone(),
|
||||||
this.operator.clone(),
|
this.operator.clone(),
|
||||||
this.right.clone(),
|
this.right.clone(),
|
||||||
|
@ -2222,6 +2307,26 @@ export class InfixExpression extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isExpression(node: Syntax): node is Expression {
|
||||||
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.MemberExpression:
|
||||||
|
case SyntaxKind.CallExpression:
|
||||||
|
case SyntaxKind.StructExpression:
|
||||||
|
case SyntaxKind.ReferenceExpression:
|
||||||
|
case SyntaxKind.ConstantExpression:
|
||||||
|
case SyntaxKind.TupleExpression:
|
||||||
|
case SyntaxKind.MatchExpression:
|
||||||
|
case SyntaxKind.NestedExpression:
|
||||||
|
case SyntaxKind.PrefixExpression:
|
||||||
|
case SyntaxKind.InfixExpression:
|
||||||
|
case SyntaxKind.PostfixExpression:
|
||||||
|
case SyntaxKind.FunctionExpression:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type Expression
|
export type Expression
|
||||||
= MemberExpression
|
= MemberExpression
|
||||||
| CallExpression
|
| CallExpression
|
||||||
|
@ -3233,7 +3338,7 @@ export function isToken(value: any): value is Token {
|
||||||
&& value instanceof TokenBase;
|
&& value instanceof TokenBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function vistEachChild<T extends Syntax>(node: T, proc: (node: Syntax) => Syntax | undefined): Syntax {
|
export function visitEachChild<T extends Syntax>(node: T, proc: (node: Syntax) => Syntax | void): Syntax {
|
||||||
|
|
||||||
const newArgs = [];
|
const newArgs = [];
|
||||||
let changed = false;
|
let changed = false;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
import { Kind, KindType } from "./checker";
|
import { Kind, KindType } from "./checker";
|
||||||
import { type Type, TypeKind } from "./types"
|
import { type Type, TypeKind, TTuple } from "./types"
|
||||||
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
|
import { ClassConstraint, ClassDeclaration, IdentifierAlt, InstanceDeclaration, Syntax, SyntaxKind, TextFile, TextPosition, TextRange, Token } from "./cst";
|
||||||
import { assertNever, countDigits, IndentWriter } from "./util";
|
import { assertNever, countDigits, IndentWriter } from "./util";
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ const enum DiagnosticKind {
|
||||||
UnexpectedToken,
|
UnexpectedToken,
|
||||||
KindMismatch,
|
KindMismatch,
|
||||||
TypeMismatch,
|
TypeMismatch,
|
||||||
|
TupleIndexOutOfRange,
|
||||||
TypeclassNotFound,
|
TypeclassNotFound,
|
||||||
TypeclassDecaredTwice,
|
TypeclassDecaredTwice,
|
||||||
TypeclassNotImplemented,
|
TypeclassNotImplemented,
|
||||||
|
@ -170,6 +171,21 @@ export class TypeMismatchDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TupleIndexOutOfRangeDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
|
public readonly kind = DiagnosticKind.TupleIndexOutOfRange;
|
||||||
|
|
||||||
|
public level = Level.Error;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public index: number,
|
||||||
|
public tupleType: TTuple,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class FieldNotFoundDiagnostic extends DiagnosticBase {
|
export class FieldNotFoundDiagnostic extends DiagnosticBase {
|
||||||
|
|
||||||
public readonly kind = DiagnosticKind.FieldNotFound;
|
public readonly kind = DiagnosticKind.FieldNotFound;
|
||||||
|
@ -225,6 +241,7 @@ export type Diagnostic
|
||||||
| TypeclassNotImplementedDiagnostic
|
| TypeclassNotImplementedDiagnostic
|
||||||
| BindingNotFoundDiagnostic
|
| BindingNotFoundDiagnostic
|
||||||
| TypeMismatchDiagnostic
|
| TypeMismatchDiagnostic
|
||||||
|
| TupleIndexOutOfRangeDiagnostic
|
||||||
| UnexpectedTokenDiagnostic
|
| UnexpectedTokenDiagnostic
|
||||||
| FieldNotFoundDiagnostic
|
| FieldNotFoundDiagnostic
|
||||||
| KindMismatchDiagnostic
|
| KindMismatchDiagnostic
|
||||||
|
@ -510,6 +527,8 @@ export function describeType(type: Type): string {
|
||||||
}
|
}
|
||||||
return out + ')';
|
return out + ')';
|
||||||
}
|
}
|
||||||
|
case TypeKind.TupleIndex:
|
||||||
|
return describeType(type.tupleType) + '.' + type.index;
|
||||||
case TypeKind.Nominal:
|
case TypeKind.Nominal:
|
||||||
{
|
{
|
||||||
return type.decl.name.text;
|
return type.decl.name.text;
|
||||||
|
|
|
@ -68,6 +68,11 @@ import {
|
||||||
AssignStatement,
|
AssignStatement,
|
||||||
ForallTypeExpression,
|
ForallTypeExpression,
|
||||||
TypeExpressionWithConstraints,
|
TypeExpressionWithConstraints,
|
||||||
|
Annotation,
|
||||||
|
TypeAnnotation,
|
||||||
|
Annotations,
|
||||||
|
ExprOperator,
|
||||||
|
Integer,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Stream } from "./util";
|
import { Stream } from "./util";
|
||||||
|
|
||||||
|
@ -322,16 +327,55 @@ export class Parser {
|
||||||
return new ArrowTypeExpression(paramTypes, returnType);
|
return new ArrowTypeExpression(paramTypes, returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseConstantExpression(): ConstantExpression {
|
private parseAnnotations(inline = true): Annotation[] {
|
||||||
|
const annotations = [];
|
||||||
|
for (;;) {
|
||||||
|
const t0 = this.tokens.peek();
|
||||||
|
if (t0.kind !== SyntaxKind.At) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.tokens.get();
|
||||||
|
const t1 = this.tokens.peek();
|
||||||
|
if (t1.kind === SyntaxKind.Colon) {
|
||||||
|
this.tokens.get();
|
||||||
|
let typeExpr;
|
||||||
|
if (inline) {
|
||||||
|
typeExpr = this.parsePrimitiveTypeExpression();
|
||||||
|
} else {
|
||||||
|
typeExpr = this.parseTypeExpression();
|
||||||
|
this.expectToken(SyntaxKind.LineFoldEnd);
|
||||||
|
}
|
||||||
|
annotations.push(new TypeAnnotation(t0, t1, typeExpr));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let expr;
|
||||||
|
if (inline) {
|
||||||
|
expr = this.parsePrimitiveExpression();
|
||||||
|
} else {
|
||||||
|
expr = this.parseExpression();
|
||||||
|
this.expectToken(SyntaxKind.LineFoldEnd);
|
||||||
|
}
|
||||||
|
annotations.push(new ExprAnnotation(t0, expr));
|
||||||
|
}
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseConstantExpression(annotations?: Annotation[]): ConstantExpression {
|
||||||
|
if (annotations === undefined) {
|
||||||
|
annotations = this.parseAnnotations();
|
||||||
|
}
|
||||||
const token = this.getToken()
|
const token = this.getToken()
|
||||||
if (token.kind !== SyntaxKind.StringLiteral
|
if (token.kind !== SyntaxKind.StringLiteral
|
||||||
&& token.kind !== SyntaxKind.Integer) {
|
&& token.kind !== SyntaxKind.Integer) {
|
||||||
this.raiseParseError(token, [ SyntaxKind.StringLiteral, SyntaxKind.Integer ])
|
this.raiseParseError(token, [ SyntaxKind.StringLiteral, SyntaxKind.Integer ])
|
||||||
}
|
}
|
||||||
return new ConstantExpression(token);
|
return new ConstantExpression(annotations, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseReferenceExpression(): ReferenceExpression {
|
public parseReferenceExpression(annotations?: Annotation[]): ReferenceExpression {
|
||||||
|
if (annotations === undefined) {
|
||||||
|
annotations = this.parseAnnotations();
|
||||||
|
}
|
||||||
const modulePath: Array<[IdentifierAlt, Dot]> = [];
|
const modulePath: Array<[IdentifierAlt, Dot]> = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t0 = this.peekToken(1);
|
const t0 = this.peekToken(1);
|
||||||
|
@ -347,10 +391,13 @@ export class Parser {
|
||||||
if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.IdentifierAlt) {
|
if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.IdentifierAlt) {
|
||||||
this.raiseParseError(name, [ SyntaxKind.Identifier, SyntaxKind.IdentifierAlt ]);
|
this.raiseParseError(name, [ SyntaxKind.Identifier, SyntaxKind.IdentifierAlt ]);
|
||||||
}
|
}
|
||||||
return new ReferenceExpression(modulePath, name);
|
return new ReferenceExpression(annotations, modulePath, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseExpressionWithParens(): Expression {
|
private parseExpressionWithParens(annotations?: Annotation[]): Expression {
|
||||||
|
if (annotations === undefined) {
|
||||||
|
annotations = this.parseAnnotations();
|
||||||
|
}
|
||||||
const elements = [];
|
const elements = [];
|
||||||
const lparen = this.expectToken(SyntaxKind.LParen)
|
const lparen = this.expectToken(SyntaxKind.LParen)
|
||||||
let rparen;
|
let rparen;
|
||||||
|
@ -374,46 +421,52 @@ export class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (elements.length === 1) {
|
if (elements.length === 1) {
|
||||||
return new NestedExpression(lparen, elements[0], rparen);
|
return new NestedExpression(annotations, lparen, elements[0], rparen);
|
||||||
}
|
}
|
||||||
return new TupleExpression(lparen, elements, rparen);
|
return new TupleExpression(annotations, lparen, elements, rparen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public parseMatchExpression(annotations?: Annotation[]): MatchExpression {
|
||||||
|
if (annotations === undefined) {
|
||||||
|
annotations = this.parseAnnotations();
|
||||||
|
}
|
||||||
|
const t0 = this.expectToken(SyntaxKind.MatchKeyword);
|
||||||
|
let expression = null
|
||||||
|
const t1 = this.peekToken();
|
||||||
|
if (t1.kind !== SyntaxKind.BlockStart) {
|
||||||
|
expression = this.parseExpression();
|
||||||
|
}
|
||||||
|
this.expectToken(SyntaxKind.BlockStart);
|
||||||
|
const arms = [];
|
||||||
|
for (;;) {
|
||||||
|
const t2 = this.peekToken();
|
||||||
|
if (t2.kind === SyntaxKind.BlockEnd) {
|
||||||
|
this.getToken();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const pattern = this.parsePattern();
|
||||||
|
const rarrowAlt = this.expectToken(SyntaxKind.RArrowAlt);
|
||||||
|
const expression = this.parseExpression();
|
||||||
|
arms.push(new MatchArm(pattern, rarrowAlt, expression));
|
||||||
|
this.expectToken(SyntaxKind.LineFoldEnd);
|
||||||
|
}
|
||||||
|
return new MatchExpression(annotations, t0, expression, arms);
|
||||||
}
|
}
|
||||||
|
|
||||||
private parsePrimitiveExpression(): Expression {
|
private parsePrimitiveExpression(): Expression {
|
||||||
|
const annotations = this.parseAnnotations();
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken();
|
||||||
switch (t0.kind) {
|
switch (t0.kind) {
|
||||||
case SyntaxKind.LParen:
|
case SyntaxKind.LParen:
|
||||||
return this.parseExpressionWithParens();
|
return this.parseExpressionWithParens(annotations);
|
||||||
case SyntaxKind.Identifier:
|
case SyntaxKind.Identifier:
|
||||||
case SyntaxKind.IdentifierAlt:
|
case SyntaxKind.IdentifierAlt:
|
||||||
return this.parseReferenceExpression();
|
return this.parseReferenceExpression(annotations);
|
||||||
case SyntaxKind.Integer:
|
case SyntaxKind.Integer:
|
||||||
case SyntaxKind.StringLiteral:
|
case SyntaxKind.StringLiteral:
|
||||||
return this.parseConstantExpression();
|
return this.parseConstantExpression(annotations);
|
||||||
case SyntaxKind.MatchKeyword:
|
case SyntaxKind.MatchKeyword:
|
||||||
{
|
return this.parseMatchExpression(annotations);
|
||||||
this.getToken();
|
|
||||||
let expression = null
|
|
||||||
const t1 = this.peekToken();
|
|
||||||
if (t1.kind !== SyntaxKind.BlockStart) {
|
|
||||||
expression = this.parseExpression();
|
|
||||||
}
|
|
||||||
this.expectToken(SyntaxKind.BlockStart);
|
|
||||||
const arms = [];
|
|
||||||
for (;;) {
|
|
||||||
const t2 = this.peekToken();
|
|
||||||
if (t2.kind === SyntaxKind.BlockEnd) {
|
|
||||||
this.getToken();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const pattern = this.parsePattern();
|
|
||||||
const rarrowAlt = this.expectToken(SyntaxKind.RArrowAlt);
|
|
||||||
const expression = this.parseExpression();
|
|
||||||
arms.push(new MatchArm(pattern, rarrowAlt, expression));
|
|
||||||
this.expectToken(SyntaxKind.LineFoldEnd);
|
|
||||||
}
|
|
||||||
return new MatchExpression(t0, expression, arms);
|
|
||||||
}
|
|
||||||
case SyntaxKind.LBrace:
|
case SyntaxKind.LBrace:
|
||||||
{
|
{
|
||||||
this.getToken();
|
this.getToken();
|
||||||
|
@ -452,7 +505,7 @@ export class Parser {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new StructExpression(t0, fields, rbrace);
|
return new StructExpression(annotations, t0, fields, rbrace);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
this.raiseParseError(t0, [
|
this.raiseParseError(t0, [
|
||||||
|
@ -466,20 +519,25 @@ export class Parser {
|
||||||
|
|
||||||
private tryParseMemberExpression(): Expression {
|
private tryParseMemberExpression(): Expression {
|
||||||
const expression = this.parsePrimitiveExpression();
|
const expression = this.parsePrimitiveExpression();
|
||||||
const path: Array<[Dot, Identifier]> = [];
|
const path: Array<[Dot, Identifier | Integer]> = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t1 = this.peekToken();
|
const t1 = this.peekToken();
|
||||||
if (t1.kind !== SyntaxKind.Dot) {
|
if (t1.kind !== SyntaxKind.Dot) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.getToken();
|
this.getToken();
|
||||||
const name = this.expectToken(SyntaxKind.Identifier);
|
const t2 = this.getToken();
|
||||||
path.push([t1, name]);
|
if (t2.kind !== SyntaxKind.Identifier && t2.kind !== SyntaxKind.Integer) {
|
||||||
|
this.raiseParseError(t2, [ SyntaxKind.Identifier, SyntaxKind.Integer ]);
|
||||||
|
}
|
||||||
|
path.push([t1, t2]);
|
||||||
}
|
}
|
||||||
if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
return new MemberExpression(expression, path);
|
const annotations = expression.annotations;
|
||||||
|
expression.annotations = [];
|
||||||
|
return new MemberExpression(annotations, expression, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private tryParseCallExpression(): Expression {
|
private tryParseCallExpression(): Expression {
|
||||||
|
@ -501,13 +559,16 @@ export class Parser {
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
return func
|
return func
|
||||||
}
|
}
|
||||||
return new CallExpression(func, args);
|
const annotations = func.annotations;
|
||||||
|
func.annotations = [];
|
||||||
|
return new CallExpression(annotations, func, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseUnaryExpression(): Expression {
|
private parseUnaryExpression(): Expression {
|
||||||
let result = this.tryParseCallExpression()
|
let result = this.tryParseCallExpression()
|
||||||
const prefixes = [];
|
const prefixes: Array<[Annotations, ExprOperator]> = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
const annotations = this.parseAnnotations();
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken();
|
||||||
if (!isExprOperator(t0)) {
|
if (!isExprOperator(t0)) {
|
||||||
break;
|
break;
|
||||||
|
@ -515,12 +576,12 @@ export class Parser {
|
||||||
if (!this.prefixExprOperators.has(t0.text)) {
|
if (!this.prefixExprOperators.has(t0.text)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prefixes.push(t0);
|
prefixes.push([annotations, t0]);
|
||||||
this.getToken()
|
this.getToken()
|
||||||
}
|
}
|
||||||
for (let i = prefixes.length-1; i >= 0; i--) {
|
for (let i = prefixes.length-1; i >= 0; i--) {
|
||||||
const operator = prefixes[i];
|
const [annotations, operator] = prefixes[i];
|
||||||
result = new PrefixExpression(operator, result);
|
result = new PrefixExpression(annotations, operator, result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -550,7 +611,9 @@ export class Parser {
|
||||||
}
|
}
|
||||||
rhs = this.parseBinaryOperatorAfterExpr(rhs, info0.precedence);
|
rhs = this.parseBinaryOperatorAfterExpr(rhs, info0.precedence);
|
||||||
}
|
}
|
||||||
lhs = new InfixExpression(lhs, t0, rhs);
|
const annotations = lhs.annotations;
|
||||||
|
lhs.annotations = [];
|
||||||
|
lhs = new InfixExpression(annotations, lhs, t0, rhs);
|
||||||
}
|
}
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
||||||
new ExprBody(
|
new ExprBody(
|
||||||
new Equals(),
|
new Equals(),
|
||||||
new StructExpression(
|
new StructExpression(
|
||||||
|
[],
|
||||||
new LBrace(),
|
new LBrace(),
|
||||||
node.elements.map(element => {
|
node.elements.map(element => {
|
||||||
assert(element.kind === SyntaxKind.LetDeclaration);
|
assert(element.kind === SyntaxKind.LetDeclaration);
|
||||||
|
@ -71,7 +72,7 @@ export class TypeclassDictPassing implements Pass<SourceFile, SourceFile> {
|
||||||
return new StructExpressionField(
|
return new StructExpressionField(
|
||||||
new Identifier(null, element.pattern.name.text),
|
new Identifier(null, element.pattern.name.text),
|
||||||
new Equals(),
|
new Equals(),
|
||||||
new FunctionExpression(new Backslash(), element.params, element.body!)
|
new FunctionExpression([], new Backslash(), element.params, element.body!)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
new RBrace(),
|
new RBrace(),
|
||||||
|
|
|
@ -46,6 +46,7 @@ import {
|
||||||
InstanceKeyword,
|
InstanceKeyword,
|
||||||
Backslash,
|
Backslash,
|
||||||
ForallKeyword,
|
ForallKeyword,
|
||||||
|
At,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Diagnostics } from "./diagnostics"
|
import { Diagnostics } from "./diagnostics"
|
||||||
import { Stream, BufferedStream, assert } from "./util";
|
import { Stream, BufferedStream, assert } from "./util";
|
||||||
|
@ -221,6 +222,7 @@ export class Scanner extends BufferedStream<Token> {
|
||||||
return new EndOfFile(startPos);
|
return new EndOfFile(startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case '@': return new At(startPos);
|
||||||
case '\\': return new Backslash(startPos);
|
case '\\': return new Backslash(startPos);
|
||||||
case '(': return new LParen(startPos);
|
case '(': return new LParen(startPos);
|
||||||
case ')': return new RParen(startPos);
|
case ')': return new RParen(startPos);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { InspectOptions } from "util";
|
import { InspectOptions } from "util";
|
||||||
import { ClassDeclaration, EnumDeclaration, StructDeclaration, Syntax } from "./cst";
|
import { ClassDeclaration, EnumDeclaration, StructDeclaration, Syntax } from "./cst";
|
||||||
import { InspectFn, toStringTag } from "./util";
|
import { InspectFn, assert, assertNever, toStringTag } from "./util";
|
||||||
|
|
||||||
export enum TypeKind {
|
export enum TypeKind {
|
||||||
Arrow,
|
Arrow,
|
||||||
|
@ -568,6 +568,58 @@ export type TVar
|
||||||
= TUniVar
|
= TUniVar
|
||||||
| TRigidVar
|
| TRigidVar
|
||||||
|
|
||||||
|
|
||||||
|
export function typesEqual(a: Type, b: Type): boolean {
|
||||||
|
if (a.kind !== b.kind) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (a.kind) {
|
||||||
|
case TypeKind.Con:
|
||||||
|
assert(b.kind === TypeKind.Con);
|
||||||
|
return a.id === b.id;
|
||||||
|
case TypeKind.UniVar:
|
||||||
|
assert(b.kind === TypeKind.UniVar);
|
||||||
|
return a.id === b.id;
|
||||||
|
case TypeKind.RigidVar:
|
||||||
|
assert(b.kind === TypeKind.RigidVar);
|
||||||
|
return a.id === b.id;
|
||||||
|
case TypeKind.Nil:
|
||||||
|
case TypeKind.Absent:
|
||||||
|
return true;
|
||||||
|
case TypeKind.Nominal:
|
||||||
|
assert(b.kind === TypeKind.Nominal);
|
||||||
|
return a.decl === b.decl;
|
||||||
|
case TypeKind.App:
|
||||||
|
assert(b.kind === TypeKind.App);
|
||||||
|
return typesEqual(a.left, b.left) && typesEqual(a.right, b.right);
|
||||||
|
case TypeKind.Field:
|
||||||
|
assert(b.kind === TypeKind.Field);
|
||||||
|
return a.name === b.name && typesEqual(a.type, b.type) && typesEqual(a.restType, b.restType);
|
||||||
|
case TypeKind.Arrow:
|
||||||
|
assert(b.kind === TypeKind.Arrow);
|
||||||
|
return typesEqual(a.paramType, b.paramType) && typesEqual(a.returnType, b.returnType);
|
||||||
|
case TypeKind.Tuple:
|
||||||
|
assert(b.kind === TypeKind.Tuple);
|
||||||
|
if (a.elementTypes.length !== b.elementTypes.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < a.elementTypes.length; i++) {
|
||||||
|
if (!typesEqual(a.elementTypes[i], b.elementTypes[i])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case TypeKind.Present:
|
||||||
|
assert(b.kind === TypeKind.Present);
|
||||||
|
return typesEqual(a.type, b.type);
|
||||||
|
case TypeKind.TupleIndex:
|
||||||
|
assert(b.kind === TypeKind.TupleIndex);
|
||||||
|
return a.index === b.index && typesEqual(a.tupleType, b.tupleType);
|
||||||
|
default:
|
||||||
|
assertNever(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TVSet {
|
export class TVSet {
|
||||||
|
|
||||||
private mapping = new Map<number, TVar>();
|
private mapping = new Map<number, TVar>();
|
||||||
|
|
Loading…
Reference in a new issue