Skip to content

Commit

Permalink
Make ElseIf into an ElseIfStatement
Browse files Browse the repository at this point in the history
  • Loading branch information
Philippe Elsass committed Nov 18, 2020
1 parent e920e8f commit 0d6925e
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 67 deletions.
9 changes: 7 additions & 2 deletions src/astUtils/reflection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/* eslint-disable no-multi-spaces */
import { Position, Range } from 'vscode-languageserver';
import { expect } from 'chai';
import { PrintStatement, Block, Body, AssignmentStatement, CommentStatement, ExitForStatement, ExitWhileStatement, ExpressionStatement, FunctionStatement, IfStatement, IncrementStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassStatement, EmptyStatement } from '../parser/Statement';
import { PrintStatement, Block, Body, AssignmentStatement, CommentStatement, ExitForStatement, ExitWhileStatement, ExpressionStatement, FunctionStatement, IfStatement, IncrementStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassStatement, EmptyStatement, ElseIfStatement } from '../parser/Statement';
import { FunctionExpression, NamespacedVariableNameExpression, BinaryExpression, CallExpression, DottedGetExpression, IndexedGetExpression, GroupingExpression, LiteralExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, XmlAttributeGetExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression } from '../parser/Expression';
import type { Token } from '../lexer';
import { TokenKind } from '../lexer';
import { BrsString } from '../brsTypes';
import { isPrintStatement, isIfStatement, isBody, isAssignmentStatement, isBlock, isExpressionStatement, isCommentStatement, isExitForStatement, isExitWhileStatement, isFunctionStatement, isIncrementStatement, isGotoStatement, isLabelStatement, isReturnStatement, isEndStatement, isStopStatement, isForStatement, isForEachStatement, isWhileStatement, isDottedSetStatement, isIndexedSetStatement, isLibraryStatement, isNamespaceStatement, isImportStatement, isExpression, isBinaryExpression, isCallExpression, isFunctionExpression, isNamespacedVariableNameExpression, isDottedGetExpression, isXmlAttributeGetExpression, isIndexedGetExpression, isGroupingExpression, isLiteralExpression, isEscapedCharCodeLiteralExpression, isArrayLiteralExpression, isAALiteralExpression, isUnaryExpression, isVariableExpression, isSourceLiteralExpression, isNewExpression, isCallfuncExpression, isTemplateStringQuasiExpression, isTemplateStringExpression, isTaggedTemplateStringExpression, isBrsFile, isXmlFile, isClassStatement, isStatement, isAnnotationExpression } from './reflection';
import { isPrintStatement, isIfStatement, isBody, isAssignmentStatement, isBlock, isExpressionStatement, isCommentStatement, isExitForStatement, isExitWhileStatement, isFunctionStatement, isIncrementStatement, isGotoStatement, isLabelStatement, isReturnStatement, isEndStatement, isStopStatement, isForStatement, isForEachStatement, isWhileStatement, isDottedSetStatement, isIndexedSetStatement, isLibraryStatement, isNamespaceStatement, isImportStatement, isExpression, isBinaryExpression, isCallExpression, isFunctionExpression, isNamespacedVariableNameExpression, isDottedGetExpression, isXmlAttributeGetExpression, isIndexedGetExpression, isGroupingExpression, isLiteralExpression, isEscapedCharCodeLiteralExpression, isArrayLiteralExpression, isAALiteralExpression, isUnaryExpression, isVariableExpression, isSourceLiteralExpression, isNewExpression, isCallfuncExpression, isTemplateStringQuasiExpression, isTemplateStringExpression, isTaggedTemplateStringExpression, isBrsFile, isXmlFile, isClassStatement, isStatement, isAnnotationExpression, isElseIfStatement } from './reflection';
import { createRange, createToken, createStringLiteral, createIdentifier } from './creators';
import { Program } from '../Program';
import { BrsFile } from '../files/BrsFile';
Expand Down Expand Up @@ -40,6 +40,7 @@ describe('reflection', () => {
const exitWhile = new ExitWhileStatement({ exitWhile: token });
const funs = new FunctionStatement(ident, new FunctionExpression([], undefined, block, token, token, token, token), undefined);
const ifs = new IfStatement({ if: token }, expr, block, []);
const elseifs = new ElseIfStatement({ elseIfToken: token }, expr, block);
const increment = new IncrementStatement(expr, token);
const print = new PrintStatement({ print: token }, []);
const gotos = new GotoStatement({ goto: token, label: token });
Expand Down Expand Up @@ -105,6 +106,10 @@ describe('reflection', () => {
expect(isIfStatement(ifs)).to.be.true;
expect(isIfStatement(body)).to.be.false;
});
it('isElseIfStatement', () => {
expect(isElseIfStatement(elseifs)).to.be.true;
expect(isElseIfStatement(body)).to.be.false;
});
it('isIncrementStatement', () => {
expect(isIncrementStatement(increment)).to.be.true;
expect(isIncrementStatement(body)).to.be.false;
Expand Down
5 changes: 4 additions & 1 deletion src/astUtils/reflection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Body, AssignmentStatement, Block, ExpressionStatement, CommentStatement, ExitForStatement, ExitWhileStatement, FunctionStatement, IfStatement, IncrementStatement, PrintStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassFieldStatement, ClassMethodStatement, ClassStatement, Statement } from '../parser/Statement';
import type { Body, AssignmentStatement, Block, ExpressionStatement, CommentStatement, ExitForStatement, ExitWhileStatement, FunctionStatement, IfStatement, ElseIfStatement, IncrementStatement, PrintStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassFieldStatement, ClassMethodStatement, ClassStatement, Statement } from '../parser/Statement';
import type { LiteralExpression, Expression, BinaryExpression, CallExpression, FunctionExpression, NamespacedVariableNameExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression } from '../parser/Expression';
import type { BrsString, BrsInvalid, BrsBoolean, RoString, RoArray, RoAssociativeArray, RoSGNode, FunctionParameterExpression } from '../brsTypes';
import { ValueKind } from '../brsTypes';
Expand Down Expand Up @@ -60,6 +60,9 @@ export function isFunctionStatement(element: Statement | Expression | undefined)
export function isIfStatement(element: Statement | Expression | undefined): element is IfStatement {
return element?.constructor?.name === 'IfStatement';
}
export function isElseIfStatement(element: Statement | Expression | undefined): element is ElseIfStatement {
return element?.constructor?.name === 'ElseIfStatement';
}
export function isIncrementStatement(element: Statement | Expression | undefined): element is IncrementStatement {
return element?.constructor?.name === 'IncrementStatement';
}
Expand Down
20 changes: 14 additions & 6 deletions src/astUtils/visitors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ describe('astUtils visitors', () => {
'IfStatement:1', // if a = 1
'Block:2', // then block
'PrintStatement:3', // print 3
'Block:2', // elseif block
'PrintStatement:3', // print 4
'ElseIfStatement:2', // elseif statement
'Block:3', // elseif block
'PrintStatement:4', // print 4
'Block:2', // else block
'PrintStatement:3', // print 5
'WhileStatement:1', // while a <> invalid
Expand Down Expand Up @@ -150,6 +151,7 @@ describe('astUtils visitors', () => {
'IfStatement', // if a = 1
'Block', // then block
'PrintStatement', // print 3
'ElseIfStatement', // elseif statement
'Block', // elseif block
'PrintStatement', // print 4
'Block', // else block
Expand Down Expand Up @@ -194,6 +196,7 @@ describe('astUtils visitors', () => {
'IfStatement', // if a = 1
'Block', // then block
'PrintStatement', // print 3
'ElseIfStatement', // elseif statement
'Block', // elseif block
'PrintStatement' // print 4
]);
Expand Down Expand Up @@ -302,10 +305,10 @@ describe('astUtils visitors', () => {
'IfStatement:1:BinaryExpression', // if <j > 0>
'IfStatement:1:VariableExpression', // if <j> > 0
'IfStatement:1:LiteralExpression', // if j > <0>
'IfStatement:1:BinaryExpression', // else if <j < -10>
'IfStatement:1:VariableExpression', // else if <j> < -10
'IfStatement:1:UnaryExpression', // else if j < <-10>
'IfStatement:1:LiteralExpression', // else if j < -<10>
'ElseIfStatement:2:BinaryExpression', // else if <j < -10>
'ElseIfStatement:2:VariableExpression', // else if <j> < -10
'ElseIfStatement:2:UnaryExpression', // else if j < <-10>
'ElseIfStatement:2:LiteralExpression', // else if j < -<10>
'ReturnStatement:1:LiteralExpression' // return <invalid>
]);
});
Expand Down Expand Up @@ -375,6 +378,7 @@ describe('astUtils visitors', () => {
'PrintStatement',
'LiteralExpression',
//else if
'ElseIfStatement',
'LiteralExpression',
'Block',
'PrintStatement',
Expand Down Expand Up @@ -741,18 +745,22 @@ describe('astUtils visitors', () => {
'Block',
'AssignmentStatement',
'LiteralExpression',
//if
'IfStatement',
'BinaryExpression',
'VariableExpression',
'LiteralExpression',
'Block',
'ReturnStatement',
//else if
'ElseIfStatement',
'BinaryExpression',
'VariableExpression',
'LiteralExpression',
'Block',
'ReturnStatement',
'LiteralExpression',
//else
'Block',
'ReturnStatement',
'CommentStatement'
Expand Down
34 changes: 19 additions & 15 deletions src/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import {
import type {
Statement,
PrintSeparatorTab,
PrintSeparatorSpace,
ElseIf
PrintSeparatorSpace
} from './Statement';
import {
ElseIfStatement,
FunctionStatement,
CommentStatement,
AssignmentStatement,
Expand Down Expand Up @@ -1381,7 +1381,7 @@ export class Parser {

const condition = this.expression();
let thenBranch: Block;
let elseIfBranches: ElseIf[] = [];
let elseIfBranches: ElseIfStatement[] = [];
let elseBranch: Block | undefined;

let thenToken: Token | undefined;
Expand Down Expand Up @@ -1472,12 +1472,14 @@ export class Parser {
endIfToken = blockEnd;
}

elseIfBranches.push({
condition: elseIfCondition,
thenBranch: elseIfThen,
thenToken: thenToken,
elseIfToken: elseIfToken
});
elseIfBranches.push(new ElseIfStatement(
{
thenToken: thenToken,
elseIfToken: elseIfToken
},
elseIfCondition,
elseIfThen
));
}

if (this.match(TokenKind.Else)) {
Expand Down Expand Up @@ -1558,12 +1560,14 @@ export class Parser {
throw this.lastDiagnosticAsError();
}

elseIfBranches.push({
condition: elseIfCondition,
thenBranch: new Block([elseIfThen], this.peek().range),
thenToken: thenToken,
elseIfToken: elseIf
});
elseIfBranches.push(new ElseIfStatement(
{
thenToken: thenToken,
elseIfToken: elseIf
},
elseIfCondition,
new Block([elseIfThen], this.peek().range)
));
}
if (this.previous().kind !== TokenKind.Newline && this.match(TokenKind.Else)) {
elseToken = this.previous();
Expand Down
112 changes: 69 additions & 43 deletions src/parser/Statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,67 @@ export class FunctionStatement extends Statement implements TypedefProvider {
}
}

export interface ElseIf {
elseIfToken: Token;
thenToken?: Token;
condition: Expression;
thenBranch: Block;
export class ElseIfStatement extends Statement {
constructor(
readonly tokens: {
elseIfToken: Token;
thenToken?: Token;
},
readonly condition: Expression,
readonly thenBranch: Block
) {
super();
this.range = util.createRangeFromPositions(
this.tokens.elseIfToken.range.start,
(this.thenBranch ?? this.tokens.thenToken ?? this.condition).range.end
);
}
public readonly range: Range;

transpile(state: TranspileState) {
const { elseIfToken, thenToken } = this.tokens;
const { condition, thenBranch } = this;
const results = [];

results.push(
state.indent(),
new SourceNode(elseIfToken.range.start.line + 1, elseIfToken.range.start.character, state.pathAbsolute, 'else if'),
' '
);

//condition
results.push(...condition.transpile(state));
//then
results.push(' ');
if (thenToken) {
results.push(
new SourceNode(thenToken.range.start.line + 1, thenToken.range.start.character, state.pathAbsolute, 'then')
);
} else {
results.push('then');
}

//then body
state.lineage.unshift(thenBranch);
let body = thenBranch.transpile(state);
state.lineage.shift();

if (body.length > 0) {
results.push(...body);
}
results.push('\n');

return results;
}

walk(visitor: WalkVisitor, options: WalkOptions) {
if (options.walkMode & InternalWalkMode.walkExpressions) {
walk(this, 'condition', visitor, options);
}
if (options.walkMode & InternalWalkMode.walkStatements) {
walk(this, 'thenBranch', visitor, options);
}
}
}

export class IfStatement extends Statement {
Expand All @@ -403,7 +459,7 @@ export class IfStatement extends Statement {
},
readonly condition: Expression,
readonly thenBranch: Block,
readonly elseIfs: ElseIf[],
readonly elseIfs: ElseIfStatement[],
readonly elseBranch?: Block
) {
super();
Expand Down Expand Up @@ -441,34 +497,8 @@ export class IfStatement extends Statement {

//else if blocks
for (let elseif of this.elseIfs) {
//elseif
results.push(
state.indent(),
new SourceNode(elseif.elseIfToken.range.start.line + 1, elseif.elseIfToken.range.start.character, state.pathAbsolute, 'else if'),
' '
);

//condition
results.push(...elseif.condition.transpile(state));
//then
results.push(' ');
if (elseif.thenToken) {
results.push(
new SourceNode(elseif.thenToken.range.start.line + 1, elseif.thenToken.range.start.character, state.pathAbsolute, 'then')
);
} else {
results.push('then');
}

//then body
state.lineage.unshift(elseif.thenBranch);
let body = elseif.thenBranch.transpile(state);
state.lineage.shift();

if (body.length > 0) {
results.push(...body);
}
results.push('\n');
let elseifNodes = elseif.transpile(state);
results.push(elseifNodes);
}

//else branch
Expand Down Expand Up @@ -510,18 +540,14 @@ export class IfStatement extends Statement {
walk(this, 'thenBranch', visitor, options);
}

for (let i = 0; i < this.elseIfs.length; i++) {
if (options.walkMode & InternalWalkMode.walkExpressions) {
walk(this.elseIfs[i], 'condition', visitor, options, this);
if (options.walkMode & InternalWalkMode.walkStatements) {
for (let i = 0; i < this.elseIfs.length; i++) {
walk(this.elseIfs, i, visitor, options, this);
}
if (options.walkMode & InternalWalkMode.walkStatements) {
walk(this.elseIfs[i], 'thenBranch', visitor, options, this);
if (this.elseBranch) {
walk(this, 'elseBranch', visitor, options);
}
}

if (this.elseBranch && options.walkMode & InternalWalkMode.walkStatements) {
walk(this, 'elseBranch', visitor, options);
}
}
}

Expand Down

0 comments on commit 0d6925e

Please sign in to comment.