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); 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;

View file

@ -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

View file

@ -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();