Skip to content

Commit

Permalink
fix(lex,parse,e2e): Allow 'then' as a property
Browse files Browse the repository at this point in the history
The `then` keyword isn't _actually_ a keyword, in that it can be used as
a valid identifier.  But it's still reserved as far as I can tell?

fixes #182
  • Loading branch information
sjbarag committed Mar 1, 2019
1 parent e9c7701 commit 11789b2
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 32 deletions.
1 change: 0 additions & 1 deletion src/lexer/Lexeme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ export enum Lexeme {
Step,
Sub,
Tab,
Then,
To,
True,
Type,
Expand Down
1 change: 0 additions & 1 deletion src/lexer/ReservedWords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ export const KeyWords: {[key: string]: L} = {
return: L.Return,
step: L.Step,
sub: L.Sub,
then: L.Then,
to: L.To,
true: L.True,
while: L.While,
Expand Down
17 changes: 14 additions & 3 deletions src/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,18 @@ export class Parser {
let elseIfTokens: Token[] = [];
let endIf: Token | undefined;

if (check(Lexeme.Then)) {
/**
* A simple wrapper around `check`, to make tests for a `then` identifier.
* As with many other words, "then" is a keyword but not reserved, so associative
* arrays can have properties called "then". It's a valid identifier sometimes, so the
* parser has to take on the burden of understanding that I guess.
* @returns `true` if the next token is an identifier with text "then", otherwise `false`.
*/
function checkThen() {
return check(Lexeme.Identifier) && peek().text === "then";
}

if (checkThen()) {
// `then` is optional after `if ...condition...`, so only advance to the next token if `then` is present
then = advance();
}
Expand Down Expand Up @@ -510,7 +521,7 @@ export class Parser {
while (check(Lexeme.ElseIf)) {
elseIfTokens.push(advance());
let elseIfCondition = expression();
if (check(Lexeme.Then)) {
if (checkThen()) {
// `then` is optional after `else if ...condition...`, so only advance to the next token if `then` is present
advance();
}
Expand Down Expand Up @@ -558,7 +569,7 @@ export class Parser {
while(match(Lexeme.ElseIf)) {
let elseIf = previous();
let elseIfCondition = expression();
if (check(Lexeme.Then)) {
if (checkThen()) {
// `then` is optional after `else if ...condition...`, so only advance to the next token if `then` is present
advance();
}
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/Syntax.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ describe("end to end syntax", () => {
allArgs(outputStreams.stdout.write).filter(arg => arg !== "\n")
).toEqual([
// note: associative array keys are sorted before iteration
"createobject", "in", "run", "stop"
"createobject", "in", "run", "stop", "then",
"promise-like resolved to 'foo'"
]);
});
});
Expand Down
23 changes: 21 additions & 2 deletions test/e2e/resources/reserved-words.brs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,29 @@ sub main()
createObject: true,
in: true,
stop: true,
run: true
run: true,
then: "useful for promises!"
}

for each word in hasReservedWords
print word
end for
end sub

immediatePromise("foo").then(sub(result)
print "promise-like resolved to '" + result + "'"
end sub)
end sub

' A simple promise-like function that immediately resolves to the provided value.
' You probably don't want to use it in production.
' @param {*} val the value this promise-like should immediately resolve to
' @returns {AssociativeArray} an associative array contianing a `then` property, used to chain
' promise-like constructs.
function immediatePromise(val as dynamic) as object
return {
__result: val
then: sub(resolved as function)
resolved(m.__result)
end sub
}
end function
8 changes: 4 additions & 4 deletions test/interpreter/If.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe("interpreter if statements", () => {
new Stmt.If(
{
if: token(Lexeme.If, "if"),
then: token(Lexeme.Then, "then"),
then: identifier("then"),
endIf: token(Lexeme.EndIf, "end if")
},
new Expr.Binary(
Expand All @@ -85,7 +85,7 @@ describe("interpreter if statements", () => {
new Stmt.If(
{
if: token(Lexeme.If, "if"),
then: token(Lexeme.Then, "then"),
then: identifier("then"),
endIf: token(Lexeme.EndIf, "end if")
},
new Expr.Binary(
Expand Down Expand Up @@ -119,7 +119,7 @@ describe("interpreter if statements", () => {
new Stmt.If(
{
if: token(Lexeme.If, "if"),
then: token(Lexeme.Then, "then"),
then: identifier("then"),
elseIfs: [ token(Lexeme.ElseIf, "else if"), token(Lexeme.ElseIf, "else if") ],
else: token(Lexeme.Else, "else"),
endIf: token(Lexeme.EndIf, "end if")
Expand Down Expand Up @@ -167,7 +167,7 @@ describe("interpreter if statements", () => {
new Stmt.If(
{
if: token(Lexeme.If, "if"),
then: token(Lexeme.Then, "then"),
then: identifier("then"),
elseIfs: [ token(Lexeme.ElseIf, "else if"), token(Lexeme.ElseIf, "else if") ],
else: token(Lexeme.Else, "else"),
endIf: token(Lexeme.EndIf, "end if")
Expand Down
16 changes: 8 additions & 8 deletions test/parser/controlFlow/If.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
identifier("foo"),
token(Lexeme.Equal, "="),
token(Lexeme.True, "true", BrsBoolean.True),
Expand All @@ -39,7 +39,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
identifier("foo"),
token(Lexeme.Equal, "="),
token(Lexeme.True, "true", BrsBoolean.True),
Expand All @@ -63,15 +63,15 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
identifier("foo"),
token(Lexeme.Equal, "="),
token(Lexeme.True, "true", BrsBoolean.True),
token(Lexeme.ElseIf, "else if"),
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Equal, "="),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
identifier("same"),
token(Lexeme.Equal, "="),
token(Lexeme.True, "true", BrsBoolean.True),
Expand Down Expand Up @@ -127,7 +127,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
token(Lexeme.Newline, "\n"),
identifier("foo"),
token(Lexeme.Equal, "="),
Expand All @@ -153,7 +153,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
token(Lexeme.Newline, "\n"),
identifier("foo"),
token(Lexeme.Equal, "="),
Expand Down Expand Up @@ -185,7 +185,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Less, "<"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
token(Lexeme.Newline, "\n"),
identifier("foo"),
token(Lexeme.Equal, "="),
Expand All @@ -195,7 +195,7 @@ describe("parser if statements", () => {
token(Lexeme.Integer, "1", new Int32(1)),
token(Lexeme.Greater, ">"),
token(Lexeme.Integer, "2", new Int32(2)),
token(Lexeme.Then, "then"),
identifier("then"),
token(Lexeme.Newline, "\n"),
identifier("foo"),
token(Lexeme.Equal, "="),
Expand Down
6 changes: 3 additions & 3 deletions test/parser/controlFlow/__snapshots__/For.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ Array [
},
"to": Object {
"isReserved": true,
"kind": 85,
"kind": 84,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -306,7 +306,7 @@ Array [
"step": undefined,
"to": Object {
"isReserved": true,
"kind": 85,
"kind": 84,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -461,7 +461,7 @@ Array [
"step": undefined,
"to": Object {
"isReserved": true,
"kind": 85,
"kind": 84,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down
12 changes: 6 additions & 6 deletions test/parser/controlFlow/__snapshots__/If.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -886,7 +886,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -1300,7 +1300,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -1791,7 +1791,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -2017,7 +2017,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -2362,7 +2362,7 @@ Array [
},
"then": Object {
"isReserved": true,
"kind": 84,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down
4 changes: 2 additions & 2 deletions test/parser/controlFlow/__snapshots__/While.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Array [
},
"while": Object {
"isReserved": true,
"kind": 88,
"kind": 87,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down Expand Up @@ -219,7 +219,7 @@ Array [
},
"while": Object {
"isReserved": true,
"kind": 88,
"kind": 87,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down
2 changes: 1 addition & 1 deletion test/parser/expression/__snapshots__/Function.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2271,7 +2271,7 @@ Array [
},
"name": Object {
"isReserved": false,
"kind": 29,
"kind": 28,
"literal": undefined,
"location": Object {
"end": Object {
Expand Down
Binary file modified test/preprocessor/__snapshots__/parser.test.js.snap
Binary file not shown.

0 comments on commit 11789b2

Please sign in to comment.