diff --git a/package-lock.json b/package-lock.json index fb6a629d..e9287e53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,16 +14,32 @@ "integrity": "sha512-q3zfJvaTroV5BjAAR+peTHEGAAhGrPX0z2EzCzpt2mwFA+qzUn2nigJLqSekXRtdULKmT8am7zjvTMZSapIgHw==", "dev": true }, + "@types/glob": { + "version": "5.0.33", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.33.tgz", + "integrity": "sha512-BcD4yyWz+qmCggaYMSFF0Xn7GkO6tgwm3Fh9Gxk/kQmEU3Z7flQTnVlMyKBUNvXXNTCCyjqK4XT4/2hLd1gQ2A==", + "dev": true, + "requires": { + "@types/minimatch": "3.0.1", + "@types/node": "8.0.53" + } + }, "@types/long": { "version": "3.0.32", "resolved": "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz", "integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", + "dev": true + }, "@types/node": { - "version": "8.0.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.28.tgz", - "integrity": "sha512-HupkFXEv3O3KSzcr3Ylfajg0kaerBg1DyaZzRBBQfrU3NN1mTBRE7sCveqHwXLS5Yrjvww8qFzkzYQQakG9FuQ==" + "version": "8.0.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.53.tgz", + "integrity": "sha512-54Dm6NwYeiSQmRB1BLXKr5GELi0wFapR1npi8bnZhEcu84d/yQKqnwwXQ56hZ0RUbTG6L5nqDZaN3dgByQXQRQ==" }, "ansi-styles": { "version": "3.2.0", @@ -31,7 +47,7 @@ "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { - "color-convert": "1.9.0" + "color-convert": "1.9.1" } }, "arrify": { @@ -40,26 +56,42 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "binaryen": { "version": "39.0.0-nightly.20171116", "resolved": "https://registry.npmjs.org/binaryen/-/binaryen-39.0.0-nightly.20171116.tgz", "integrity": "sha512-ljl/qPne0+8hYtNWITRSAtxNM1EG5NnvTg+HRmSUdNAK2j9wcyAAg5uVj+TgipEqY82kmHt5C9+TSQNEwaxgrw==" }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", "dev": true, "requires": { "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" + "supports-color": "4.5.0" } }, "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz", + "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { "color-name": "1.1.3" @@ -71,10 +103,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", "dev": true }, "escape-string-regexp": { @@ -83,12 +121,48 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, "has-flag": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, "long": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", @@ -101,6 +175,15 @@ "integrity": "sha1-Uq06M5zPEM5itAQLcI/nByRLi5Y=", "dev": true }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, "minimist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", @@ -124,6 +207,21 @@ } } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -152,9 +250,9 @@ "dev": true }, "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { "has-flag": "2.0.0" @@ -167,8 +265,8 @@ "dev": true, "requires": { "arrify": "1.0.1", - "chalk": "2.1.0", - "diff": "3.3.1", + "chalk": "2.3.0", + "diff": "3.4.0", "make-error": "1.3.0", "minimist": "1.2.0", "mkdirp": "0.5.1", @@ -189,9 +287,9 @@ } }, "typescript": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.2.tgz", - "integrity": "sha1-A4qV99m7tCCxvzW6MdTFwd0//jQ=", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", + "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", "dev": true }, "user-home": { @@ -209,6 +307,12 @@ "user-home": "1.1.1" } }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", diff --git a/package.json b/package.json index 377dca49..f54ef959 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,20 @@ { "license": "Apache-2.0", + "dependencies": { + "@types/node": "^8.0.53", + "binaryen": "39.0.0-nightly.20171116" + }, "devDependencies": { "@types/chalk": "^0.4.31", "@types/diff": "^3.2.2", + "@types/glob": "^5.0.33", "@types/long": "^3.0.32", - "chalk": "^2.1.0", - "diff": "^3.3.1", + "chalk": "^2.3.0", + "diff": "^3.4.0", + "glob": "^7.1.2", "long": "^3.2.0", "ts-node": "^3.3.0", - "typescript": "^2.5.2" - }, - "dependencies": { - "@types/node": "^8.0.28", - "binaryen": "39.0.0-nightly.20171116" + "typescript": "^2.6.2" }, "scripts": { "build": "tsc -P src", diff --git a/src/ast.ts b/src/ast.ts index aebcadc3..f84e4975 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1257,7 +1257,6 @@ export class ExpressionStatement extends Statement { serialize(sb: string[]): void { this.expression.serialize(sb); - sb.push(";"); } } diff --git a/src/compiler.ts b/src/compiler.ts index e419caba..8d001157 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,6 +1,6 @@ import { PATH_DELIMITER } from "./constants"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; -import { Module, ModuleRef, MemorySegment, ExpressionRef, UnaryOp, BinaryOp, HostOp, NativeType, FunctionTypeRef, FunctionRef, ImportRef, ExportRef, GlobalRef, Relooper } from "./module"; +import { Module, MemorySegment, ExpressionRef, UnaryOp, BinaryOp, NativeType, FunctionTypeRef } from "./module"; import { Program, ClassPrototype, Class, Element, ElementKind, Enum, FunctionPrototype, Function, Global, Local, Namespace, Parameter } from "./program"; import { CharCode, I64, U64, normalizePath, sb } from "./util"; import { Token, Range } from "./tokenizer"; diff --git a/src/parser.ts b/src/parser.ts index 03472e0b..bb004949 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -212,7 +212,7 @@ export class Parser extends DiagnosticEmitter { return this.program; } - parseType(tn: Tokenizer, acceptParenthesized: bool = true): TypeNode | null { + parseType(tn: Tokenizer, acceptParenthesized: bool = true, suppressErrors: bool = false): TypeNode | null { // not TypeScript-compatible const token: Token = tn.next(); const startPos: i32 = tn.tokenPos; @@ -225,11 +225,12 @@ export class Parser extends DiagnosticEmitter { // ( ... ) if (acceptParenthesized && token == Token.OPENPAREN) { - const innerType: TypeNode | null = this.parseType(tn, false); + const innerType: TypeNode | null = this.parseType(tn, false, suppressErrors); if (!innerType) return null; if (!tn.skip(Token.CLOSEPAREN)) { - this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "}"); + if (!suppressErrors) + this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "}"); return null; } type = innerType; @@ -264,7 +265,8 @@ export class Parser extends DiagnosticEmitter { parameters.push(parameter); } while (tn.skip(Token.COMMA)); if (!tn.skip(Token.GREATERTHAN)) { - this.error(DiagnosticCode._0_expected, tn.range(tn.pos), ">"); + if (!suppressErrors) + this.error(DiagnosticCode._0_expected, tn.range(tn.pos), ">"); return null; } } @@ -273,21 +275,24 @@ export class Parser extends DiagnosticEmitter { if (tn.skip(Token.NULL)) { nullable = true; } else { - this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "null"); + if (!suppressErrors) + this.error(DiagnosticCode._0_expected, tn.range(tn.pos), "null"); return null; } } type = TypeNode.create(identifier, parameters, nullable, tn.range(startPos, tn.pos)); } else { - this.error(DiagnosticCode.Identifier_expected, tn.range()); + if (!suppressErrors) + this.error(DiagnosticCode.Identifier_expected, tn.range()); return null; } // ... [][] while (tn.skip(Token.OPENBRACKET)) { let bracketStart: i32 = tn.tokenPos; if (!tn.skip(Token.CLOSEBRACKET)) { - this.error(DiagnosticCode._0_expected, tn.range(), "]"); + if (!suppressErrors) + this.error(DiagnosticCode._0_expected, tn.range(), "]"); return null; } const bracketRange = tn.range(bracketStart, tn.pos); @@ -298,7 +303,8 @@ export class Parser extends DiagnosticEmitter { if (tn.skip(Token.NULL)) { nullable = true; } else { - this.error(DiagnosticCode._0_expected, tn.range(), "null"); + if (!suppressErrors) + this.error(DiagnosticCode._0_expected, tn.range(), "null"); return null; } } @@ -999,26 +1005,36 @@ export class Parser extends DiagnosticEmitter { // at 'for': '(' Statement? Expression? ';' Expression? ')' Statement const startPos: i32 = tn.tokenPos; if (tn.skip(Token.OPENPAREN)) { - const initializer: Statement | null = this.parseStatement(tn); // skips the semicolon (actually an expression) - if (!initializer) - return null; - if (initializer.kind != NodeKind.EXPRESSION && initializer.kind != NodeKind.VARIABLE) - this.error(DiagnosticCode.Expression_expected, initializer.range); // recoverable - if (tn.token == Token.SEMICOLON) { - const condition: Expression | null = this.parseExpression(tn); - if (!condition) + let initializer: Statement | null = null; + if (tn.skip(Token.LET) || tn.skip(Token.CONST) || tn.skip(Token.VAR)) { + initializer = this.parseVariable(tn, /* TODO */ createModifiers()); + } else if (!tn.skip(Token.SEMICOLON)) { + initializer = this.parseExpressionStatement(tn); + if (!initializer) return null; - if (tn.skip(Token.SEMICOLON)) { - const incrementor: Expression | null = this.parseExpression(tn); - if (!incrementor) + } + if (tn.token == Token.SEMICOLON) { + let condition: ExpressionStatement | null = null; + if (!tn.skip(Token.SEMICOLON)) { + condition = this.parseExpressionStatement(tn); + if (!condition) return null; - if (tn.skip(Token.CLOSEPAREN)) { - const statement: Statement | null = this.parseStatement(tn); - if (!statement) + } + if (tn.token == Token.SEMICOLON) { + let incrementor: Expression | null = null; + if (!tn.skip(Token.CLOSEPAREN)) { + incrementor = this.parseExpression(tn); + if (!incrementor) return null; - return Statement.createFor(initializer, condition, incrementor, statement, tn.range(startPos, tn.pos)); - } else - this.error(DiagnosticCode._0_expected, tn.range(), ")"); + if (!tn.skip(Token.CLOSEPAREN)) { + this.error(DiagnosticCode._0_expected, tn.range(), ")"); + return null; + } + } + const statement: Statement | null = this.parseStatement(tn); + if (!statement) + return null; + return Statement.createFor(initializer, condition ? condition.expression : null, incrementor, statement, tn.range(startPos, tn.pos)); } else this.error(DiagnosticCode._0_expected, tn.range(), ";"); } else @@ -1225,6 +1241,7 @@ export class Parser extends DiagnosticEmitter { parseExpressionPrefix(tn: Tokenizer): Expression | null { const token: Token = tn.next(); const startPos: i32 = tn.tokenPos; + let expr: Expression | null = null; if (token == Token.NULL) return Expression.createNull(tn.range()); @@ -1248,10 +1265,10 @@ export class Parser extends DiagnosticEmitter { if (tn.skip(Token.OPENPAREN)) { if (tn.peek() != Token.CLOSEPAREN) { do { - const expr: Expression | null = this.parseExpression(tn, Precedence.COMMA + 1); + expr = this.parseExpression(tn, Precedence.COMMA + 1); if (!expr) return null; - args.push(expr); + args.push(expr); } while (tn.skip(Token.COMMA)); } if (!tn.skip(Token.CLOSEPAREN)) { @@ -1277,7 +1294,7 @@ export class Parser extends DiagnosticEmitter { // ParenthesizedExpression case Token.OPENPAREN: { - const expr: Expression | null = this.parseExpression(tn); + expr = this.parseExpression(tn); if (!expr) return null; if (!tn.skip(Token.CLOSEPAREN)) { @@ -1292,7 +1309,6 @@ export class Parser extends DiagnosticEmitter { const elementExpressions: (Expression | null)[] = new Array(); if (!tn.skip(Token.CLOSEBRACKET)) { do { - let expr: Expression | null; if (tn.peek() == Token.COMMA || tn.peek() == Token.CLOSEBRACKET) expr = null; // omitted else { @@ -1319,7 +1335,7 @@ export class Parser extends DiagnosticEmitter { this.error(DiagnosticCode._0_expected, tn.range(), ">"); return null; } - const expr: Expression | null = this.parseExpressionPrefix(tn); + expr = this.parseExpressionPrefix(tn); if (!expr) return null; return Expression.createAssertion(AssertionKind.PREFIX, expr, toType, tn.range(startPos, tn.pos)); @@ -1359,7 +1375,7 @@ export class Parser extends DiagnosticEmitter { const typeArguments: TypeNode[] = []; do { - const type: TypeNode | null = this.parseType(tn); + const type: TypeNode | null = this.parseType(tn, true, true); if (!type) { tn.reset(); return null; diff --git a/tests/parser/fixtures/do.ts b/tests/parser/fixtures/do.ts new file mode 100644 index 00000000..f7c1dc71 --- /dev/null +++ b/tests/parser/fixtures/do.ts @@ -0,0 +1,3 @@ +do { + ; +} while (a != b); diff --git a/tests/parser/fixtures/for.ts b/tests/parser/fixtures/for.ts new file mode 100644 index 00000000..79906e75 --- /dev/null +++ b/tests/parser/fixtures/for.ts @@ -0,0 +1,9 @@ +for (let i: i32 = 0; i < 10; ++i) { + ; +} +for (i = 0; i < 10; ++i) { + ; +} +for (;;) { + ; +} diff --git a/tests/parser/fixtures/literals.ts b/tests/parser/fixtures/literals.ts new file mode 100644 index 00000000..ef9f9232 --- /dev/null +++ b/tests/parser/fixtures/literals.ts @@ -0,0 +1,42 @@ +0; +1; +2; +3; +4; +5; +6; +7; +8; +9; +// 0x0; +// 0x1; +// 0x2; +// 0x3; +// 0x4; +// 0x5; +// 0x6; +// 0x7; +// 0x8; +// 0x9; +// 0xA; +// 0xB; +// 0xC; +// 0xD; +// 0xE; +// 0xF; +// 0xa; +// 0xb; +// 0xc; +// 0xd; +// 0xe; +// 0xf; +// 0o0; +// 0o1; +// 0o2; +// 0o3; +// 0o4; +// 0o5; +// 0o6; +// 0o7; +// 0b0; +// 0b1; diff --git a/tests/parser/fixtures/while.ts b/tests/parser/fixtures/while.ts new file mode 100644 index 00000000..1166ab96 --- /dev/null +++ b/tests/parser/fixtures/while.ts @@ -0,0 +1,9 @@ +while (1) { + ; +} +while (true) { + ; +} +while ("str") { + ; +} diff --git a/tests/parser/index.ts b/tests/parser/index.ts index 99d9fb90..6cddb19b 100644 --- a/tests/parser/index.ts +++ b/tests/parser/index.ts @@ -1,12 +1,15 @@ import * as fs from "fs"; import * as diff from "diff"; import * as chalk from "chalk"; +import * as glob from "glob"; import "../../src/glue/js"; import { NodeKind, ExpressionStatement } from "../../src/ast"; import { Parser } from "../../src/parser"; -const files = fs.readdirSync(__dirname + "/fixtures"); +const filter = process.argv.length > 2 ? "*" + process.argv[2] + "*.ts" : "**.ts"; + +const files = glob.sync(filter, { cwd: __dirname + "/fixtures" }); files.forEach(filename => { if (filename.charAt(0) == "_") return; const isTree = filename.indexOf(".tree.") >= 0;