Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactors PrintStatement so that .values array is all expressions #1296

Merged
merged 8 commits into from
Jan 3, 2025
1 change: 1 addition & 0 deletions src/astUtils/visitors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ describe('astUtils visitors', () => {
program.setFile('source/main.brs', EXPRESSIONS_SRC);
expect(actual).to.deep.equal([
'PrintStatement:1:LiteralExpression', // print <"msg">; 3
'PrintStatement:1:LiteralExpression', // print "msg"<;> 3
'PrintStatement:1:LiteralExpression', // print "msg"; <3>
'PrintStatement:1:TemplateStringExpression', // print <`expand ${var}`>
'PrintStatement:1:TemplateStringQuasiExpression', // print `<expand >${var}`
Expand Down
9 changes: 9 additions & 0 deletions src/lexer/TokenKind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,12 @@ export const AllowedTriviaTokens: ReadonlyArray<TokenKind> = [
TokenKind.Comment,
TokenKind.Colon
];


/**
* The tokens that are used as print separators
*/
export const PrintSeparatorTokens: ReadonlyArray<TokenKind> = [
TokenKind.Comma,
TokenKind.Semicolon
];
9 changes: 3 additions & 6 deletions src/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2428,16 +2428,13 @@ export class Parser {
private printStatement(): PrintStatement {
let printKeyword = this.advance();

let values: (
| Expression
| PrintSeparatorTab
| PrintSeparatorSpace)[] = [];
let values: Expression[] = [];

while (!this.checkEndOfStatement()) {
if (this.check(TokenKind.Semicolon)) {
values.push(this.advance() as PrintSeparatorSpace);
values.push(new LiteralExpression({ value: this.advance() as PrintSeparatorSpace }));
} else if (this.check(TokenKind.Comma)) {
values.push(this.advance() as PrintSeparatorTab);
values.push(new LiteralExpression({ value: this.advance() as PrintSeparatorTab }));
} else if (this.check(TokenKind.Else)) {
break; // inline branch
} else {
Expand Down
19 changes: 10 additions & 9 deletions src/parser/Statement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-bitwise */
import type { Token, Identifier } from '../lexer/Token';
import { TokenKind } from '../lexer/TokenKind';
import { PrintSeparatorTokens, TokenKind } from '../lexer/TokenKind';
import type { DottedGetExpression, FunctionExpression, FunctionParameterExpression, LiteralExpression, TypeExpression, TypecastExpression } from './Expression';
import { CallExpression, VariableExpression } from './Expression';
import { util } from '../util';
Expand All @@ -9,7 +9,7 @@ import type { BrsTranspileState } from './BrsTranspileState';
import { ParseMode } from './Parser';
import type { WalkVisitor, WalkOptions } from '../astUtils/visitors';
import { InternalWalkMode, walk, createVisitor, WalkMode, walkArray } from '../astUtils/visitors';
import { isCallExpression, isCatchStatement, isConditionalCompileStatement, isEnumMemberStatement, isExpression, isExpressionStatement, isFieldStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isIfStatement, isInterfaceFieldStatement, isInterfaceMethodStatement, isInvalidType, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTryCatchStatement, isTypedefProvider, isUnaryExpression, isVoidType, isWhileStatement } from '../astUtils/reflection';
import { isCallExpression, isCatchStatement, isConditionalCompileStatement, isEnumMemberStatement, isExpressionStatement, isFieldStatement, isForEachStatement, isForStatement, isFunctionExpression, isFunctionStatement, isIfStatement, isInterfaceFieldStatement, isInterfaceMethodStatement, isInvalidType, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTryCatchStatement, isTypedefProvider, isUnaryExpression, isVoidType, isWhileStatement } from '../astUtils/reflection';
import { TypeChainEntry, type GetTypeOptions, type TranspileResult, type TypedefProvider } from '../interfaces';
import { createInvalidLiteral, createMethodStatement, createToken } from '../astUtils/creators';
import { DynamicType } from '../types/DynamicType';
Expand Down Expand Up @@ -771,11 +771,11 @@ export class PrintStatement extends Statement {
* Creates a new internal representation of a BrightScript `print` statement.
* @param options the options for this statement
* @param options.print a print token
* @param options.expressions an array of expressions or `PrintSeparator`s to be evaluated and printed.
* @param options.expressions an array of expressions to be evaluated and printed. Wrap `PrintSeparator`s (`;` or `,`) in LiteralExpression
*/
constructor(options: {
print: Token;
expressions: Array<Expression | PrintSeparatorTab | PrintSeparatorSpace>;
expressions: Array<Expression>;
}) {
super();
this.tokens = {
Expand All @@ -790,7 +790,7 @@ export class PrintStatement extends Statement {
public readonly tokens: {
readonly print: Token;
};
public readonly expressions: Array<Expression | PrintSeparatorTab | PrintSeparatorSpace>;
public readonly expressions: Array<Expression>;
public readonly kind = AstNodeKind.PrintStatement;

public readonly location: Location | undefined;
Expand All @@ -809,8 +809,10 @@ export class PrintStatement extends Statement {
state.tokenToSourceNode(expressionOrSeparator)
);
}
//if there's an expression after us, add a space
if ((this.expressions[i + 1] as any)?.transpile) {
//if there isn't a print separator, add a space
const nextExpression = this.expressions[i + 1];
const isSeparator = isLiteralExpression(nextExpression) && PrintSeparatorTokens.includes(nextExpression.tokens.value.kind);
if (nextExpression && !isSeparator) {
result.push(' ');
}
}
Expand All @@ -819,8 +821,7 @@ export class PrintStatement extends Statement {

walk(visitor: WalkVisitor, options: WalkOptions) {
if (options.walkMode & InternalWalkMode.walkExpressions) {
//sometimes we have semicolon Tokens in the expressions list (should probably fix that...), so only walk the actual expressions
walkArray(this.expressions, visitor, options, this, (item) => isExpression(item as any));
walkArray(this.expressions, visitor, options, this);
}
}

Expand Down