Add support for parsing/type-checking member expressions

This commit is contained in:
Sam Vervaeck 2022-09-09 00:00:28 +02:00
parent ebf600bdfc
commit f1a365e29c
3 changed files with 80 additions and 16 deletions

View file

@ -687,6 +687,23 @@ export class Checker {
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:
{
const opType = this.inferExpression(node.func);
@ -930,6 +947,12 @@ export class Checker {
break;
}
case SyntaxKind.MemberExpression:
{
this.addReferencesToGraph(graph, node.expression, source);
break;
}
case SyntaxKind.NamedTupleExpression:
{
for (const arg of node.elements) {
@ -1465,13 +1488,13 @@ export class Checker {
if (right.fields === undefined) {
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 (!this.unify(fieldType, left.fields.get(fieldName)!, solution, constraint)) {
success = false;
}
} else {
this.diagnostics.add(new FieldMissingDiagnostic(right, fieldName));
this.diagnostics.add(new FieldMissingDiagnostic(left, fieldName));
}
}
return success;

View file

@ -122,6 +122,7 @@ export const enum SyntaxKind {
VariadicStructPatternElement,
// Expressions
MemberExpression,
CallExpression,
ReferenceExpression,
NamedTupleExpression,
@ -1142,7 +1143,7 @@ export class QualifiedName extends SyntaxBase {
public readonly kind = SyntaxKind.QualifiedName;
public constructor(
public modulePath: Array<[Identifier, Dot]>,
public modulePath: Array<[IdentifierAlt, Dot]>,
public name: Identifier,
) {
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 {
public readonly kind = SyntaxKind.PrefixExpression;
@ -1363,7 +1385,8 @@ export class InfixExpression extends SyntaxBase {
}
export type Expression
= CallExpression
= MemberExpression
| CallExpression
| StructExpression
| NamedTupleExpression
| ReferenceExpression

View file

@ -1,6 +1,4 @@
import { warn } from "console";
import { argv0 } from "process";
import {
ReferenceTypeExpression,
SourceFile,
@ -46,6 +44,8 @@ import {
PunnedStructExpressionField,
IfStatementCase,
IfStatement,
MemberExpression,
IdentifierAlt,
} from "./cst"
import { Stream } from "./util";
@ -180,16 +180,16 @@ export class Parser {
}
public parseQualifiedName(): QualifiedName {
const modulePath: Array<[Identifier, Dot]> = [];
let name = this.expectToken(SyntaxKind.Identifier)
const modulePath: Array<[IdentifierAlt, Dot]> = [];
for (;;) {
const t1 = this.peekToken()
if (t1.kind !== SyntaxKind.Dot) {
const t0 = this.peekToken(1);
const t1 = this.peekToken(2);
if (t0.kind !== SyntaxKind.IdentifierAlt || t1.kind !== SyntaxKind.Dot) {
break;
}
modulePath.push([name, t1]);
name = this.expectToken(SyntaxKind.Identifier)
modulePath.push([t0, t1]);
}
const name = this.expectToken(SyntaxKind.Identifier);
return new QualifiedName(modulePath, name);
}
@ -290,8 +290,26 @@ export class Parser {
}
}
private parseExpressionNoOperators(): Expression {
const func = this.parsePrimitiveExpression();
private tryParseMemberExpression(): Expression {
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 = [];
for (;;) {
const t1 = this.peekToken();
@ -305,7 +323,7 @@ export class Parser {
|| isPrefixOperatorLike(t1)) {
break;
}
args.push(this.parsePrimitiveExpression());
args.push(this.tryParseMemberExpression());
}
if (args.length === 0) {
return func
@ -314,7 +332,7 @@ export class Parser {
}
private parseUnaryExpression(): Expression {
let result = this.parseExpressionNoOperators()
let result = this.tryParseCallExpression()
const prefixes = [];
for (;;) {
const t0 = this.peekToken();