Multiple updates to the type-checker

- Add support for type declarations
- Make polymorphism in struct declarations work
This commit is contained in:
Sam Vervaeck 2022-09-11 11:20:21 +02:00
parent d12ffa1de5
commit 988215cdb3
4 changed files with 353 additions and 41 deletions

View file

@ -4,13 +4,21 @@ import {
Pattern, Pattern,
Scope, Scope,
SourceFile, SourceFile,
SourceFileElement, Symkind,
StructDeclaration,
Syntax, Syntax,
SyntaxKind, SyntaxKind,
TypeExpression TypeExpression
} from "./cst"; } from "./cst";
import { ArityMismatchDiagnostic, BindingNotFoudDiagnostic, describeType, Diagnostics, FieldDoesNotExistDiagnostic, FieldMissingDiagnostic, UnificationFailedDiagnostic } from "./diagnostics"; import {
describeType,
ArityMismatchDiagnostic,
BindingNotFoudDiagnostic,
Diagnostics,
FieldDoesNotExistDiagnostic,
FieldMissingDiagnostic,
UnificationFailedDiagnostic,
KindMismatchDiagnostic
} from "./diagnostics";
import { assert, isEmpty } from "./util"; import { assert, isEmpty } from "./util";
import { LabeledDirectedHashGraph, LabeledGraph, strongconnect } from "yagl" import { LabeledDirectedHashGraph, LabeledGraph, strongconnect } from "yagl"
@ -30,6 +38,7 @@ export enum TypeKind {
Tuple, Tuple,
Labeled, Labeled,
Record, Record,
App,
} }
abstract class TypeBase { abstract class TypeBase {
@ -272,7 +281,7 @@ export class TRecord extends TypeBase {
public nextRecord: TRecord | null = null; public nextRecord: TRecord | null = null;
public constructor( public constructor(
public decl: StructDeclaration, public decl: Syntax,
public fields: Map<string, Type>, public fields: Map<string, Type>,
public node: Syntax | null = null, public node: Syntax | null = null,
) { ) {
@ -308,6 +317,68 @@ export class TRecord extends TypeBase {
} }
export class TApp extends TypeBase {
public readonly kind = TypeKind.App;
public constructor(
public operatorType: Type,
public argType: Type,
public node: Syntax | null = null
) {
super(node);
}
public static build(operatorType: Type, argTypes: Type[], node: Syntax | null = null): TApp {
let count = argTypes.length;
let result = argTypes[count-1];
for (let i = count-2; i >= 0; i--) {
result = new TApp(argTypes[i], result, node);
}
return new TApp(operatorType, result, node);
}
public *getSequence(): Iterable<Type> {
if (this.operatorType.kind === TypeKind.App) {
yield* this.operatorType.getSequence();
} else {
yield this.operatorType;
}
if (this.argType.kind === TypeKind.App) {
yield* this.argType.getSequence();
} else {
yield this.argType;
}
}
public *getTypeVars(): Iterable<TVar> {
yield* this.operatorType.getTypeVars();
yield* this.argType.getTypeVars();
}
public shallowClone() {
return new TApp(
this.operatorType,
this.argType,
this.node
);
}
public substitute(sub: TVSub): Type {
let changed = false;
const newOperatorType = this.operatorType.substitute(sub);
if (newOperatorType !== this.operatorType) {
changed = true;
}
const newArgType = this.argType.substitute(sub);
if (newArgType !== this.argType) {
changed = true;
}
return changed ? new TApp(newOperatorType, newArgType, this.node) : this;
}
}
export type Type export type Type
= TCon = TCon
| TArrow | TArrow
@ -315,6 +386,7 @@ export type Type
| TTuple | TTuple
| TLabeled | TLabeled
| TRecord | TRecord
| TApp
class TVSet { class TVSet {
@ -597,11 +669,15 @@ export class Checker {
return context.returnType; return context.returnType;
} }
private instantiate(scheme: Scheme, node: Syntax | null): Type { private createSubstitution(scheme: Scheme): TVSub {
const sub = new TVSub(); const sub = new TVSub();
for (const tv of scheme.typeVars) { for (const tv of scheme.typeVars) {
sub.set(tv, this.createTypeVar()); sub.set(tv, this.createTypeVar());
} }
return sub;
}
private instantiate(scheme: Scheme, node: Syntax | null, sub = this.createSubstitution(scheme)): Type {
for (const constraint of scheme.constraints) { for (const constraint of scheme.constraints) {
const substituted = constraint.substitute(sub); const substituted = constraint.substitute(sub);
substituted.node = node; substituted.node = node;
@ -716,6 +792,8 @@ export class Checker {
break; break;
} }
case SyntaxKind.TypeDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
break; break;
@ -820,13 +898,20 @@ export class Checker {
case SyntaxKind.StructExpression: case SyntaxKind.StructExpression:
{ {
const scheme = this.lookup(node.name.text); const scope = node.getScope();
if (scheme === null) { const decl = scope.lookup(node.name.text, Symkind.Constructor);
if (decl === null) {
this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name)); this.diagnostics.add(new BindingNotFoudDiagnostic(node.name.text, node.name));
return this.createTypeVar(); return this.createTypeVar();
} }
const recordType = this.instantiate(scheme, node); assert(decl.kind === SyntaxKind.StructDeclaration || decl.kind === SyntaxKind.EnumDeclarationStructElement);
assert(recordType.kind === TypeKind.Record); const scheme = decl.scheme;
const sub = this.createSubstitution(scheme);
const declType = this.instantiate(scheme, node, sub);
const argTypes = [];
for (const typeVar of decl.tvs) {
argTypes.push(sub.get(typeVar)!);
}
const fields = new Map(); const fields = new Map();
for (const member of node.members) { for (const member of node.members) {
switch (member.kind) { switch (member.kind) {
@ -852,10 +937,10 @@ export class Checker {
throw new Error(`Unexpected ${member}`); throw new Error(`Unexpected ${member}`);
} }
} }
const type = new TRecord(recordType.decl, fields, node); const type = TApp.build(new TRecord(decl, fields, node), argTypes, node);
this.addConstraint( this.addConstraint(
new CEqual( new CEqual(
recordType, TApp.build(declType, argTypes, node),
type, type,
node, node,
) )
@ -923,6 +1008,16 @@ export class Checker {
return scheme.type; return scheme.type;
} }
case SyntaxKind.AppTypeExpression:
{
const operatorType = this.inferTypeExpression(node.operator);
const argTypes = [];
for (const argTypeExpr of node.args) {
argTypes.push(this.inferTypeExpression(argTypeExpr));
}
return TApp.build(operatorType, argTypes);
}
case SyntaxKind.ArrowTypeExpression: case SyntaxKind.ArrowTypeExpression:
{ {
const paramTypes = []; const paramTypes = [];
@ -1145,6 +1240,7 @@ export class Checker {
break; break;
} }
case SyntaxKind.TypeDeclaration:
case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumDeclaration:
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
break; break;
@ -1186,6 +1282,7 @@ export class Checker {
case SyntaxKind.IfStatement: case SyntaxKind.IfStatement:
case SyntaxKind.ReturnStatement: case SyntaxKind.ReturnStatement:
case SyntaxKind.ExpressionStatement: case SyntaxKind.ExpressionStatement:
case SyntaxKind.TypeDeclaration:
case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumDeclaration:
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
break; break;
@ -1232,6 +1329,29 @@ export class Checker {
break; break;
} }
case SyntaxKind.TypeDeclaration:
{
const env = node.typeEnv = new TypeEnv(parentEnv);
const constraints = new ConstraintSet();
const typeVars = new TVSet();
const context: InferContext = {
constraints,
typeVars,
env,
returnType: null,
};
this.pushContext(context);
for (const varExpr of node.typeVars) {
env.add(varExpr.text, new Forall([], [], this.createTypeVar()));
}
const type = this.inferTypeExpression(node.typeExpression);
this.popContext(context);
const scheme = new Forall(typeVars, constraints, type);
parentEnv.add(node.name.text, scheme);
node.scheme = scheme;
break;
}
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
{ {
const env = node.typeEnv = new TypeEnv(parentEnv); const env = node.typeEnv = new TypeEnv(parentEnv);
@ -1244,8 +1364,11 @@ export class Checker {
returnType: null, returnType: null,
}; };
this.pushContext(context); this.pushContext(context);
const argTypes = [];
for (const varExpr of node.typeVars) { for (const varExpr of node.typeVars) {
env.add(varExpr.text, new Forall([], [], this.createTypeVar())); const type = this.createTypeVar();
env.add(varExpr.text, new Forall([], [], type));
argTypes.push(type);
} }
const fields = new Map<string, Type>(); const fields = new Map<string, Type>();
if (node.members !== null) { if (node.members !== null) {
@ -1254,8 +1377,11 @@ export class Checker {
} }
} }
this.popContext(context); this.popContext(context);
const type = new TRecord(node, fields); const type = new TRecord(node, fields, node);
parentEnv.add(node.name.text, new Forall(typeVars, constraints, type)); const scheme = new Forall(typeVars, constraints, type);
parentEnv.add(node.name.text, scheme);
node.tvs = argTypes;
node.scheme = scheme; //new Forall(typeVars, constraints, new TApp(type, argTypes));
break; break;
} }
@ -1561,6 +1687,23 @@ export class Checker {
} }
} }
if (left.kind === TypeKind.App && right.kind === TypeKind.App) {
let leftElements = [...left.getSequence()];
let rightElements = [...right.getSequence()];
if (leftElements.length !== rightElements.length) {
this.diagnostics.add(new KindMismatchDiagnostic(leftElements.length-1, rightElements.length-1, constraint.node));
return false;
}
const count = leftElements.length;
let success = true;
for (let i = 0; i < count; i++) {
if (!this.unify(leftElements[i], rightElements[i], solution, constraint)) {
success = false;
}
}
return success;
}
if (left.kind === TypeKind.Labeled && right.kind === TypeKind.Labeled) { if (left.kind === TypeKind.Labeled && right.kind === TypeKind.Labeled) {
let success = false; let success = false;
// This works like an ordinary union-find algorithm where an additional // This works like an ordinary union-find algorithm where an additional
@ -1604,12 +1747,12 @@ export class Checker {
} }
remaining.delete(fieldName); remaining.delete(fieldName);
} else { } else {
this.diagnostics.add(new FieldMissingDiagnostic(right, fieldName)); this.diagnostics.add(new FieldMissingDiagnostic(right, fieldName, constraint.node));
success = false; success = false;
} }
} }
for (const fieldName of remaining) { for (const fieldName of remaining) {
this.diagnostics.add(new FieldDoesNotExistDiagnostic(left, fieldName)); this.diagnostics.add(new FieldDoesNotExistDiagnostic(left, fieldName, constraint.node));
} }
if (success) { if (success) {
TypeBase.join(left, right); TypeBase.join(left, right);

View file

@ -108,6 +108,7 @@ export const enum SyntaxKind {
ReferenceTypeExpression, ReferenceTypeExpression,
ArrowTypeExpression, ArrowTypeExpression,
VarTypeExpression, VarTypeExpression,
AppTypeExpression,
// Patterns // Patterns
BindPattern, BindPattern,
@ -147,14 +148,11 @@ export const enum SyntaxKind {
IfStatementCase, IfStatementCase,
// Declarations // Declarations
VariableDeclaration,
PrefixFuncDecl,
SuffixFuncDecl,
LetDeclaration, LetDeclaration,
StructDeclaration, StructDeclaration,
EnumDeclaration, EnumDeclaration,
ImportDeclaration, ImportDeclaration,
TypeAliasDeclaration, TypeDeclaration,
// Let declaration body members // Let declaration body members
ExprBody, ExprBody,
@ -185,6 +183,7 @@ export type Syntax
| Param | Param
| Body | Body
| StructDeclarationField | StructDeclarationField
| EnumDeclarationElement
| TypeAssert | TypeAssert
| Declaration | Declaration
| Statement | Statement
@ -207,9 +206,16 @@ function isNodeWithScope(node: Syntax): node is NodeWithScope {
|| node.kind === SyntaxKind.LetDeclaration; || node.kind === SyntaxKind.LetDeclaration;
} }
export const enum Symkind {
Var = 1,
Type = 2,
Constructor = 4,
Any = Var | Type | Constructor
}
export class Scope { export class Scope {
private mapping = new Map<string, Syntax>(); private mapping = new Map<string, [Symkind, Syntax]>();
public constructor( public constructor(
public node: NodeWithScope, public node: NodeWithScope,
@ -228,6 +234,10 @@ export class Scope {
return null; return null;
} }
private add(name: string, node: Syntax, kind: Symkind): void {
this.mapping.set(name, [kind, node]);
}
private scan(node: Syntax): void { private scan(node: Syntax): void {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.SourceFile: case SyntaxKind.SourceFile:
@ -241,9 +251,17 @@ export class Scope {
case SyntaxKind.ReturnStatement: case SyntaxKind.ReturnStatement:
case SyntaxKind.IfStatement: case SyntaxKind.IfStatement:
break; break;
case SyntaxKind.TypeDeclaration:
{
this.add(node.name.text, node, Symkind.Type);
break;
}
case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumDeclaration:
case SyntaxKind.StructDeclaration: case SyntaxKind.StructDeclaration:
{
this.add(node.name.text, node, Symkind.Constructor);
break; break;
}
case SyntaxKind.LetDeclaration: case SyntaxKind.LetDeclaration:
{ {
for (const param of node.params) { for (const param of node.params) {
@ -257,7 +275,7 @@ export class Scope {
} }
} else { } else {
if (node.pattern.kind === SyntaxKind.WrappedOperator) { if (node.pattern.kind === SyntaxKind.WrappedOperator) {
this.mapping.set(node.pattern.operator.text, node); this.add(node.pattern.operator.text, node, Symkind.Var);
} else { } else {
this.scanPattern(node.pattern, node); this.scanPattern(node.pattern, node);
} }
@ -273,7 +291,7 @@ export class Scope {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BindPattern: case SyntaxKind.BindPattern:
{ {
this.mapping.set(node.name.text, decl); this.add(node.name.text, decl, Symkind.Var);
break; break;
} }
case SyntaxKind.StructPattern: case SyntaxKind.StructPattern:
@ -287,7 +305,7 @@ export class Scope {
} }
case SyntaxKind.PunnedStructPatternField: case SyntaxKind.PunnedStructPatternField:
{ {
this.mapping.set(node.name.text, decl); this.add(node.name.text, decl, Symkind.Var);
break; break;
} }
} }
@ -299,13 +317,16 @@ export class Scope {
} }
} }
public lookup(name: string): Syntax | null { public lookup(name: string, expectedKind = Symkind.Any): Syntax | null {
let curr: Scope | null = this; let curr: Scope | null = this;
do { do {
const decl = curr.mapping.get(name); const match = curr.mapping.get(name);
if (decl !== undefined) { if (match !== undefined) {
const [kind, decl] = match;
if (kind & expectedKind) {
return decl; return decl;
} }
}
curr = curr.getParent(); curr = curr.getParent();
} while (curr !== null); } while (curr !== null);
return null; return null;
@ -967,6 +988,30 @@ export class ReferenceTypeExpression extends SyntaxBase {
} }
export class AppTypeExpression extends SyntaxBase {
public readonly kind = SyntaxKind.AppTypeExpression;
public constructor(
public operator: TypeExpression,
public args: TypeExpression[],
) {
super();
}
public getFirstToken(): Token {
return this.operator.getFirstToken();
}
public getLastToken(): Token {
if (this.args.length > 0) {
return this.args[this.args.length-1].getLastToken();
}
return this.operator.getLastToken();
}
}
export class VarTypeExpression extends SyntaxBase { export class VarTypeExpression extends SyntaxBase {
public readonly kind = SyntaxKind.VarTypeExpression; public readonly kind = SyntaxKind.VarTypeExpression;
@ -991,6 +1036,7 @@ export type TypeExpression
= ReferenceTypeExpression = ReferenceTypeExpression
| ArrowTypeExpression | ArrowTypeExpression
| VarTypeExpression | VarTypeExpression
| AppTypeExpression
export class BindPattern extends SyntaxBase { export class BindPattern extends SyntaxBase {
@ -1853,6 +1899,34 @@ export class WrappedOperator extends SyntaxBase {
} }
export class TypeDeclaration extends SyntaxBase {
public readonly kind = SyntaxKind.TypeDeclaration;
public constructor(
public pubKeyword: PubKeyword | null,
public typeKeyword: TypeKeyword,
public name: IdentifierAlt,
public typeVars: Identifier[],
public equals: Equals,
public typeExpression: TypeExpression
) {
super();
}
public getFirstToken(): Token {
if (this.pubKeyword !== null) {
return this.pubKeyword;
}
return this.typeKeyword;
}
public getLastToken(): Token {
return this.typeExpression.getLastToken();
}
}
export class LetDeclaration extends SyntaxBase { export class LetDeclaration extends SyntaxBase {
public readonly kind = SyntaxKind.LetDeclaration; public readonly kind = SyntaxKind.LetDeclaration;
@ -1923,6 +1997,7 @@ export type Declaration
| ImportDeclaration | ImportDeclaration
| StructDeclaration | StructDeclaration
| EnumDeclaration | EnumDeclaration
| TypeDeclaration
export class Initializer extends SyntaxBase { export class Initializer extends SyntaxBase {

View file

@ -198,19 +198,25 @@ export function describeType(type: Type): string {
} }
case TypeKind.Record: case TypeKind.Record:
{ {
let out = type.decl.name.text + ' { '; return type.decl.name.text;
let first = true; // let out = type.decl.name.text + ' { ';
for (const [fieldName, fieldType] of type.fields) { // let first = true;
if (first) first = false; // for (const [fieldName, fieldType] of type.fields) {
else out += ', '; // if (first) first = false;
out += fieldName + ': ' + describeType(fieldType); // else out += ', ';
} // out += fieldName + ': ' + describeType(fieldType);
return out + ' }'; // }
// return out + ' }';
} }
case TypeKind.Labeled: case TypeKind.Labeled:
{ {
// FIXME may need to include fields that were added during unification
return '{ ' + type.name + ': ' + describeType(type.type) + ' }'; return '{ ' + type.name + ': ' + describeType(type.type) + ' }';
} }
case TypeKind.App:
{
return describeType(type.operatorType) + ' ' + describeType(type.argType);
}
} }
} }
@ -294,6 +300,7 @@ export class FieldMissingDiagnostic {
public constructor( public constructor(
public recordType: TRecord, public recordType: TRecord,
public fieldName: string, public fieldName: string,
public node: Syntax | null,
) { ) {
} }
@ -302,8 +309,8 @@ export class FieldMissingDiagnostic {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET); out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`field '${this.fieldName}' is missing from `); out.write(`field '${this.fieldName}' is missing from `);
out.write(describeType(this.recordType) + '\n\n'); out.write(describeType(this.recordType) + '\n\n');
if (this.recordType.node !== null) { if (this.node !== null) {
out.write(printNode(this.recordType.node) + '\n'); out.write(printNode(this.node) + '\n');
} }
} }
@ -316,13 +323,48 @@ export class FieldDoesNotExistDiagnostic {
public constructor( public constructor(
public recordType: TRecord, public recordType: TRecord,
public fieldName: string, public fieldName: string,
public node: Syntax | null,
) { ) {
} }
public format(out: IndentWriter): void { public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET); out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`field '${this.fieldName}' does not exist on type `); out.write(`field '${this.fieldName}' does not exist on type `);
out.write(describeType(this.recordType) + '\n\n'); out.write(describeType(this.recordType) + '\n\n');
if (this.node !== null) {
out.write(printNode(this.node) + '\n');
}
}
}
export class KindMismatchDiagnostic {
public readonly level = Level.Error;
public constructor(
public leftSize: number,
public rightSize: number,
public node: Syntax | null,
) {
}
public format(out: IndentWriter): void {
out.write(ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET);
out.write(`kind `);
for (let i = 0; i < this.leftSize-1; i++) {
out.write(`* -> `);
}
out.write(`* does not match with `);
for (let i = 0; i < this.rightSize-1; i++) {
out.write(`* -> `);
}
out.write(`*\n\n`);
if (this.node !== null) {
out.write(printNode(this.node) + '\n');
}
} }
} }
@ -335,6 +377,7 @@ export type Diagnostic
| ArityMismatchDiagnostic | ArityMismatchDiagnostic
| FieldMissingDiagnostic | FieldMissingDiagnostic
| FieldDoesNotExistDiagnostic | FieldDoesNotExistDiagnostic
| KindMismatchDiagnostic
export interface Diagnostics { export interface Diagnostics {
add(diagnostic: Diagnostic): void; add(diagnostic: Diagnostic): void;

View file

@ -52,6 +52,8 @@ import {
EnumDeclaration, EnumDeclaration,
EnumDeclarationTupleElement, EnumDeclarationTupleElement,
VarTypeExpression, VarTypeExpression,
TypeDeclaration,
AppTypeExpression,
} from "./cst" } from "./cst"
import { Stream } from "./util"; import { Stream } from "./util";
@ -181,8 +183,30 @@ export class Parser {
} }
} }
private tryParseAppTypeExpression(): TypeExpression {
const operator = this.parsePrimitiveTypeExpression();
const args = [];
for (;;) {
const t1 = this.peekToken();
if (t1.kind === SyntaxKind.RParen
|| t1.kind === SyntaxKind.RBrace
|| t1.kind === SyntaxKind.RBracket
|| t1.kind === SyntaxKind.Equals
|| t1.kind === SyntaxKind.BlockStart
|| t1.kind === SyntaxKind.LineFoldEnd
|| t1.kind === SyntaxKind.RArrow) {
break;
}
args.push(this.parsePrimitiveTypeExpression());
}
if (args.length === 0) {
return operator;
}
return new AppTypeExpression(operator, args);
}
public parseTypeExpression(): TypeExpression { public parseTypeExpression(): TypeExpression {
let returnType = this.parsePrimitiveTypeExpression(); let returnType = this.tryParseAppTypeExpression();
const paramTypes = []; const paramTypes = [];
for (;;) { for (;;) {
const t1 = this.peekToken(); const t1 = this.peekToken();
@ -191,7 +215,7 @@ export class Parser {
} }
this.getToken(); this.getToken();
paramTypes.push(returnType); paramTypes.push(returnType);
returnType = this.parsePrimitiveTypeExpression(); returnType = this.tryParseAppTypeExpression();
} }
if (paramTypes.length === 0) { if (paramTypes.length === 0) {
return returnType; return returnType;
@ -417,6 +441,31 @@ export class Parser {
return this.parseBinaryOperatorAfterExpr(lhs, 0); return this.parseBinaryOperatorAfterExpr(lhs, 0);
} }
public parseTypeDeclaration(): TypeDeclaration {
let pubKeyword = null;
let t0 = this.getToken();
if (t0.kind === SyntaxKind.PubKeyword) {
pubKeyword = t0;
t0 = this.getToken();
}
if (t0.kind !== SyntaxKind.TypeKeyword) {
this.raiseParseError(t0, [ SyntaxKind.TypeKeyword ]);
}
const name = this.expectToken(SyntaxKind.IdentifierAlt);
const typeVars = [];
let t1 = this.getToken();
while (t1.kind === SyntaxKind.Identifier) {
typeVars.push(t1);
t1 = this.getToken();
}
if (t1.kind !== SyntaxKind.Equals) {
this.raiseParseError(t1, [ SyntaxKind.Equals ]);
}
const typeExpr = this.parseTypeExpression();
this.expectToken(SyntaxKind.LineFoldEnd);
return new TypeDeclaration(pubKeyword, t0, name, typeVars, t1, typeExpr);
}
public parseEnumDeclaration(): EnumDeclaration { public parseEnumDeclaration(): EnumDeclaration {
let pubKeyword = null; let pubKeyword = null;
let t0 = this.getToken(); let t0 = this.getToken();
@ -817,7 +866,9 @@ export class Parser {
case SyntaxKind.StructKeyword: case SyntaxKind.StructKeyword:
return this.parseStructDeclaration(); return this.parseStructDeclaration();
case SyntaxKind.EnumKeyword: case SyntaxKind.EnumKeyword:
return this.parseEnumDeclaration(); return this.parseStructDeclaration();
case SyntaxKind.TypeKeyword:
return this.parseTypeDeclaration();
case SyntaxKind.IfKeyword: case SyntaxKind.IfKeyword:
return this.parseIfStatement(); return this.parseIfStatement();
default: default: