Skip to content

Commit c848389

Browse files
committed
Change parse to return an error instead of throwing.
1 parent 3e177a2 commit c848389

3 files changed

Lines changed: 54 additions & 27 deletions

File tree

server/src/parseDataProvider.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,16 @@ export class ParseDataProvider {
5454
//
5555
// Returns |ParseError| on failing to parse the file contents.
5656
updateParseDataWithContent(uri: vscodeUri.URI, content: string): Maybe<ParseData> {
57-
try {
58-
const parseData = parse(content, uri.toString(), this.parseOptions);
59-
this.data.set(uri.toString(), {
60-
isStale: false,
61-
data: parseData,
62-
fileContent: content,
63-
});
64-
return Maybe.ok(parseData);
65-
} catch (error) {
57+
const maybeParseData = parse(content, uri.toString(), this.parseOptions);
58+
if (maybeParseData.hasError) {
6659
const existingData = this.data.get(uri.toString());
6760
if (existingData !== undefined) {
6861
// Mark the existing data as stale instead of deleting it, so that we can still do
6962
// useful things like auto-completion while in the middle of editing text.
7063
existingData.isStale = true;
7164
}
65+
66+
const error = maybeParseData.getError();
7267
if (error instanceof Error) {
7368
return Maybe.error(error);
7469
} else {
@@ -77,6 +72,14 @@ export class ParseDataProvider {
7772
const typedError = new Error(String(error));
7873
return Maybe.error(typedError);
7974
}
75+
} else {
76+
const parseData = maybeParseData.getValue();
77+
this.data.set(uri.toString(), {
78+
isStale: false,
79+
data: parseData,
80+
fileContent: content,
81+
});
82+
return Maybe.ok(parseData);
8083
}
8184
}
8285

server/src/parser.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as nearley from 'nearley';
2+
import { Maybe } from './coreTypes';
23
import fbuildGrammar from './fbuild-grammar';
34
import { UriStr } from './parseDataProvider';
45

@@ -304,8 +305,8 @@ function createParseErrorFromNearlyParseError(
304305

305306
// Parse the input and return the statements.
306307
//
307-
// Throws |ParseError| on a parse error, or |Error| on unknown errors.
308-
export function parse(input: string, fileUri: UriStr, options: ParseOptions): ParseData {
308+
// Returns |ParseError| on a parse error, or |Error| on unknown errors.
309+
export function parse(input: string, fileUri: UriStr, options: ParseOptions): Maybe<ParseData> {
309310
// Pre-process the input:
310311
// * Remove comments.
311312
// * Add a newline in order to make parsing easier.
@@ -330,11 +331,16 @@ export function parse(input: string, fileUri: UriStr, options: ParseOptions): Pa
330331
}
331332

332333
if (nearlyParseError instanceof Error) {
333-
throw createParseErrorFromNearlyParseError(nearlyParseError, modifiedInput, fileUri, options.includeCodeLocationInError);
334+
const parseError = createParseErrorFromNearlyParseError(
335+
nearlyParseError,
336+
modifiedInput,
337+
fileUri,
338+
options.includeCodeLocationInError);
339+
return Maybe.error(parseError);
334340
} else {
335341
// We should only throw `Error` instances, but handle other types as a fallback.
336342
// `error` could be anything. Try to get a useful message out of it.
337-
throw new Error(String(nearlyParseError));
343+
return Maybe.error(new Error(String(nearlyParseError)));
338344
}
339345
}
340346

@@ -343,12 +349,13 @@ export function parse(input: string, fileUri: UriStr, options: ParseOptions): Pa
343349
if (options.enableDiagnostics) {
344350
console.log(getParseTable(parser));
345351
}
346-
throw new ParseNumParsesError(`Should parse to exactly 1 result, but parsed to ${numResults}`, fileUri);
352+
return Maybe.error(new ParseNumParsesError(`Should parse to exactly 1 result, but parsed to ${numResults}`, fileUri));
347353
}
348354
const statements = parser.results[0];
349-
return {
355+
const result: ParseData = {
350356
statements
351357
};
358+
return Maybe.ok(result);
352359
}
353360

354361
// TODO: make this more efficient by moving it into the grammar.

server/src/test/1-parser.test.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,36 @@ import {
55
createRange,
66
isPositionInRange,
77
parse,
8+
ParseError,
89
ParseSourceRange,
910
} from '../parser';
1011

1112
function assertParseResultsEqual(input: string, expectedResult: any[]): void {
12-
const result = parse(input, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
13+
const maybeResult = parse(input, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
14+
if (maybeResult.hasError) {
15+
const error = maybeResult.getError();
16+
throw error;
17+
}
18+
const result = maybeResult.getValue();
1319
assert.deepStrictEqual(result.statements, expectedResult);
1420
}
1521

1622
function assertInputsGenerateSameParseResult(input1: string, input2: string): void {
17-
const result1 = parse(input1, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
18-
const result2 = parse(input2, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
23+
const maybeResult1 = parse(input1, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
24+
const maybeResult2 = parse(input2, 'file:///dummy.bff', { enableDiagnostics: true, includeCodeLocationInError: true} );
25+
26+
if (maybeResult1.hasError) {
27+
const error = maybeResult1.getError();
28+
throw error;
29+
}
30+
const result1 = maybeResult1.getValue();
31+
32+
if (maybeResult2.hasError) {
33+
const error = maybeResult2.getError();
34+
throw error;
35+
}
36+
const result2 = maybeResult2.getValue();
37+
1938
assert.deepStrictEqual(result1.statements, result2.statements);
2039
}
2140

@@ -24,15 +43,13 @@ function getParseSourceRangeString(range: ParseSourceRange): string {
2443
}
2544

2645
function assertParseSyntaxError(input: string, expectedErrorMessage: string, expectedRange: ParseSourceRange): void {
27-
assert.throws(
28-
() => parse(input, 'file:///dummy.bff', { enableDiagnostics: false, includeCodeLocationInError: true } ),
29-
actualError => {
30-
assert.strictEqual(actualError.name, 'ParseSyntaxError', `Expected a ParseSyntaxError exception but got ${actualError}:\n\n${actualError.stack}`);
31-
assert(actualError.message === expectedErrorMessage, `Got error message <${actualError.message}> but expected <${expectedErrorMessage}>`);
32-
assert.deepStrictEqual(actualError.range, expectedRange, `Expected the error range to be ${getParseSourceRangeString(expectedRange)} but it is ${getParseSourceRangeString(actualError.range)}`);
33-
return true;
34-
}
35-
);
46+
const maybeResult = parse(input, 'file:///dummy.bff', { enableDiagnostics: false, includeCodeLocationInError: true } );
47+
assert.strictEqual(maybeResult.hasError, true);
48+
const actualError = maybeResult.getError();
49+
assert.strictEqual(actualError.name, 'ParseSyntaxError', `Expected a ParseSyntaxError exception but got ${actualError}:\n\n${actualError.stack}`);
50+
const actualParseError = actualError as ParseError;
51+
assert(actualParseError.message === expectedErrorMessage, `Got error message <${actualParseError.message}> but expected <${expectedErrorMessage}>`);
52+
assert.deepStrictEqual(actualParseError.range, expectedRange, `Expected the error range to be ${getParseSourceRangeString(expectedRange)} but it is ${getParseSourceRangeString(actualParseError.range)}`);
3653
}
3754

3855
describe('parser', () => {

0 commit comments

Comments
 (0)