rokucommunity / brighterscript Goto Github PK
View Code? Open in Web Editor NEWA superset of Roku's BrightScript language
License: MIT License
A superset of Roku's BrightScript language
License: MIT License
We should validate these translation files from within brighterscript since they are formally supported by Roku.
https://developer.roku.com/en-ca/docs/developer-program/core-concepts/localization.md
In case a XML Scenegraph component includes a script file and all the functions in that script file, are not used in the object - flag it.
This can help reduce unnecessary bloat.
This is a proposal for classes. Here is the brighterscript syntax:
class VideoPlayer
'public constructor
public sub New(url as string)
m.url = url
end sub
'properties
public url as string
private isPlaying as boolean = false
'public method
public play()
m.isPlaying = true
m.updateVideoStatus()
end function
'private method
private updateVideoStatus()
'do something private here
end sub
end class
function VideoPlayer(url as string)
instance = {
url: nothing,
'underscore denotes private
_isPlaying: nothing,
play: VideoPlayer_play,
_updateVideoStatus: VideoPlayer__updateVideoStatus
}
'constructor body
instance.firstName = firstName
instance.lastName = lastName
instance.fullName = firstName + " " + lastName
return instance
end function
function VideoPlayer_play()
m.isPlaying = true
m._updateVideoStatus()
end function
'double underscore (one for namespacing, the other for indicating private
function VideoPlayer__updateVideoStatus()
'do something private here
end sub
'usage
sub main()
player = new VideoPlayer("some_url")
player.play()
end sub
sub main()
player = VideoPlayer("some_url")
player.play()
end sub
Forgive me if this is too general of an issue and may not be true in all situations but this is just what I'm seeing in this particular codebase I'm working on. (NOTE: I'm very very new to Roku dev so forgive me if any of this looks silly haha)
' Constants.brs
' ...
sub Init()
' ...
m.top.something = "something"
' ...
end sub
' ...
' SomeFile.brs
' ...
sub Init()
' ...
m.c = m.top.createChild("Constants")
' ...
end sub
' ...
This is a proposal for namespaces.
'simple
namespace App
function GetSettings()
end function
end namespace
'multi-part
namespace App.UI.Screens
function MainScreen()
end function
end namespace
'nested
namespace App
namespace Util
namespace Functions
function GetSomething()
end function
end namespace
end namespace
end namespace
function App_GetSettings()
end function
function App_UI_Screens_MainScreen()
end function
function App_Util_Functions_GetSomething()
end function
#Usage
sub main()
App.GetSettings()
App.UI.Screens.MainScreen()
App.Util.Functions.GetSomething()
end sub
sub main()
App_GetSettings()
App_UI_Screens_MainScreen()
App_Util_Functions_GetSomething()
end sub
we could perhaps/maybe/theoretically create a shadow project, in typescript, with types that match the brightscript project's types, and use that to ascertain all of our type/scope errors..
point being, use typescripts type inference as a proxy, rather than having to write it all for brighterscript.
if (true) then print "Truthy!"
if ({}) then print "Truthy!"
if ([]) then print "Truthy!"
if ("some string") then print "Truthy!"
if (3.14) then print "Truthy!"
if (new Date()) then print "Truthy!"
if (false) print "Falsy."
if (muUndefinedVar) print "Falsy."
if (Invalid) print "Falsy."
if (0) print "Falsy."
if ("") print "Falsy."
given TodoVM.bs
class TodoVM extends BaseViewModel
where BaseViewModel
is a library that I import in my project with compiled sources (i.e. BaseViewModel.brs
), how does the compiler know the extend is valid?
I have this currently in my project, so I'm distributing both compiled and uncompiled sources.
I'm also thinking of adding options to turn the class BaseViewModel
not found error into a warning; but it got me thinking...
is there some lightweight metadata we can bundle in comments at the bottom of a file, or something similar to d.ts
file, which the compiler can read, to not lose important information when encountering compiled sources.
When generating the brightscript output, brighterscript should also produce sourcemaps so that debuggers can map output line/column numbers to source line/column numbers.
switch something
case somethingElse1:
' ...
case somethingElse2:
' ...
case somethingElse3:
' ...
default:
' ...
end switch
should transpile to:
if something = somethingElse1 then
' ...
else if something = somethingElse2 then
' ...
else if something = somethingElse3 then
' ...
else
' ...
end if
may because of the if else
, the break
statement is implied, otherwise it might be transpiled with goto
statements to support fallthrough.
if something = somethingElse1 then
goto somethingElse1Label
else if ...
somethingElse1Label:
'...
This causes parse errors:
obj = {
"name": true,
'comment
}
this seems a good fit for the language
e.g.
With theCustomer
.Name = "Coho Vineyard"
.URL = "http://www.cohovineyard.com/"
.City = "Redmond"
End With
This will become
theCustomer.Name = "Coho Vineyard"
theCustomer.URL = "http://www.cohovineyard.com/"
theCustomer.City = "Redmond"
would be great to not have to do
aa[currentIndex as string] = thing
instead of
if currentIndex <> invalid
text = str(currentIndex).trim()
aa[text] = thing
end if
This is valid BrightScript syntax (see this BrightScript documentation page for info):
xml = CreateObject("roXMLElement")
xml.parse("<?xml version=""1.0"" encoding=""UTF-8""?><person name=""bob"" age=""12""></person>")
print "xml age: "
print xml@age 'notice the @ symbol being used to read the attribute from the xml element
prints out
xml age:
12
However, the brighterscript cli shows a parse error.
For the following example:
sub doSomething()
Dim c[5, 4, 6]
For x = 1 To 5
For y = 1 To 4
For z = 1 To 6
c[x, y, z] = k
k = k + 1
End for
End for
End for
end sub
Taken from Roku documentation found: here
the Dim statement is flagged as error 1081 ("Found enexpected token 'Dim'") and appears to be unsupported.
Like we have in javascript, support for this would be very covenant.
When declaring a class field, it should allow you to declare their initial values
for example:
class Person
public name = "Bob"
public age = 12
end class
myValue = (false) ? "value1" : "value2"
print myValue
' Output: value2
It can get very difficult sometimes to keep track of what files depend on one another. Many other programming languages have the concept of imports, and this specification brings that concept to brighterscript.
import "pkg:/source/lib.brs"
import "../lib.brs"
import "lib.brs"
For source scope (i.e. pkg:/source
and everything below), an import statement will cause that file to be included in pkg:/source
folder during packaging.
For component scope, any component that references a script that has imports, those imports will be added to the component's script imports. For example, consider these source files:
src/components/MyComponent.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="MyComponent" extends="Scene">
<script type="text/brightscript" uri="MyComponent.brs" />
</component>
src/components/MyComponent.brs
import "pkg:/source/lib.brs"
function DoSomething()
end function
This will result in the output files as such:
pkg:/components/MyComponent.xml
<?xml version="1.0" encoding="utf-8" ?>
<component name="MyComponent" extends="Scene">
<script type="text/brightscript" uri="MyComponent.brs" />
<script type="text/brightscript" uri="pkg:/source/lib.brs" />
</component>
pkg:/components/MyComponent.brs
function DoSomething()
end function
When running the cli bsc
, it should return a nonzero code so that tools can properly handle parse errors.
It'd be wonderful to not have to use callfunc anymore
given a node with function foo, I want to be able to do
node.foo, instead of node.callFunc("foo"
I also want to be able to call it like a regular function without having to pass a single args param, i.e.
node.foo(title, video, someOtherThing)
as opposed to node.callFunc("foo", {title:title, video:video, someOtherThing"..etc)
an idea to achieve this, without having to need full-bulletproof type checking implemented is something like this
.bs
defined functions that map to an xml file's node's public interface functions result in the creation of a new function (e.g. foo_public) and the xml interface's definition will use that for function calls - the code for that destructures args to the fieldnames in the real method signature, and invokes that methodThe latest version of brighterscript was failing to detect the following as a compile issue:
if key = "back" AND then
end if
Here are some other examples that it also failed to detect:
if key = "back" OR then
end if
if key = "back" MOD then
end if
So just learned that a node doesn't have to extend from another node type as it appears to fall back to using Group by default. This should not be reported as an error like it currently is Component must have an extends attribute.brs(1007)
Preprocessor statements such as this:
#if not production
? "Debug Version"
#else
? "Production Version"
#end if
Give a 1091 error ("#if conditionals can only be 'true', 'false', or other #const names"). This doesn't seem to be the case for vanilla BrightScript as can be seen in the example in the Roku documentation:
Conditional Compilation - Roku Documentation
Unfortunately the Roku docs don't expand on which operators are permitted. It's probably relatively safe to assume that the logical operators (AND, OR, NOT) are OK, but what about others? The code shown above works fine on a Roku device.
we could theoretically have code like
function myFoo(arg1 as node, arg2 as string, arg3 as Group, arg4 as myNameSpace.NetModule
and the transpiler/thingy could inject a line of code below the function definition to ensure the runtime type.
I propose that we support async/await semantics in brighterscript.
example:
private async function login()
m.account = await m.getAccount(m.nameInput.text, m.passwordInput.text
if m.account
m.username.label.text = m.account.name
else
m.username.label.text = "ERROR"
end if
end function
private async function getAccount(name as string, password as string)
token = await m.getToken(name, password)
user = await m.getUserAccount(token)
return user <> invalid ? user.account : invalid
end function
private async function getToken(name as string, password as string)
'returns a promise, as per roku-promise (i.e a creates a task with an out field)
'e.g.
task = createObject("roSGNode", "UserTask")
task.command = "getToken"
task.name = name
task.password = password
task.control = "RUN"
return task
end function
private async function getUserAccount(token as string)
'returns a promise, as per roku-promise (i.e a task with an out field)
task = createObject("roSGNOde", "UserTask")
task.command = "getAccount"
task.token = token
task.control = "RUN"
return task
end function
Similar to class, we should allow interface. this will allow us to get to a goal where the ide will have increasing information about types.
interface would produce nothing in the brs code - the benefit of the type declaration would be entirely for the ide's symbol and definition resolution and the compiler.
example
interface styleInfo
selectedColor: string
color: string
fontSize : integer
end struct
public function someMethod(buttonStyle: StyleInfo)
button.applyStyle(styleInfo)
end function
output is
public function someMethod(buttonStyle: StyleInfo)
button.applyStyle(buttonStyle)
end function
the interface would simply vanish at runtime; merely providing the ide with more type info. This does of course require that we have more advanced typing, which i would dearly love to see, whereby we can have as default values (object|integer|dynamic) as well as new specific types for our classes and interface (StyleInfo|MyButton|UserInfo|FancyButton), whereby the types here are the class or node names.
wouldn't it be great if we could drop these into the code at any time, like macros, so that they could be resolved at compile time. These are super handy for debugging, and currently require the use of third party tools.
Suggestion
print "unknown error occurred " ; BS_FILE() ; "." BS_FUNCTION() ; BS_LINENUM()
Would compile to
print "unknown error occurred pkg:/path/ToFile.brs.LoadStuff(23)"
super useful statement, get's rid of need for huge if statements
can easily be implemented under the hood by injecting gotos
example code
for i = 0 to 10
item = items[i]
if item.isLocked
continue
end if
doSomeStuffwithItem(item)
end for
will produce code:
for i = 0 to 10
item = items[i]
if item.isLocked
goto namespace_class_method_continue1
end if
doSomeStuffwithItem(item)
namespace_class_method_continue1:
end for
note, I suggest we namespace the continue label with namespace_class_method_continue_index, as we could have multiple continues in one method, or even one loop.
it might be we just namespace on per file indexes though (i.e. take filename, and each continue that appears in the file has an index that increments each time a continue is encountered)
if a parent class has a constructor function, and a child class has a constructor function, then the child class must call super()
as the first statement in its constructor function
Is there anyway we could do classes ether as AA out put or as Node output. I say this because internally we avoid AA's as much as possible.
this is a good suggestion - so something like
NodeClass
? whereby
- automatcially creates a className.xml,
- imports the declaring brs file into className.xml
- the public methods become
<function>
interface entries in className.xml- public fields become
<field>
entries on className.xmlThere's a lot in this idea - I think you should raise it as a separate issue - as it's a hugely useful; but also huge feature; but (imho) way beyond the scope of pobo (i.e. aa) classes
The above was a response from @georgejecook
Originally posted by @chrisdp in #1 (comment)
would be great to have a type keyword that could return the brightscript or brighterscript type of an object. So if it's a brighterscript class, you'll get className, if it's a node/primitive, you'll get the default type
I'm already starting to need this issue in my bs code.. would be lovely if, given a class
class BaseClass
public function load()
that the extended class
class SpecificThingLoader
public function load()
will throw a compiler error.
The syntax in this case should be
override public function load()
I'm not suggesting we support super classes at this point; though I may in future; but truth be told, I've only ever had to swizzle an aa for a superclass access less than a handful of times.
Similar to Type Declaration Characters such as $
(String), %
(Integer), !
(Float), #
(Double), &
(LongInteger), which don't allow assignments of invalid
, we could make the language null-safe by never allowing assignments of invalid unless the var name ends with ?
by creating a runtime error.
Like in Swift, this value could be an associative array with two possible values in it, none
or some
, with the associated value. (Swift does that with an Enum
). To assign an optional to a non-optional, an unwrapping function is needed which releases the some
value or provides an alternative if none
is encountered.
an implements
tag in an associative array could make the transpiler to check for the validity of the associative array against the interface.
for each outline in feedXml.body.outline
' ...
display = ValidStr(outline@display) ' throws error BS1000: Unexpected character '@'
' ...
end for
Any tips on how to re-write this?
We should provide full intellisense, parsing and validation for manifest files.
When looking for a property value that's deep in a tree-like structure, one often has to check whether intermediate nodes exist:
if user.address AND user.address.street then
street = user.address.street
end if
Also, many API return either an object or Invalid, and one may want to extract a property from the result only when it is not Invalid:
fooInput = m.top.findNode('fooIinput')
if Invalid <> fooInput then
fooValue = fooInput.value
end if
The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:
street = user.address?.street
fooValue = m.top.findNode('fooInput')?.value
My examples above are just tweaks to the one on this repo: https://github.com/tc39/proposal-optional-chaining
XML files found outside of the pkg:/components/
folder should be excluded from parsing/validating.
Roku will throw exceptions for any non-component-structured xml file in the components folder.
<?xml version="1.0" encoding="utf-8" ?>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend! </body>
</note>
error:
=================================================================
Found 9 parse errors in XML file test.xml
--- Line 2: No top level <component> element found
--- Line 3: No top level <component> element found
--- Line 3: Unexpected data found in file (first 10 characters are "Tove")
--- Line 4: No top level <component> element found
--- Line 4: Unexpected data found in file (first 10 characters are "Jani")
--- Line 5: No top level <component> element found
--- Line 5: Unexpected data found in file (first 10 characters are "Reminder")
--- Line 6: No top level <component> element found
--- Line 6: Unexpected data found in file (first 10 characters are "Don't forg")
Operating System: Fedora 30
VS Code: 1.44.0
BRS Extension: 2.1.2
I am getting red squiggle lines under code which is actually correct. It was on a huge non-SG screensaver main.brs
, but I can reproduce it at will with the following steps.
$ mkdir test
$ cd test
$ code .
Open a new Untitled file, and then type or paste the following code:
Function RunScreenSaver( params As Object ) As Object
main()
End Function
sub main()
print "Entering main()..."
m.screen = CreateObject("roScreen") ' Required object
m.port = CreateObject("roMessagePort") ' Required object
di = CreateObject("roDeviceInfo") ' For resolution info
timeStamp = CreateObject("roDateTime") ' For elapsed time
i% = 1
i% = i% << 3
i% = i% >> 3
for j = 1 to 10
print j
next j
for k = 1 to 10
print k
end for
end sub
All should look well, until you hit <CTRL-S>
and save as main.brs.
After writing to disk, the red marks appear, as shown in the attached screenshot below.
There was a red squiggle under the "j" in "next j" but I upgraded the version of VS Code and the extension this morning and I can no longer reproduce that.
My screensaver compiles and runs fine even with the squigle marks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.