From d6f1fa4b2c46cc32bd082f22a802bec208c4d78d Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Wed, 13 Jul 2022 14:16:14 -0400 Subject: [PATCH 1/2] Flag unsupported top-level statements --- src/DiagnosticMessages.ts | 5 +++ src/Program.spec.ts | 16 +++++++++ src/bscPlugin/validation/BrsFileValidator.ts | 34 ++++++++++++++++++-- src/files/BrsFile.ts | 11 +++++-- src/parser/Parser.spec.ts | 2 +- 5 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts index b9d87b226..ecd65306c 100644 --- a/src/DiagnosticMessages.ts +++ b/src/DiagnosticMessages.ts @@ -683,6 +683,11 @@ export let DiagnosticMessages = { message: `Circular reference detected between ${Array.isArray(items) ? items.join(' -> ') : ''} in scope '${scopeName}'`, code: 1132, severity: DiagnosticSeverity.Error + }), + unexpectedStatementOutsideFunction: () => ({ + message: `Unexpected statement found outside of function body`, + code: 1133, + severity: DiagnosticSeverity.Error }) }; diff --git a/src/Program.spec.ts b/src/Program.spec.ts index 682a91edd..b1b1407aa 100644 --- a/src/Program.spec.ts +++ b/src/Program.spec.ts @@ -75,6 +75,22 @@ describe('Program', () => { //if the program didn't get stuck in an infinite loop, this test passes }); + it('flags unsupported statements at root of file', () => { + program.setFile('source/main.brs', ` + result = true + print true + createObject("roSGNode", "Rectangle") + `); + program.validate(); + expectDiagnostics(program, [{ + ...DiagnosticMessages.unexpectedStatementOutsideFunction() + }, { + ...DiagnosticMessages.unexpectedStatementOutsideFunction() + }, { + ...DiagnosticMessages.unexpectedStatementOutsideFunction() + }]); + }); + it('only parses xml files as components when file is found within the "components" folder', () => { expect(Object.keys(program.files).length).to.equal(0); diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts index 6f4f9d093..d83a9b27c 100644 --- a/src/bscPlugin/validation/BrsFileValidator.ts +++ b/src/bscPlugin/validation/BrsFileValidator.ts @@ -1,4 +1,4 @@ -import { isLiteralExpression } from '../..'; +import { isClassStatement, isCommentStatement, isEnumStatement, isFunctionStatement, isImportStatement, isInterfaceStatement, isLibraryStatement, isLiteralExpression, isNamespaceStatement } from '../../astUtils/reflection'; import { DiagnosticMessages } from '../../DiagnosticMessages'; import type { BrsFile } from '../../files/BrsFile'; import type { BsDiagnostic, OnFileValidateEvent } from '../../interfaces'; @@ -14,9 +14,10 @@ export class BrsFileValidator { public process() { this.validateEnumDeclarations(); + this.flagTopLevelStatements(); } - public validateEnumDeclarations() { + private validateEnumDeclarations() { const diagnostics = [] as BsDiagnostic[]; for (const stmt of this.event.file.parser.references.enumStatements) { const members = stmt.getMembers(); @@ -93,4 +94,33 @@ export class BrsFileValidator { } } } + + /** + * Find statements defined at the top level (or inside a namespace body) that are not allowed to be there + */ + private flagTopLevelStatements() { + const statements = [...this.event.file.ast.statements]; + while (statements.length > 0) { + const statement = statements.pop(); + if (isNamespaceStatement(statement)) { + statements.push(...statement.body.statements); + } else { + //only allow these statement types + if ( + !isFunctionStatement(statement) && + !isClassStatement(statement) && + !isEnumStatement(statement) && + !isInterfaceStatement(statement) && + !isCommentStatement(statement) && + !isLibraryStatement(statement) && + !isImportStatement(statement) + ) { + this.event.file.addDiagnostic({ + ...DiagnosticMessages.unexpectedStatementOutsideFunction(), + range: statement.range + }); + } + } + } + } } diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts index d8142b4e4..48a6263dd 100644 --- a/src/files/BrsFile.ts +++ b/src/files/BrsFile.ts @@ -1,13 +1,13 @@ import type { CodeWithSourceMap } from 'source-map'; import { SourceNode } from 'source-map'; -import type { CompletionItem, Hover, Position, Location } from 'vscode-languageserver'; +import type { CompletionItem, Hover, Position, Location, Diagnostic } from 'vscode-languageserver'; import { CompletionItemKind, SymbolKind, SignatureInformation, ParameterInformation, DocumentSymbol, SymbolInformation, TextEdit } from 'vscode-languageserver'; import chalk from 'chalk'; import * as path from 'path'; import type { Scope } from '../Scope'; import { DiagnosticCodeMap, diagnosticCodes, DiagnosticMessages } from '../DiagnosticMessages'; import { FunctionScope } from '../FunctionScope'; -import type { Callable, CallableArg, CallableParam, CommentFlag, FunctionCall, BsDiagnostic, FileReference, FileLink } from '../interfaces'; +import type { Callable, CallableArg, CallableParam, CommentFlag, FunctionCall, BsDiagnostic, FileReference, FileLink, BscFile } from '../interfaces'; import type { Token } from '../lexer/Token'; import { Lexer } from '../lexer/Lexer'; import { TokenKind, AllowedLocalIdentifiers, Keywords } from '../lexer/TokenKind'; @@ -101,6 +101,13 @@ export class BrsFile { return [...this.diagnostics]; } + public addDiagnostic(diagnostic: Diagnostic & { file?: BscFile }) { + if (!diagnostic.file) { + diagnostic.file = this; + } + this.diagnostics.push(diagnostic as any); + } + public addDiagnostics(diagnostics: BsDiagnostic[]) { this.diagnostics.push(...diagnostics); } diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index 7a1989c86..7a93553fd 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -9,7 +9,7 @@ import { PrintStatement, FunctionStatement, NamespaceStatement, ImportStatement import { Range } from 'vscode-languageserver'; import { DiagnosticMessages } from '../DiagnosticMessages'; import { isBlock, isCommentStatement, isFunctionStatement, isIfStatement, isIndexedGetExpression } from '../astUtils/reflection'; -import { expectZeroDiagnostics } from '../testHelpers.spec'; +import { expectDiagnostics, expectZeroDiagnostics } from '../testHelpers.spec'; import { BrsTranspileState } from './BrsTranspileState'; import { SourceNode } from 'source-map'; import { BrsFile } from '../files/BrsFile'; From f9ddebb1173f981d65e15c1a39801e78bed85922 Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Wed, 13 Jul 2022 15:21:14 -0400 Subject: [PATCH 2/2] Fix broken tests --- src/files/BrsFile.spec.ts | 141 ++++++----- src/files/tests/optionalChaning.spec.ts | 38 +-- src/parser/Parser.spec.ts | 2 +- .../NullCoalescenceExpression.spec.ts | 49 ++-- .../TemplateStringExpression.spec.ts | 228 ++++++++++++------ .../expression/TernaryExpression.spec.ts | 39 +-- .../tests/statement/PrintStatement.spec.ts | 129 +++++----- 7 files changed, 377 insertions(+), 249 deletions(-) diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index 637a72997..720ae9d02 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -2019,15 +2019,17 @@ describe('BrsFile', () => { it('transpiles if statement keywords as provided', () => { const code = ` - If True Then - Print True - Else If True Then - print True - Else If False Then - Print False - Else - Print False - End If + sub main() + If True Then + Print True + Else If True Then + print True + Else If False Then + Print False + Else + Print False + End If + end sub `; testTranspile(code); testTranspile(code.toLowerCase()); @@ -2035,36 +2037,41 @@ describe('BrsFile', () => { }); it('does not transpile `then` tokens', () => { - const code = ` - if true - print true - else if true - print false - end if - `; - testTranspile(code); + testTranspile(` + sub main() + if true + print true + else if true + print false + end if + end sub + `); }); it('honors spacing between multi-word tokens', () => { testTranspile(` - if true - print true - elseif true - print false - endif + sub main() + if true + print true + elseif true + print false + endif + end sub `); }); it('handles when only some of the statements have `then`', () => { testTranspile(` - if true - else if true then - else if true - else if true then - if true then - return true + sub main() + if true + else if true then + else if true + else if true then + if true then + return true + end if end if - end if + end sub `); }); @@ -2467,51 +2474,57 @@ describe('BrsFile', () => { it('handles empty if block', () => { testTranspile(` - if true then - end if - if true then - else - print "else" - end if - if true then - else if true then - print "else" - end if - if true then - else if true then - print "elseif" - else - print "else" - end if + sub main() + if true then + end if + if true then + else + print "else" + end if + if true then + else if true then + print "else" + end if + if true then + else if true then + print "elseif" + else + print "else" + end if + end sub `); }); it('handles empty elseif block', () => { testTranspile(` - if true then - print "if" - else if true then - end if - if true then - print "if" - else if true then - else if true then - end if + sub main() + if true then + print "if" + else if true then + end if + if true then + print "if" + else if true then + else if true then + end if + end sub `); }); it('handles empty else block', () => { testTranspile(` - if true then - print "if" - else - end if - if true then - print "if" - else if true then - print "elseif" - else - end if + sub main() + if true then + print "if" + else + end if + if true then + print "if" + else if true then + print "elseif" + else + end if + end sub `); }); diff --git a/src/files/tests/optionalChaning.spec.ts b/src/files/tests/optionalChaning.spec.ts index eb66538c3..b788d8476 100644 --- a/src/files/tests/optionalChaning.spec.ts +++ b/src/files/tests/optionalChaning.spec.ts @@ -73,24 +73,26 @@ describe('optional chaining', () => { it('transpiles various use cases', () => { testTranspile(` - obj = {} - arr = [] - print arr?.["0"] - print arr?.value - print obj?.[0] - print obj?.getName()?.first?.second - print createObject("roByteArray")?.value - print createObject("roByteArray")?["0"] - print createObject("roList")?.value - print createObject("roList")?["0"] - print createObject("roXmlList")?["0"] - print createObject("roDateTime")?.value - print createObject("roDateTime")?.GetTimeZoneOffset - print createObject("roSGNode", "Node")?[0] - print obj?.first?.second - print obj?.first?.second - print obj.b.xmlThing?@someAttr - print obj.b.localFunc?() + sub main() + obj = {} + arr = [] + print arr?.["0"] + print arr?.value + print obj?.[0] + print obj?.getName()?.first?.second + print createObject("roByteArray")?.value + print createObject("roByteArray")?["0"] + print createObject("roList")?.value + print createObject("roList")?["0"] + print createObject("roXmlList")?["0"] + print createObject("roDateTime")?.value + print createObject("roDateTime")?.GetTimeZoneOffset + print createObject("roSGNode", "Node")?[0] + print obj?.first?.second + print obj?.first?.second + print obj.b.xmlThing?@someAttr + print obj.b.localFunc?() + end sub `); }); }); diff --git a/src/parser/Parser.spec.ts b/src/parser/Parser.spec.ts index 7a93553fd..7a1989c86 100644 --- a/src/parser/Parser.spec.ts +++ b/src/parser/Parser.spec.ts @@ -9,7 +9,7 @@ import { PrintStatement, FunctionStatement, NamespaceStatement, ImportStatement import { Range } from 'vscode-languageserver'; import { DiagnosticMessages } from '../DiagnosticMessages'; import { isBlock, isCommentStatement, isFunctionStatement, isIfStatement, isIndexedGetExpression } from '../astUtils/reflection'; -import { expectDiagnostics, expectZeroDiagnostics } from '../testHelpers.spec'; +import { expectZeroDiagnostics } from '../testHelpers.spec'; import { BrsTranspileState } from './BrsTranspileState'; import { SourceNode } from 'source-map'; import { BrsFile } from '../files/BrsFile'; diff --git a/src/parser/tests/expression/NullCoalescenceExpression.spec.ts b/src/parser/tests/expression/NullCoalescenceExpression.spec.ts index 94872ca26..fafc0668a 100644 --- a/src/parser/tests/expression/NullCoalescenceExpression.spec.ts +++ b/src/parser/tests/expression/NullCoalescenceExpression.spec.ts @@ -191,14 +191,29 @@ describe('NullCoalescingExpression', () => { it('uses the proper prefix when aliased package is installed', () => { program.setFile('source/roku_modules/rokucommunity_bslib/bslib.brs', ''); - testTranspile( - 'a = user ?? false', - `a = rokucommunity_bslib_coalesce(user, false)` - ); + testTranspile(` + sub main() + a = user ?? false + end sub + `, ` + sub main() + a = rokucommunity_bslib_coalesce(user, false) + end sub + `); }); it('properly transpiles null coalesence assignments - simple', () => { - testTranspile(`a = user ?? {"id": "default"}`, 'a = bslib_coalesce(user, {\n "id": "default"\n})', 'none'); + testTranspile(` + sub main() + a = user ?? {"id": "default"} + end sub + `, ` + sub main() + a = bslib_coalesce(user, { + "id": "default" + }) + end sub + `); }); it('properly transpiles null coalesence assignments - complex consequent', () => { @@ -225,15 +240,21 @@ describe('NullCoalescingExpression', () => { }); it('transpiles null coalesence assignment for variable alternate- complex consequent', () => { - testTranspile(`a = obj.link ?? false`, ` - a = (function(obj) - __bsConsequent = obj.link - if __bsConsequent <> invalid then - return __bsConsequent - else - return false - end if - end function)(obj) + testTranspile(` + sub main() + a = obj.link ?? false + end sub + `, ` + sub main() + a = (function(obj) + __bsConsequent = obj.link + if __bsConsequent <> invalid then + return __bsConsequent + else + return false + end if + end function)(obj) + end sub `); }); diff --git a/src/parser/tests/expression/TemplateStringExpression.spec.ts b/src/parser/tests/expression/TemplateStringExpression.spec.ts index 3760777b8..35461a24e 100644 --- a/src/parser/tests/expression/TemplateStringExpression.spec.ts +++ b/src/parser/tests/expression/TemplateStringExpression.spec.ts @@ -76,133 +76,201 @@ describe('TemplateStringExpression', () => { it('uses the proper prefix when aliased package is installed', () => { program.setFile('source/roku_modules/rokucommunity_bslib/bslib.brs', ''); - testTranspile( - 'a = `${LINE_NUM},${LINE_NUM}`', - `a = rokucommunity_bslib_toString(LINE_NUM) + "," + rokucommunity_bslib_toString(LINE_NUM)` - ); + testTranspile(` + sub main() + a = \`\${LINE_NUM},\${LINE_NUM}\` + end sub + `, ` + sub main() + a = rokucommunity_bslib_toString(LINE_NUM) + "," + rokucommunity_bslib_toString(LINE_NUM) + end sub + `); }); it('properly transpiles simple template string with no leading text', () => { - testTranspile( - 'a = `${LINE_NUM},${LINE_NUM}`', - `a = bslib_toString(LINE_NUM) + "," + bslib_toString(LINE_NUM)` + testTranspile(` + sub main() + a = \`\${LINE_NUM},\${LINE_NUM}\` + end sub + `, ` + sub main() + a = bslib_toString(LINE_NUM) + "," + bslib_toString(LINE_NUM) + end sub + ` ); }); it('properly transpiles simple template string', () => { - testTranspile( - 'a = `hello world`', - 'a = "hello world"' - ); + testTranspile(` + sub main() + a = \`hello world\` + end sub + `, ` + sub main() + a = "hello world" + end sub + `); }); it('properly transpiles one line template string with expressions', () => { - testTranspile( - 'a = `hello ${LINE_NUM.text} world ${"template" + "".getChars()} test`', - `a = "hello " + bslib_toString(LINE_NUM.text) + " world " + bslib_toString("template" + "".getChars()) + " test"` - ); + testTranspile(` + sub main() + a = \`hello \${LINE_NUM.text} world \${"template" + "".getChars()} test\` + end sub + `, ` + sub main() + a = "hello " + bslib_toString(LINE_NUM.text) + " world " + bslib_toString("template" + "".getChars()) + " test" + end sub + `); }); it('handles escaped characters', () => { - testTranspile( - 'a = `\\r\\n\\`\\$`', - `a = chr(13) + chr(10) + chr(96) + chr(36)` - ); + testTranspile(` + sub main() + a = \`\\r\\n\\\`\\$\` + end sub + `, ` + sub main() + a = chr(13) + chr(10) + chr(96) + chr(36) + end sub + `); }); it('handles escaped unicode char codes', () => { - testTranspile( - 'a = `\\c2\\c987`', - `a = chr(2) + chr(987)` - ); + testTranspile(` + sub main() + a = \`\\c2\\c987\` + end sub + `, ` + sub main() + a = chr(2) + chr(987) + end sub + `); }); it('properly transpiles simple multiline template string', () => { - testTranspile( - 'a = `hello world\nI am multiline`', - 'a = "hello world" + chr(10) + "I am multiline"' - ); + testTranspile(` + sub main() + a = \`hello world\nI am multiline\` + end sub + `, ` + sub main() + a = "hello world" + chr(10) + "I am multiline" + end sub + `); }); it('properly handles newlines', () => { - testTranspile( - 'a = `\n`', - 'a = chr(10)' - ); + testTranspile(` + sub main() + a = \`\n\` + end sub + `, ` + sub main() + a = chr(10) + end sub + `); }); it('properly handles clrf', () => { - testTranspile( - 'a = `\r\n`', - 'a = chr(13) + chr(10)' - ); + testTranspile(` + sub main() + a = \`\r\n\` + end sub + `, ` + sub main() + a = chr(13) + chr(10) + end sub + `); }); it('properly transpiles more complex multiline template string', () => { - testTranspile( - 'a = `I am multiline\n${a.isRunning()}\nmore`', - 'a = "I am multiline" + chr(10) + bslib_toString(a.isRunning()) + chr(10) + "more"' - ); + testTranspile(` + sub main() + a = \`I am multiline\n\${a.isRunning()}\nmore\` + end sub + `, ` + sub main() + a = "I am multiline" + chr(10) + bslib_toString(a.isRunning()) + chr(10) + "more" + end sub + `); }); it('properly transpiles complex multiline template string in array def', () => { - testTranspile( - `a = [ - "one", - "two", - \`I am a complex example\${a.isRunning(["a", "b", "c"])}\` - ] + testTranspile(` + sub main() + a = [ + "one", + "two", + \`I am a complex example\${a.isRunning(["a", "b", "c"])}\` + ] + end sub `, ` - a = [ - "one" - "two" - "I am a complex example" + bslib_toString(a.isRunning([ - "a" - "b" - "c" - ])) - ] + sub main() + a = [ + "one" + "two" + "I am a complex example" + bslib_toString(a.isRunning([ + "a" + "b" + "c" + ])) + ] + end sub `); }); it('properly transpiles complex multiline template string in array def, with nested template', () => { testTranspile(` - a = [ - "one", - "two", - \`I am a complex example \${a.isRunning([ - "a", - "b", - "c", - \`d_open \${"inside" + m.items[1]} d_close\` - ])}\` - ] + sub main() + a = [ + "one", + "two", + \`I am a complex example \${a.isRunning([ + "a", + "b", + "c", + \`d_open \${"inside" + m.items[1]} d_close\` + ])}\` + ] + end sub `, ` - a = [ - "one" - "two" - "I am a complex example " + bslib_toString(a.isRunning([ - "a" - "b" - "c" - "d_open " + bslib_toString("inside" + m.items[1]) + " d_close" - ])) - ] + sub main() + a = [ + "one" + "two" + "I am a complex example " + bslib_toString(a.isRunning([ + "a" + "b" + "c" + "d_open " + bslib_toString("inside" + m.items[1]) + " d_close" + ])) + ] + end sub `); }); it('properly transpiles two template strings side-by-side', () => { - testTranspile( - 'a = `${"hello"}${"world"}`', - 'a = "hello" + "world"' - ); + testTranspile(` + sub main() + a = \`\${"hello"}\${"world"}\` + end sub + `, ` + sub main() + a = "hello" + "world" + end sub + `); }); it('skips calling toString on strings', () => { testTranspile(` - text = \`Hello \${"world"}\` + sub main() + text = \`Hello \${"world"}\` + end sub `, ` - text = "Hello " + "world" + sub main() + text = "Hello " + "world" + end sub `); }); diff --git a/src/parser/tests/expression/TernaryExpression.spec.ts b/src/parser/tests/expression/TernaryExpression.spec.ts index 32d7d7c45..8ef777bde 100644 --- a/src/parser/tests/expression/TernaryExpression.spec.ts +++ b/src/parser/tests/expression/TernaryExpression.spec.ts @@ -405,10 +405,15 @@ describe('ternary expressions', () => { }); it('complex conditions do not cause scope capture', () => { - testTranspile( - `a = str("true") = "true" ? true : false `, - `a = bslib_ternary(str("true") = "true", true, false)` - ); + testTranspile(` + sub main() + a = str("true") = "true" ? true : false + end sub + `, ` + sub main() + a = bslib_ternary(str("true") = "true", true, false) + end sub + `); testTranspile(` sub main() @@ -525,18 +530,22 @@ describe('ternary expressions', () => { it('uses scope capture for property access', () => { testTranspile( ` - person = {} - name = person <> invalid ? person.name : "John Doe" - `, + sub main() + person = {} + name = person <> invalid ? person.name : "John Doe" + end sub + `, ` - person = {} - name = (function(__bsCondition, person) - if __bsCondition then - return person.name - else - return "John Doe" - end if - end function)(person <> invalid, person) + sub main() + person = {} + name = (function(__bsCondition, person) + if __bsCondition then + return person.name + else + return "John Doe" + end if + end function)(person <> invalid, person) + end sub ` ); }); diff --git a/src/parser/tests/statement/PrintStatement.spec.ts b/src/parser/tests/statement/PrintStatement.spec.ts index 762568799..54b90b6b6 100644 --- a/src/parser/tests/statement/PrintStatement.spec.ts +++ b/src/parser/tests/statement/PrintStatement.spec.ts @@ -108,15 +108,19 @@ describe('parser print statements', () => { describe('transpile', () => { it('retains comma separators', () => { testTranspile(` - a$ = "string" - print a$, a$, a$ + sub main() + a$ = "string" + print a$, a$, a$ + end sub `); }); it('retains semicolon separators', () => { testTranspile(` - a$ = "string" - print a$; a$; a$ + sub main() + a$ = "string" + print a$; a$; a$ + end sub `); }); @@ -125,71 +129,82 @@ describe('parser print statements', () => { function getText() return "text" end function - print getText() getText() getText() + + function main() + print getText() getText() getText() + end function `); }); it('supports print in loop', () => { testTranspile(` - paramArr = ["This", "is", true, "and", "this", "is", 1] - print "This is one line of stuff:"; - for each item in paramArr - print item; " "; - end for - print "" + sub main() + paramArr = ["This", "is", true, "and", "this", "is", 1] + print "This is one line of stuff:"; + for each item in paramArr + print item; " "; + end for + print "" + end sub `, ` - paramArr = [ - "This" - "is" - true - "and" - "this" - "is" - 1 - ] - print "This is one line of stuff:"; - for each item in paramArr - print item; " "; - end for - print "" + sub main() + paramArr = [ + "This" + "is" + true + "and" + "this" + "is" + 1 + ] + print "This is one line of stuff:"; + for each item in paramArr + print item; " "; + end for + print "" + end sub `); }); it('handles roku documentation examples', () => { testTranspile(` - x=5:print 25; " is equal to"; x^2 - a$="string":print a$;a$,a$;" ";a$ - print "zone 1","zone 2","zone 3","zone 4" - print "print statement #1 ":print "print statement #2" - print "this is a five " 5 "!!" - print {} - print {a:1} - print [] - print [5] - print tab(5)"tabbed 5";tab(25)"tabbed 25" - print tab(40) pos(0) 'prints 40 at position 40 - print "these" tab(pos(0)+5)"words" tab(pos(0)+5)"are":print tab(pos(0)+5)"evenly" tab(pos(0)+5)"spaced" + sub main() + x=5:print 25; " is equal to"; x^2 + a$="string":print a$;a$,a$;" ";a$ + print "zone 1","zone 2","zone 3","zone 4" + print "print statement #1 ":print "print statement #2" + print "this is a five " 5 "!!" + print {} + print {a:1} + print [] + print [5] + print tab(5)"tabbed 5";tab(25)"tabbed 25" + print tab(40) pos(0) 'prints 40 at position 40 + print "these" tab(pos(0)+5)"words" tab(pos(0)+5)"are":print tab(pos(0)+5)"evenly" tab(pos(0)+5)"spaced" + end sub `, ` - x = 5 - print 25; " is equal to"; x ^ 2 - a$ = "string" - print a$; a$, a$; " "; a$ - print "zone 1", "zone 2", "zone 3", "zone 4" - print "print statement #1 " - print "print statement #2" - print "this is a five " 5 "!!" - print {} - print { - a: 1 - } - print [] - print [ - 5 - ] - print tab(5) "tabbed 5"; tab(25) "tabbed 25" - print tab(40) pos(0) 'prints 40 at position 40 - print "these" tab(pos(0) + 5) "words" tab(pos(0) + 5) "are" - print tab(pos(0) + 5) "evenly" tab(pos(0) + 5) "spaced" + sub main() + x = 5 + print 25; " is equal to"; x ^ 2 + a$ = "string" + print a$; a$, a$; " "; a$ + print "zone 1", "zone 2", "zone 3", "zone 4" + print "print statement #1 " + print "print statement #2" + print "this is a five " 5 "!!" + print {} + print { + a: 1 + } + print [] + print [ + 5 + ] + print tab(5) "tabbed 5"; tab(25) "tabbed 25" + print tab(40) pos(0) 'prints 40 at position 40 + print "these" tab(pos(0) + 5) "words" tab(pos(0) + 5) "are" + print tab(pos(0) + 5) "evenly" tab(pos(0) + 5) "spaced" + end sub `); }); });