Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/BsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,14 @@ export interface BsConfig {
*/
allowBrighterScriptInBrightScript?: boolean;

/**
* Allow automatic line continuation in BrightScript (`.brs`) files after binary operators (`+`, `-`, `*`, `/`, `\`, `mod`, `^`, `and`, `or`, and relational operators).
* This syntax is always enabled in BrighterScript (`.bs`) files.
* Corresponds to the line continuation support added in Roku OS 15.2.
* @default false
*/
allowLineContinuation?: boolean;

/**
* Override the destination directory for the bslib.brs file. Use this if
* you want to customize where the bslib.brs file is located in the staging
Expand Down
1 change: 1 addition & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ let options = yargs
.option('create-package', { type: 'boolean', defaultDescription: 'true', description: 'Creates a zip package. This setting is ignored when deploy is enabled.' })
.option('source-map', { type: 'boolean', defaultDescription: 'false', description: 'Enables generating sourcemap files, which allow debugging tools to show the original source code while running the emitted files.' })
.option('allow-brighterscript-in-brightscript', { alias: 'allow-brighter-script-in-bright-script', type: 'boolean', defaultDescription: 'false', description: 'Allow brighterscript features (classes, interfaces, etc...) to be included in BrightScript (`.brs`) files, and force those files to be transpiled..' })
.option('allow-line-continuation', { type: 'boolean', defaultDescription: 'false', description: 'Allow automatic line continuation in BrightScript (`.brs`) files after binary operators. Always enabled in BrighterScript (`.bs`) files.' })
.option('cwd', { type: 'string', description: 'Override the current working directory.' })
.option('copy-to-staging', { type: 'boolean', defaultDescription: 'true', description: 'Copy project files into the staging folder, ready to be packaged.' })
.option('diagnostic-level', { type: 'string', defaultDescription: '"warn"', description: 'Specify what diagnostic types should be printed to the console. Value can be "error", "warn", "hint", "info".' })
Expand Down
98 changes: 95 additions & 3 deletions src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,100 @@ describe('BrsFile', () => {
});
});

describe('allowLineContinuation', () => {
it('does not allow binary operator continuation in .brs files by default', () => {
program.setFile('source/main.brs', `
sub main()
result = 1 +
2
end sub
`);
program.validate();
expectHasDiagnostics(program);
});

it('allows binary operator continuation in .bs files', () => {
program.setFile('source/main.bs', `
sub main()
result = 1 +
2
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});

it('allows binary operator continuation in .brs files when allowBrighterScriptInBrightScript is enabled', () => {
program.options.allowBrighterScriptInBrightScript = true;
program.setFile('source/main.brs', `
sub main()
result = 1 +
2
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});

it('allows binary operator continuation in .brs files when allowLineContinuation is enabled', () => {
program.options.allowLineContinuation = true;
program.setFile('source/main.brs', `
sub main()
result = 1 +
2
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});

it('does not allow multi-line function call args in .brs files by default', () => {
program.setFile('source/main.brs', `
sub main()
foo(
1,
2
)
end sub
sub foo(a, b)
end sub
`);
program.validate();
expectHasDiagnostics(program);
});

it('allows multi-line function call args in .bs files', () => {
program.setFile('source/main.bs', `
sub main()
foo(
1,
2
)
end sub
sub foo(a, b)
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});

it('allows multi-line function call args in .brs files when allowLineContinuation is enabled', () => {
program.options.allowLineContinuation = true;
program.setFile('source/main.brs', `
sub main()
foo(
1,
2
)
end sub
sub foo(a, b)
end sub
`);
program.validate();
expectZeroDiagnostics(program);
});
});

it('does not show "missing function" diagnostic for `call().dottedGet` as a statement', () => {
program.setFile(`source/main.brs`, `
sub main()
Expand Down Expand Up @@ -1551,10 +1645,8 @@ describe('BrsFile', () => {
`);
expectDiagnostics(file.parser.diagnostics, [
DiagnosticMessages.expectedRightParenAfterFunctionCallArguments(),
DiagnosticMessages.expectedNewlineOrColon(),
DiagnosticMessages.unexpectedToken('end function'),
DiagnosticMessages.expectedRightParenAfterFunctionCallArguments(),
DiagnosticMessages.expectedNewlineOrColon()
DiagnosticMessages.unexpectedToken('\n')
]);
expect(file.functionCalls.length).to.equal(2);

Expand Down
3 changes: 2 additions & 1 deletion src/files/BrsFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,8 @@ export class BrsFile {
this.program.logger.time('debug', ['parser.parse', chalk.green(this.srcPath)], () => {
this._parser = Parser.parse(tokens, {
mode: this.parseMode,
logger: this.program.logger
logger: this.program.logger,
allowLineContinuation: this.program.options.allowLineContinuation
});
});

Expand Down
143 changes: 143 additions & 0 deletions src/parser/Parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1786,6 +1786,149 @@ describe('parser', () => {
});
});

describe('line continuation', () => {
describe('binary operator continuation', () => {
it('is allowed after arithmetic operators in BrighterScript mode', () => {
let { diagnostics } = parse(`
sub main()
a = x +
y
b = x -
y
c = x *
y
d = x /
y
e = x \\
y
f = x mod
y
g = x ^
y
end sub
`, ParseMode.BrighterScript);
expectZeroDiagnostics(diagnostics);
});

it('is allowed after boolean operators in BrighterScript mode', () => {
let { diagnostics } = parse(`
sub main()
a = isValid and
isEnabled
b = isValid or
isEnabled
end sub
`, ParseMode.BrighterScript);
expectZeroDiagnostics(diagnostics);
});

it('is allowed after relational operators in BrighterScript mode', () => {
let { diagnostics } = parse(`
sub main()
a = x =
y
b = x <>
y
c = x >
y
d = x >=
y
e = x <
y
f = x <=
y
end sub
`, ParseMode.BrighterScript);
expectZeroDiagnostics(diagnostics);
});

it('is allowed in BrightScript mode when allowLineContinuation is set', () => {
let { tokens } = Lexer.scan(`
sub main()
result = value1 +
value2
end sub
`);
let { diagnostics } = Parser.parse(tokens, {
mode: ParseMode.BrightScript,
allowLineContinuation: true
} as any);
expectZeroDiagnostics(diagnostics);
});

it('is not allowed in BrightScript mode', () => {
let { diagnostics } = parse(`
sub main()
result = value1 +
value2
end sub
`, ParseMode.BrightScript);
expect(diagnostics.length).to.be.greaterThan(0);
});
});

describe('function call argument continuation', () => {
it('is not allowed in BrightScript mode', () => {
let { diagnostics } = parse(`
sub main()
result = foo(
arg1,
arg2
)
end sub
sub foo(a, b)
end sub
`, ParseMode.BrightScript);
expect(diagnostics.length).to.be.greaterThan(0);
});

it('is allowed in BrighterScript mode', () => {
let { diagnostics } = parse(`
sub main()
result = foo(
arg1,
arg2
)
end sub
sub foo(a, b)
end sub
`, ParseMode.BrighterScript);
expectZeroDiagnostics(diagnostics);
});

it('is allowed in BrightScript mode when allowLineContinuation is set', () => {
let { tokens } = Lexer.scan(`
sub main()
result = foo(
arg1,
arg2
)
end sub
sub foo(a, b)
end sub
`);
let { diagnostics } = Parser.parse(tokens, {
mode: ParseMode.BrightScript,
allowLineContinuation: true
});
expectZeroDiagnostics(diagnostics);
});

it('does not affect inline objects passed as arguments in BrightScript mode', () => {
let { diagnostics } = parse(`
sub main()
foo({
key: "value"
})
end sub
sub foo(a)
end sub
`, ParseMode.BrightScript);
expectZeroDiagnostics(diagnostics);
});
});
});

describe('typed functions as types', () => {
it('disallowed in brightscript mode', () => {
let { diagnostics } = parse(`
Expand Down
Loading