Skip to content

Commit

Permalink
feat(lex): Emit try/catch/throw lexemes for parsing
Browse files Browse the repository at this point in the history
RBI 9.4 adds support for error handling via the `try`/`catch` model [1].
Interestingly, `try`, `catch`, and `endtry` are all valid identifiers!
That makes parsing a little bit tougher, but it's not unprecedented in
this language.  Detect `try`/`catch`/throw`/`end try` during lexing and
emit the proper lexemes.

[1] https://developer.roku.com/docs/references/brightscript/language/error-handling.html
see #554
  • Loading branch information
sjbarag committed Jan 14, 2021
1 parent 9800c2e commit 6748221
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 5 deletions.
4 changes: 4 additions & 0 deletions src/lexer/Lexeme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export enum Lexeme {
// canonical source: https://sdkdocs.roku.com/display/sdkdoc/Reserved+Words
And = "And",
Box = "Box",
Catch = "Catch",
CreateObject = "CreateObject",
Dim = "Dim",
Else = "Else",
Expand All @@ -77,6 +78,7 @@ export enum Lexeme {
EndFor = "EndFor",
EndIf = "EndIf",
EndSub = "EndSub",
EndTry = "EndTry",
EndWhile = "EndWhile",
Eval = "Eval",
Exit = "Exit",
Expand Down Expand Up @@ -105,8 +107,10 @@ export enum Lexeme {
Stop = "Stop",
Sub = "Sub",
Tab = "Tab",
Throw = "Throw",
To = "To",
True = "True",
Try = "Try",
Type = "Type",
While = "While",

Expand Down
7 changes: 5 additions & 2 deletions src/lexer/ReservedWords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Lexeme as L } from "./Lexeme";

/**
* The set of all reserved words in the reference BrightScript runtime. These can't be used for any
* other purpose within a BrightScript file.
* other purpose (e.g. as identifiers) within a BrightScript file.
* @see https://sdkdocs.roku.com/display/sdkdoc/Reserved+Words
*/
export const ReservedWords = new Set([
Expand Down Expand Up @@ -44,14 +44,15 @@ export const ReservedWords = new Set([
"tab",
"then",
"to",
"throw",
"true",
"type",
"while",
]);

/**
* The set of keywords in the reference BrightScript runtime. Any of these that *are not* reserved
* words can be used within a BrightScript file for other purposes, e.g. `tab`.
* words can be used within a BrightScript file for other purposes as identifiers, e.g. `tab`.
*
* Unfortunately there's no canonical source for this!
*/
Expand All @@ -69,6 +70,7 @@ export const KeyWords: { [key: string]: L } = {
"end if": L.EndIf,
endsub: L.EndSub,
"end sub": L.EndSub,
"end try": L.EndTry, // note: 'endtry' (no space) is *not* a keyword
endwhile: L.EndWhile,
"end while": L.EndWhile,
exit: L.Exit,
Expand All @@ -94,6 +96,7 @@ export const KeyWords: { [key: string]: L } = {
stop: L.Stop,
sub: L.Sub,
to: L.To,
throw: L.Throw,
true: L.True,
while: L.While,
};
22 changes: 19 additions & 3 deletions test/lexer/Lexer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,8 @@ describe("lexer", () => {
describe("identifiers", () => {
it("matches single-word keywords", () => {
// test just a sample of single-word reserved words for now.
// if we find any that we've missed
let { tokens } = Lexer.scan("and or if else endif return true false line_num");
// if we find any that we've missed, add them here
let { tokens } = Lexer.scan("and or if else endif return true false line_num throw");
expect(tokens.map((w) => w.kind)).toEqual([
Lexeme.And,
Lexeme.Or,
Expand All @@ -260,20 +260,24 @@ describe("lexer", () => {
Lexeme.True,
Lexeme.False,
Lexeme.Identifier,
Lexeme.Throw,
Lexeme.Eof,
]);
expect(tokens.filter((w) => !!w.literal).length).toBe(0);
});

it("matches multi-word keywords", () => {
let { tokens } = Lexer.scan("else if end if end while End Sub end Function Exit wHILe");
let { tokens } = Lexer.scan(
"else if end if end while End Sub end Function Exit wHILe end try"
);
expect(tokens.map((w) => w.kind)).toEqual([
Lexeme.ElseIf,
Lexeme.EndIf,
Lexeme.EndWhile,
Lexeme.EndSub,
Lexeme.EndFunction,
Lexeme.ExitWhile,
Lexeme.EndTry,
Lexeme.Eof,
]);
expect(tokens.filter((w) => !!w.literal).length).toBe(0);
Expand All @@ -288,6 +292,18 @@ describe("lexer", () => {
]);
});

it("reads try/catch/throw properly", () => {
let { tokens } = Lexer.scan("try catch throw end try endtry");
expect(tokens.map((w) => w.kind)).toEqual([
Lexeme.Identifier, // try
Lexeme.Identifier, // catch
Lexeme.Throw, // throw
Lexeme.EndTry, // end try
Lexeme.Identifier, // endtry
Lexeme.Eof,
]);
});

it("matches keywords with silly capitalization", () => {
let { tokens } = Lexer.scan("iF ELSE eNDIf FUncTioN");
expect(tokens.map((w) => w.kind)).toEqual([
Expand Down

0 comments on commit 6748221

Please sign in to comment.