rokucommunity / brs Goto Github PK
View Code? Open in Web Editor NEWThis project forked from sjbarag/brs
An interpreter for the BrightScript language that runs on non-Roku platforms.
License: MIT License
This project forked from sjbarag/brs
An interpreter for the BrightScript language that runs on non-Roku platforms.
License: MIT License
As Roku has a unique and limited localization support, brs needs to emulate it somehow.
For instance, currently in roDateTime
the only supported formats for short date are:
Add also support for the "locale" folder and the object roLocalization
The following code generates an error in brs
and in Roku works ok.
a% = 126.9
print a%
In Roku it prints 126
Interface:
https://developer.roku.com/docs/references/brightscript/interfaces/iflist.md
Good explanation about the roList and it's differences to roArray:
https://forums.roku.com/viewtopic.php?t=66904
From the community slack by @TwitchBronBron :
The docs for FormatJson(json as Object, flags = 0 as Integer) as String is missing information on some of the flags. For example, flag 256 which converts non-serializable values into null rather than printing a parse error and returning invalid.
Could we get those docs updated with all the supported flags?
https://developer.roku.com/en-ca/docs/references/brightscript/language/global-utility-functions.md#formatjsonjson-as-object-flags--0-as-integer-as-string
Quote from Slack by @unwiredben
The undocumented flags for FormatJson are 256 - UnsupportedIgnore and 512 - UnsupportedAnnotate - these control what happens when an object in the AA isn't something that can be represented by JSON, like a roWhatever instance. Ignore just has it output as a null, while Annotate makes it a JSON string of the format "".
RBI automatically boxes function parameters and return values. As it turns out, RBI also unboxes function parameters and return values to match a function's declared signature. Consider this example:
sub main()
boxed = createObject("roString")
boxed.setString("lorem ipsum")
print "type(boxed) = " type(boxed) " | boxed = " boxed
unboxed = unboxing(boxed)
print "type(unboxed) = " type(unboxed) " | unboxed = " unboxed
end sub
sub unboxing(s as string) as string
print "type(s) = " type(s) " | s = " s
return s
end sub
Inside of the unboxing
function, s
is always a primitive string
and never an instance of roString
. Similarly, the return value of unboxing
is also always a primitive string
and never an instance of roString
.
brs
currently doesn't handle this demotion properly, and throws type mismatch errors at runtime ๐ข
Documentation is here:
This implementation is dependent on #4
Roku supports multi-dimensional array indexes for roArray
and roList
' three dimensional array
a = [[1,2,["a","b","c"]], [4,5,["d","e","f"]], [7,8,["g","h","i"]]]
print a[1][2][2]'==> f
print a[1,2][2] '==> f
print a[1][2,2] '==> f
print a[1,2,2] '==> f
print a[1,2,3] '==> invalid
'Assign with multi-index
a[2, 1] = true
print a[2, 1] '==> true
This interface has two methods and is implemented by roArray
and roByteArray
:
RBI supports bitwise negation of integers (and possibly floating-point/double values; needs confirmation), but brs currently doesn't. Let's make that work! Some details from a comment on a PR:
' in RBI
print not 0 ' => -1
print not 1 ' => -2
print not -1 ' => 0
print not 2 ' => -3
print not -2 ' => 1
which looks like RBI is using a two's complement representation for numbers and does something slightly unexpected with it. not someInteger
in RBI seems to take someInteger
, convert to a binary representation, perform a bitwise negation on it (all 0s become 1s, all 1s become 0s), then convert back to a decimal representation in the Two's complement understanding. So for not 0
(using just 4-bits for now because I'm lazy), we have:
not 0 (decimal) == not 0b0000 == 0b1111 == -1 (decimal in two's complement)
How strange! In the not 2 * 3
example, we get:
not (2 * 3) == not 6 (decimal) == not 0b0110 == 0b1001 == -7 (decimal in two's complement)
In the line below, the method TotalSeconds
is called 4 times (to check add a console.log
in the implementations):
? CreateObject("roTimeSpan").TotalSeconds().ToStr().Trim()
User @strattonbrazil also reported:
This is also happening for similar chained calls such as:
" 3 ".trim().toInt()
where trim() is called twice. I also found this has always behaved this way and went unnoticed since most of the chained functions had little to no side effects and only their values were used in testing. I also confirmed that this seems specific to function chaining and other cases like 1 + 2 + 3 have the appropriate number of addition operations.
Using the example above, it appears the trim() call is evaluated once in Interpreter.visitDottedGet() and evaluated in Interpreter.visitCall(). While this has some performance concerns, this can certainly introduce more problems when more functions with side effects are introduced. I'll start looking at some options. I'm guessing the Interpreter.visitCall() is the appropriate place to do this evaluation and Interpreter.visitDottedGet() needs to be updated.
value = 1.2.5
will give the following error Expected newline or ':' after assignment
even though it's valid brightscript code. The last part gets clipped off so wouldn't want to use this likely but also shouldn't give an error either. Same with:
value = {
"test": 1.3.1.4
}
Gives `Unmatched '{' - expected '}' after associative array literal``
The Roku createObject() method for creating arrays supports a boolean for whether the array is resizable or not. This currently needs to be implemented in brs.
from roArray doc:
CreateObject("roArray", size As Integer, resizeAs Boolean)
This function was discovered by the community and was discussed here:
https://community.roku.com/t5/Roku-Developer-Program/objFun-no-fun/td-p/306374
a valid code is this:
di = createObject("roDeviceInfo")
iface = getInterface(di, "ifDeviceInfo")
print objFun(di, iface, "getModel")
print objFun(di, iface, "canDecodeVideo", {codec: "mpeg2"}).result
It appears syntax like #if NOT ENABLE_FEATURE
is valid during conditional compilation in the reference BrightScript implementation, but not in brs
. Fixing that will require the conditional compilation processor to get a bit more intelligent, but hopefully it's not a major change.
Originally reported by @triwav !
The code below exemplifies the issue, when invalid
is used on the left side of the test it is not boxed in roInvalid
so is not correctly compared with the parameter (that was boxed because of the object
type on the argument).
sub main()
myVar = invalid
RightInvalid(myVar)
LeftInvalid(myVar)
end sub
sub RightInvalid(param1 as object)
if param1 <> invalid
param1.id = 1
else
print "param invalid"
end if
end sub
sub LeftInvalid(param1 as object)
if invalid <> param1
param1.id = 1
else
print "param invalid"
end if
end sub
The code was supposed to print param invalid
in two consecutive lines, instead it shows the error below, because the comparison cannot say invalid
is not different than roInvalid
:
param invalid
left-invalid-error.brs(17,12-14): Attempting to set property on non-iterable value
left: Object
sub main()
a = { bolFalse: false, bolTrue: true, intFalse: 0, intTrue: 1 }
print a
print "Positive Test Bool"
if a.bolFalse then print "True" else print "False"
if a.bolTrue then print "True" else print "False"
print "Positive Test Int"
if a.intFalse then print "True" else print "False"
if a.intTrue then print "True" else print "False"
print "Negative Test Bool"
if not a.bolFalse then print "True" else print "False"
if not a.bolTrue then print "True" else print "False"
print "Negative Test Int"
if not a.intFalse then print "True" else print "False"
if not a.intTrue then print "True" else print "False"
end sub
The actual ROKU output is:
<Component: roAssociativeArray> =
{
bolfalse: false
boltrue: true
intfalse: 0
inttrue: 1
}
Positive Test Bool
False
True
Positive Test Int
False
True
Negative Test Bool
True
False
Negative Test Int
True
True
And the current BRS output is:
<Component: roAssociativeArray> =
{
bolFalse: false
bolTrue: true
intFalse: 0
intTrue: 1
}
Positive Test Bool
False
True
Positive Test Int
False
False
Negative Test Bool
True
False
Negative Test Int
/Users/cabralmb/Repos/brs-emu/test/emulator/if-integer-test.brs(14,7-10): Attempting to NOT non-boolean value.
value type: Integer
After the PR #21 the code below stopped working, the ?
on a single line if
with no then
function testOneLineIf()
testA = ["apple", 2, "banana", "orange", 5, "grape", "pear"]
for fruit = 0 to testA.count()-1
if type(testA[fruit]).inStr(0,"String") >=0 ? testA[fruit]
? "item="+testA[fruit].toStr()+" index="+fruit.toStr()
next
end function
Now this give an error:
Expected statement or function call, but received an expression
Might I suggest using a package like `undent` to allow these tests to read a little nicer? We do this a lot in brighterscript-formatter. (see [here](https://github.com/rokucommunity/brighterscript-formatter/blob/b634f670920cd7a7d78e8cede8a4f0e62e7871b4/src/Formatter.spec.ts#L18-L41)).
So your test would look something like this:
it("lists values", () => {
let arr = new RoByteArray(new Uint8Array([1, 2, 3, 4, 5]));
expect(arr.toString()).toEqual(undent`
<Component: roByteArray> =
[
1
2
3
4
5
]
`);
});
I feel like it improves the readability of the tests. Maybe that could be a separate PR though to hit all the spots that do tests like this? Anyway, just a thought.
Originally posted by @TwitchBronBron in #53 (comment)
The code below:
print FindMemberFunction({}, "Count")
print GetInterface({}, "ifenum")
print FindMemberFunction("", "left")
print GetInterface("", "ifStringOps")
print FindMemberFunction(1, "tostr")
print GetInterface(1, "iftostr")
should display:
<Interface: ifAssociativeArray>
<Interface: ifEnum>
<Interface: ifStringOps>
<Interface: ifStringOps>
<Interface: ifIntOps>
<Interface: ifToStr>
Reference: https://developer.roku.com/en-gb/docs/references/brightscript/language/global-utility-functions.md
The print (?) statement when used in telnet do print a leading space on every positive number (unless you use .toStr() method) , negative number shows minus on the leading position.
For example, if you execute:
print "testing" 123
' In Roku it shows:
testing 123
' In brs it shows:
testing123
For example this case is not working as in Roku:
ts = CreateObject("roTimeSpan")
print "Return seconds from date until now: " ts.getsecondstoiso8601date("2030-11-10T")
This code:
color = &HC0C0C0FF
print color
Shows in Roku console as: -1061109505
Shows in brs console as: 3233857791
Quote by RokuKC from the forum (link below)
here are some somewhat subtle issues with respect to specifying integer constants and how type conversions and limit checks are performed.
Hex constants can represent a full 32-bit unsigned range of integers (0 .. &hFFFFFFFF - 4,294,967,295), as overlaid on a 32-bit signed integer variable. Values outside that range get clipped to 32-bits.
Integer constants should represent a full 32-bit signed range, but the current behavior is to only represent integer -999,999,999 .. 999,999,999. Values outside that range get implicitly promoted to type Double.
However, if you pass that Double value to an integer-typed parameter, or assign it to an integer-typed variable, it goes through a narrowing conversion back to signed integer. The value is out of range and in this case happens to be converted to an out of range value &h80000000, which happens to look like a reddish color value.
In the documentation several field types are listed that are not yet supported on brs
, for example the typed arrays.
Code at: src/brsTypes/BrsType.ts
getValueKindFromFieldType()
getBrsValueFromFieldType
Even if both numbers are float
or double
the result should return the trucated integer in Roku.
Example below:
'In Roku
? type(7.6 mod 3.0)
Float
? 7.6 mod 3.0
1
'In BRS
? type(7.6 mod 3.0)
Float
? 7.6 mod 3
1.6
Here's a pull request in BrighterScript that increases the performance of the lexer by 5-20% by re-ordering the items in the scan()
switch statement. You might want to consider doing the same in brs
.
Original issue by @TwitchBronBron at sjbarag#456
After the fix for issue #9 a side effect happened, sometimes the anonymous functions are getting the root m
instead of the context of the parent object. Below a code to reproduce the issue:
sub main()
m.name = "root"
level1 = {name: "level1", param: "test result was: "}
level1.run = sub()
print m.name
print createLevel2().testResult(m.param)
end sub
level1.run()
end sub
function createLevel2()
instance = __level2_builder()
instance.new()
return instance
end function
function __level2_builder()
print m.name
instance = {name: "level2"}
instance.new = sub()
print m.name
m.name = "newName"
end sub
instance.testResult = function(param) as string
if m.name = "newName"
result = "success"
else
result = "fail"
end if
return param + result
end function
return instance
end function
This code must print test result was: success
but in the current version 0.45.3 it shows test result was: fail
Two issues found, first non numeric numbers are returning NaN
instead of 0
and the function should recognize the prefix 0x
for hexadecimals.
Running this code:
print val("1")
print val("0xFF")
print val("AA", 16)
print val("notanumber")
In Roku we get:
1
255
170
0
With brs
we get:
1
0
170
NaN
This was introduced by this pull request
Try the code below:
Sub Main()
x = 96
y = 56
w = 1088
h = 608
Offset(-x + 96, -y + 56, -w + 1088, -h + 608)
End Sub
Sub Offset(x, y, w, h)
print x, y, w, h
End Sub
This should print:
0 0 0 0
instead it prints:
-192 -112 -2176 -1216
It is wrong for any case with negative number first, like the example below:
Off-Roku BrightScript interpreter [Version 0.44.0]
brs> print -1000 + 1000
-2000
brs> print +1000 - 1000
0
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.