Add support for parsing/type-checking member expressions
This commit is contained in:
parent
ebf600bdfc
commit
f1a365e29c
3 changed files with 80 additions and 16 deletions
|
@ -687,6 +687,23 @@ export class Checker {
|
||||||
return this.instantiate(scheme, node);
|
return this.instantiate(scheme, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.MemberExpression:
|
||||||
|
{
|
||||||
|
let type = this.inferExpression(node.expression);
|
||||||
|
for (const [_dot, name] of node.path) {
|
||||||
|
const newType = this.createTypeVar();
|
||||||
|
this.addConstraint(
|
||||||
|
new CEqual(
|
||||||
|
type,
|
||||||
|
new TLabeled(name.text, newType),
|
||||||
|
node,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
type = newType;
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
case SyntaxKind.CallExpression:
|
case SyntaxKind.CallExpression:
|
||||||
{
|
{
|
||||||
const opType = this.inferExpression(node.func);
|
const opType = this.inferExpression(node.func);
|
||||||
|
@ -930,6 +947,12 @@ export class Checker {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.MemberExpression:
|
||||||
|
{
|
||||||
|
this.addReferencesToGraph(graph, node.expression, source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SyntaxKind.NamedTupleExpression:
|
case SyntaxKind.NamedTupleExpression:
|
||||||
{
|
{
|
||||||
for (const arg of node.elements) {
|
for (const arg of node.elements) {
|
||||||
|
@ -1465,13 +1488,13 @@ export class Checker {
|
||||||
if (right.fields === undefined) {
|
if (right.fields === undefined) {
|
||||||
right.fields = new Map([ [ right.name, right.type ] ]);
|
right.fields = new Map([ [ right.name, right.type ] ]);
|
||||||
}
|
}
|
||||||
for (const [fieldName, fieldType] of left.fields) {
|
for (const [fieldName, fieldType] of right.fields) {
|
||||||
if (left.fields.has(fieldName)) {
|
if (left.fields.has(fieldName)) {
|
||||||
if (!this.unify(fieldType, left.fields.get(fieldName)!, solution, constraint)) {
|
if (!this.unify(fieldType, left.fields.get(fieldName)!, solution, constraint)) {
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.diagnostics.add(new FieldMissingDiagnostic(right, fieldName));
|
this.diagnostics.add(new FieldMissingDiagnostic(left, fieldName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
|
|
27
src/cst.ts
27
src/cst.ts
|
@ -122,6 +122,7 @@ export const enum SyntaxKind {
|
||||||
VariadicStructPatternElement,
|
VariadicStructPatternElement,
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
|
MemberExpression,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
ReferenceExpression,
|
ReferenceExpression,
|
||||||
NamedTupleExpression,
|
NamedTupleExpression,
|
||||||
|
@ -1142,7 +1143,7 @@ export class QualifiedName extends SyntaxBase {
|
||||||
public readonly kind = SyntaxKind.QualifiedName;
|
public readonly kind = SyntaxKind.QualifiedName;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public modulePath: Array<[Identifier, Dot]>,
|
public modulePath: Array<[IdentifierAlt, Dot]>,
|
||||||
public name: Identifier,
|
public name: Identifier,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
@ -1298,6 +1299,27 @@ export class ReferenceExpression extends SyntaxBase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class MemberExpression extends SyntaxBase {
|
||||||
|
|
||||||
|
public readonly kind = SyntaxKind.MemberExpression;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public expression: Expression,
|
||||||
|
public path: [Dot, Identifier][],
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getFirstToken(): Token {
|
||||||
|
return this.expression.getFirstToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLastToken(): Token {
|
||||||
|
return this.path[this.path.length-1][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export class PrefixExpression extends SyntaxBase {
|
export class PrefixExpression extends SyntaxBase {
|
||||||
|
|
||||||
public readonly kind = SyntaxKind.PrefixExpression;
|
public readonly kind = SyntaxKind.PrefixExpression;
|
||||||
|
@ -1363,7 +1385,8 @@ export class InfixExpression extends SyntaxBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Expression
|
export type Expression
|
||||||
= CallExpression
|
= MemberExpression
|
||||||
|
| CallExpression
|
||||||
| StructExpression
|
| StructExpression
|
||||||
| NamedTupleExpression
|
| NamedTupleExpression
|
||||||
| ReferenceExpression
|
| ReferenceExpression
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
import { warn } from "console";
|
|
||||||
import { argv0 } from "process";
|
|
||||||
import {
|
import {
|
||||||
ReferenceTypeExpression,
|
ReferenceTypeExpression,
|
||||||
SourceFile,
|
SourceFile,
|
||||||
|
@ -46,6 +44,8 @@ import {
|
||||||
PunnedStructExpressionField,
|
PunnedStructExpressionField,
|
||||||
IfStatementCase,
|
IfStatementCase,
|
||||||
IfStatement,
|
IfStatement,
|
||||||
|
MemberExpression,
|
||||||
|
IdentifierAlt,
|
||||||
} from "./cst"
|
} from "./cst"
|
||||||
import { Stream } from "./util";
|
import { Stream } from "./util";
|
||||||
|
|
||||||
|
@ -180,16 +180,16 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseQualifiedName(): QualifiedName {
|
public parseQualifiedName(): QualifiedName {
|
||||||
const modulePath: Array<[Identifier, Dot]> = [];
|
const modulePath: Array<[IdentifierAlt, Dot]> = [];
|
||||||
let name = this.expectToken(SyntaxKind.Identifier)
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t1 = this.peekToken()
|
const t0 = this.peekToken(1);
|
||||||
if (t1.kind !== SyntaxKind.Dot) {
|
const t1 = this.peekToken(2);
|
||||||
|
if (t0.kind !== SyntaxKind.IdentifierAlt || t1.kind !== SyntaxKind.Dot) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
modulePath.push([name, t1]);
|
modulePath.push([t0, t1]);
|
||||||
name = this.expectToken(SyntaxKind.Identifier)
|
|
||||||
}
|
}
|
||||||
|
const name = this.expectToken(SyntaxKind.Identifier);
|
||||||
return new QualifiedName(modulePath, name);
|
return new QualifiedName(modulePath, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,8 +290,26 @@ export class Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseExpressionNoOperators(): Expression {
|
private tryParseMemberExpression(): Expression {
|
||||||
const func = this.parsePrimitiveExpression();
|
const expression = this.parsePrimitiveExpression();
|
||||||
|
const path: Array<[Dot, Identifier]> = [];
|
||||||
|
for (;;) {
|
||||||
|
const t1 = this.peekToken();
|
||||||
|
if (t1.kind !== SyntaxKind.Dot) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.getToken();
|
||||||
|
const name = this.expectToken(SyntaxKind.Identifier);
|
||||||
|
path.push([t1, name]);
|
||||||
|
}
|
||||||
|
if (path.length === 0) {
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
return new MemberExpression(expression, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
private tryParseCallExpression(): Expression {
|
||||||
|
const func = this.tryParseMemberExpression();
|
||||||
const args = [];
|
const args = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t1 = this.peekToken();
|
const t1 = this.peekToken();
|
||||||
|
@ -305,7 +323,7 @@ export class Parser {
|
||||||
|| isPrefixOperatorLike(t1)) {
|
|| isPrefixOperatorLike(t1)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
args.push(this.parsePrimitiveExpression());
|
args.push(this.tryParseMemberExpression());
|
||||||
}
|
}
|
||||||
if (args.length === 0) {
|
if (args.length === 0) {
|
||||||
return func
|
return func
|
||||||
|
@ -314,7 +332,7 @@ export class Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseUnaryExpression(): Expression {
|
private parseUnaryExpression(): Expression {
|
||||||
let result = this.parseExpressionNoOperators()
|
let result = this.tryParseCallExpression()
|
||||||
const prefixes = [];
|
const prefixes = [];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
const t0 = this.peekToken();
|
const t0 = this.peekToken();
|
||||||
|
|
Loading…
Reference in a new issue