Skip to content

Commit

Permalink
Support for cancellation.
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Aug 25, 2016
1 parent 8b8b8d6 commit 003802c
Show file tree
Hide file tree
Showing 11 changed files with 98 additions and 20 deletions.
6 changes: 5 additions & 1 deletion src/lib/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CancellationToken } from "prex";
import { Dictionary } from "./core";
import { SyntaxKind } from "./tokens";
import { Symbol, SymbolKind, SymbolTable } from "./symbols";
Expand Down Expand Up @@ -194,13 +195,16 @@ export class Binder {
private parentSymbol: Symbol;
private bindings: BindingTable;
private scope: SymbolTable;
private cancellationToken: CancellationToken;

constructor(bindings: BindingTable) {
constructor(bindings: BindingTable, cancellationToken = CancellationToken.none) {
this.bindings = bindings;
this.scope = bindings.globals;
this.cancellationToken = cancellationToken;
}

public bindSourceFile(file: SourceFile): void {
this.cancellationToken.throwIfCancellationRequested();
if (this.scope.resolveSymbol(file.filename, SymbolKind.SourceFile)) {
// skip files that have already been bound.
return;
Expand Down
15 changes: 9 additions & 6 deletions src/lib/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/
import { Hash, createHash } from "crypto";
import { CancellationToken } from "prex";
import { Dictionary } from "./core";
import { Diagnostics, DiagnosticMessages, Diagnostic, formatList } from "./diagnostics";
import { SyntaxKind, tokenToString } from "./tokens";
Expand Down Expand Up @@ -73,11 +74,13 @@ export class Checker {
private sourceFile: SourceFile;
private noStrictParametricProductions: boolean;
private productionParametersByName: Dictionary<Dictionary<boolean>>;
private cancellationToken: CancellationToken;

constructor(bindings: BindingTable, diagnostics: DiagnosticMessages, options?: CompilerOptions) {
constructor(bindings: BindingTable, diagnostics: DiagnosticMessages, options?: CompilerOptions, cancellationToken = CancellationToken.none) {
this.bindings = bindings;
this.diagnostics = diagnostics;
this.noStrictParametricProductions = options && options.noStrictParametricProductions || false;
this.cancellationToken = cancellationToken;
}

public get resolver(): Resolver {
Expand All @@ -90,13 +93,12 @@ export class Checker {

public checkSourceFile(sourceFile: SourceFile): void {
if (!Dictionary.has(this.checkedFileSet, sourceFile.filename)) {
Dictionary.set(this.checkedFileSet, sourceFile.filename, true);
this.sourceFile = sourceFile;
const savedNoStrictParametricProductions = this.noStrictParametricProductions;
this.cancellationToken.throwIfCancellationRequested();
this.productionParametersByName = new Dictionary<Dictionary<boolean>>();
this.sourceFile = sourceFile;
this.diagnostics.setSourceFile(this.sourceFile);

const savedNoStrictParametricProductions = this.noStrictParametricProductions;

for (const element of sourceFile.elements) {
this.preprocessSourceElement(element);
}
Expand All @@ -105,9 +107,10 @@ export class Checker {
this.checkSourceElement(element);
}

this.noStrictParametricProductions = savedNoStrictParametricProductions;
this.sourceFile = undefined;
this.productionParametersByName = undefined;
this.noStrictParametricProductions = savedNoStrictParametricProductions;
Dictionary.set(this.checkedFileSet, sourceFile.filename, true);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/lib/emitter/emitter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as fs from "fs";
import * as path from "path";
import { CancellationToken } from "prex";
import { DiagnosticMessages } from "../diagnostics";
import { CompilerOptions } from "../options";
import { Checker, Resolver } from "../checker";
Expand Down Expand Up @@ -53,14 +54,17 @@ export class Emitter {
private diagnostics: DiagnosticMessages;
private sourceFile: SourceFile;
private triviaPos: number;
private cancellationToken: CancellationToken;

constructor(options: CompilerOptions, resolver: Resolver, diagnostics: DiagnosticMessages) {
constructor(options: CompilerOptions, resolver: Resolver, diagnostics: DiagnosticMessages, cancellationToken = CancellationToken.none) {
this.options = options;
this.resolver = resolver;
this.diagnostics = diagnostics;
this.cancellationToken = cancellationToken;
}

public emit(node: SourceFile, writeFile?: (file: string, text: string) => void): void {
this.cancellationToken.throwIfCancellationRequested();
const saveWriter = this.writer;
const saveSourceFile = this.sourceFile;
const saveTriviaPos = this.triviaPos;
Expand Down
25 changes: 16 additions & 9 deletions src/lib/grammar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { Binder, BindingTable } from "./binder";
import { Checker, Resolver } from "./checker";
import { Emitter, EcmarkupEmitter, MarkdownEmitter, HtmlEmitter } from "./emitter/index";
import { SourceFile, Import } from "./nodes";
import { CancellationToken } from "prex";

export class Grammar {
public rootFiles: SourceFile[] = [];
public sourceFiles: SourceFile[] = [];
public options: CompilerOptions;
public diagnostics: DiagnosticMessages = new DiagnosticMessages();
public readonly cancellationToken: CancellationToken;

private static knownGrammars = new Dictionary<string>({
"es6": require.resolve("../../grammars/es2015.grammar"),
Expand All @@ -39,12 +41,15 @@ export class Grammar {

constructor(rootNames: string[]);
constructor(rootNames: string[], options: CompilerOptions);
constructor(rootNames: string[], options: CompilerOptions, host: Host, oldGrammar?: Grammar);
constructor(rootNames: string[], options: CompilerOptions, host: Host, oldGrammar?: Grammar, token?: CancellationToken);
/*@obsolete*/
/*@internal*/
constructor(rootNames: string[], options: CompilerOptions, readFile: (file: string) => string, oldGrammar?: Grammar);
constructor(rootNames: string[], options: CompilerOptions = getDefaultOptions(), readFileOrHostLike?: ((file: string) => string) | HostLike, oldGrammar?: Grammar) {
constructor(rootNames: string[], options: CompilerOptions = getDefaultOptions(), readFileOrHostLike?: ((file: string) => string) | HostLike, oldGrammar?: Grammar, token = CancellationToken.none) {
this.host = Host.getHost(readFileOrHostLike);
this.options = options;
this.oldGrammar = oldGrammar;
this.cancellationToken = token;
this.parse(rootNames);
this.oldGrammar = undefined;
Object.freeze(this.sourceFiles);
Expand Down Expand Up @@ -106,7 +111,9 @@ export class Grammar {
performance.measure("parse", "beforeParse", "afterParse");
}

/* @obsolete */ /* @internal */ bind(sourceFile: SourceFile): void;
/*@obsolete*/
/*@internal*/
bind(sourceFile: SourceFile): void;

public bind(): void;
public bind(sourceFile?: SourceFile) {
Expand Down Expand Up @@ -172,28 +179,28 @@ export class Grammar {
}

protected createParser(options: CompilerOptions): Parser {
return new Parser(this.diagnostics);
return new Parser(this.diagnostics, this.cancellationToken);
}

protected createBinder(options: CompilerOptions, bindings: BindingTable): Binder {
return new Binder(bindings);
return new Binder(bindings, this.cancellationToken);
}

protected createChecker(options: CompilerOptions, bindings: BindingTable): Checker {
return new Checker(bindings, options.noChecks ? NullDiagnosticMessages.instance : this.diagnostics);
return new Checker(bindings, options.noChecks ? NullDiagnosticMessages.instance : this.diagnostics, options, this.cancellationToken);
}

protected createEmitter(options: CompilerOptions, resolver: Resolver): Emitter {
switch (options.format) {
case EmitFormat.ecmarkup:
return new EcmarkupEmitter(options, resolver, this.diagnostics)
return new EcmarkupEmitter(options, resolver, this.diagnostics, this.cancellationToken)

case EmitFormat.html:
return new HtmlEmitter(options, resolver, this.diagnostics);
return new HtmlEmitter(options, resolver, this.diagnostics, this.cancellationToken);

case EmitFormat.markdown:
default:
return new MarkdownEmitter(options, resolver, this.diagnostics);
return new MarkdownEmitter(options, resolver, this.diagnostics, this.cancellationToken);
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/lib/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,16 @@ export namespace Host {

export function getHost(): Host;
export function getHost(hostLike: HostLike): Host;
/*@obsolete*/
/*@internal*/
export function getHost(readFile: (file: string) => string): Host;
/*@obsolete*/
/*@internal*/
export function getHost(readFileOrHostLike: ((file: string) => string) | HostLike): Host;
export function getHost(readFileOrHostLike?: ((file: string) => string) | HostLike): Host {
const host = getDefaultHost();
if (typeof readFileOrHostLike === "function") {
console.warn(`Calling Host.getHost with a readFile function is an obsolete overload and will be removed in a future version.`);
host.readFile = <(file: string) => string>readFileOrHostLike;
}
else if (typeof readFileOrHostLike === "object") {
Expand Down
8 changes: 6 additions & 2 deletions src/lib/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Range, Position, TextRange } from "./core";
import { Diagnostics, DiagnosticMessages, NullDiagnosticMessages, LineMap, formatList } from "./diagnostics";
import { SyntaxKind, tokenToString } from "./tokens";
import { Scanner } from "./scanner";
import { CancellationToken } from "prex";
import {
Node,
StringLiteral,
Expand Down Expand Up @@ -115,9 +116,11 @@ export class Parser {
private diagnostics: DiagnosticMessages;
private parsingContext: ParsingContext;
private previousSourceFile: SourceFile;
private cancellationToken: CancellationToken;

constructor(diagnostics: DiagnosticMessages) {
constructor(diagnostics: DiagnosticMessages, cancellationToken = CancellationToken.none) {
this.diagnostics = diagnostics;
this.cancellationToken = cancellationToken;
}

// TODO(rbuckton): Incremental parser
Expand Down Expand Up @@ -155,7 +158,7 @@ export class Parser {
private parse(filename: string, text: string, previousSourceFile: SourceFile, changeRange: TextRange) {
this.sourceFile = new SourceFile(filename, text);
this.diagnostics.setSourceFile(this.sourceFile);
this.scanner = new Scanner(filename, text, this.diagnostics);
this.scanner = new Scanner(filename, text, this.diagnostics, this.cancellationToken);
this.parsingContext = ParsingContext.SourceElements;

this.nextToken();
Expand Down Expand Up @@ -649,6 +652,7 @@ export class Parser {
const whitespaceToSkip = this.shouldSkipWhitespace();
let result: TNode[];
while (!this.isEOF()) {
this.cancellationToken.throwIfCancellationRequested();
this.skipWhitespace(whitespaceToSkip);

let parsed = false;
Expand Down
6 changes: 5 additions & 1 deletion src/lib/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

import { EOL } from 'os';
import { CancellationToken } from "prex";
import { TextRange } from "./core";
import { CharacterCodes, SyntaxKind, stringToToken } from "./tokens";
import { Diagnostics, Diagnostic, DiagnosticMessages, NullDiagnosticMessages } from "./diagnostics";
Expand All @@ -35,12 +36,14 @@ export class Scanner {
private filename: string;
private diagnostics: DiagnosticMessages;
private proseStartToken: SyntaxKind;
private cancellationToken: CancellationToken;

constructor(filename: string, text: string, diagnostics: DiagnosticMessages) {
constructor(filename: string, text: string, diagnostics: DiagnosticMessages, cancellationToken = CancellationToken.none) {
this.filename = filename;
this.text = text;
this.len = text.length;
this.diagnostics = diagnostics;
this.cancellationToken = cancellationToken;
}

public getPos(): number {
Expand Down Expand Up @@ -76,6 +79,7 @@ export class Scanner {
}

public scan(): SyntaxKind {
this.cancellationToken.throwIfCancellationRequested();
const token = this.dequeueOrScanToken();
if (token === SyntaxKind.EndOfFileToken && this.indents.length) {
for (let i = 0; i < this.indents.length; i++) {
Expand Down
12 changes: 12 additions & 0 deletions src/tests/checker-tests.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import { basename } from "path";
import { Grammar } from "../lib/grammar";
import { Host } from "../lib/host";
import { EmitFormat } from "../lib/options";
import { getGrammarFiles } from "./resources";
import { writeTokens, writeDiagnostics, writeOutput, compareBaseline } from "./diff";
import { CancellationTokenSource } from "prex";
import { assert } from "chai";

describe("Checker", () => {
defineTests();

it("cancelable", () => {
const cts = new CancellationTokenSource();
const grammar = new Grammar(["cancelable.grammar"], {}, Host.getHost({
readFile(file) { return ""; }
}), /*oldGrammar*/ undefined, cts.token);
cts.cancel();
assert.throws(() => grammar.check(/*sourceFile*/ undefined));
});

function defineTests() {
for (let file of getGrammarFiles()) {
defineTest(basename(file), file);
Expand Down
14 changes: 14 additions & 0 deletions src/tests/emitter-tests.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { basename } from "path";
import { Grammar } from "../lib/grammar";
import { Host } from "../lib/host";
import { EmitFormat } from "../lib/options";
import { getGrammarFiles } from "./resources";
import { writeTokens, writeDiagnostics, writeOutput, compareBaseline } from "./diff";
import { CancellationTokenSource } from "prex";
import { assert } from "chai";

describe("Emitter", () => {
defineTests();

it("cancelable", () => {
const cts = new CancellationTokenSource();
const grammar = new Grammar(["cancelable.grammar"], {}, Host.getHost({
readFile(file) { return ""; },
writeFile(file, content) { }
}), /*oldGrammar*/ undefined, cts.token);
grammar.check(/*sourceFile*/ undefined);
cts.cancel();
assert.throws(() => grammar.emit(/*sourceFile*/ undefined));
});

function defineTests() {
for (const file of getGrammarFiles()) {
defineTest(basename(file), file, ".md", EmitFormat.markdown);
Expand Down
9 changes: 9 additions & 0 deletions src/tests/parser-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ import { SourceFile } from "../lib/nodes";
import { Parser } from "../lib/parser";
import { getGrammarFiles } from "./resources";
import { writeNodes, writeDiagnostics, compareBaseline } from "./diff";
import { CancellationTokenSource } from "prex";
import { assert } from "chai";

describe("Parser", () => {
defineTests();

it("cancelable", () => {
const cts = new CancellationTokenSource();
const parser = new Parser(new DiagnosticMessages(), cts.token);
cts.cancel();
assert.throws(() => parser.parseSourceFile("cancelable.grammar", ""));
});

function defineTests() {
for (let file of getGrammarFiles()) {
defineTest(basename(file), file);
Expand Down
12 changes: 12 additions & 0 deletions src/tests/scanner-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ import { SourceFile } from "../lib/nodes";
import { Scanner } from "../lib/scanner";
import { getGrammarFiles } from "./resources";
import { writeTokens, writeDiagnostics, compareBaseline } from "./diff";
import { CancellationTokenSource } from "prex";
import { assert } from "chai";

describe("Scanner", () => {
defineTests();

it("cancelable", () => {
const sourceFile = new SourceFile("cancelable.grammar", "");
const diagnostics = new DiagnosticMessages();
diagnostics.setSourceFile(sourceFile);
const cts = new CancellationTokenSource();
const scanner = new Scanner(sourceFile.filename, sourceFile.text, diagnostics, cts.token);
cts.cancel();
assert.throws(() => scanner.scan());
});

function defineTests() {
for (let file of getGrammarFiles()) {
defineTest(basename(file), file);
Expand Down

0 comments on commit 003802c

Please sign in to comment.