From f1a365e29c1acc15743ab4a1cabdcd65cf56c24d Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Fri, 9 Sep 2022 00:00:28 +0200 Subject: [PATCH] Add support for parsing/type-checking member expressions --- src/checker.ts | 27 +++++++++++++++++++++++++-- src/cst.ts | 27 +++++++++++++++++++++++++-- src/parser.ts | 42 ++++++++++++++++++++++++++++++------------ 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/src/checker.ts b/src/checker.ts index 8f0407878..59745cf10 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -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; diff --git a/src/cst.ts b/src/cst.ts index a35f276e5..e80596b53 100644 --- a/src/cst.ts +++ b/src/cst.ts @@ -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 diff --git a/src/parser.ts b/src/parser.ts index cacffabd2..a45764d8c 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -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();