Giter Site home page Giter Site logo

rokucommunity / brs Goto Github PK

View Code? Open in Web Editor NEW

This project forked from sjbarag/brs

4.0 4.0 2.0 3.67 MB

An interpreter for the BrightScript language that runs on non-Roku platforms.

License: MIT License

Shell 0.01% JavaScript 54.45% TypeScript 40.26% Brightscript 5.28%

brs's People

Contributors

alimnios72 avatar darktasevski avatar dependabot[bot] avatar dru89 avatar elsassph avatar gincwang avatar heftyfunseeker avatar jessicacaley avatar jweeber avatar jwfearn avatar karthick-sh avatar kirkyoder avatar lkipke avatar lvcabral avatar markwpearce avatar matatat avatar nadiapadalka avatar niveditasonker avatar philmein23 avatar sjbarag avatar slushy avatar strattonbrazil avatar twitchbronbron avatar underwoo16 avatar vasya-m avatar vbuchii avatar williamsmartinezglb avatar ystarangl avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

brs's Issues

Implement localization support

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:

  • Canada: YYYY/MM/DD
  • United States: MM/DD/YY
  • UK and rest of world: DD/MM/YY

Add also support for the "locale" folder and the object roLocalization

Make `if` behavior when using numeric variables consistent with Roku

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

Side effect issue with Optional Chaining implementation

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

Wrong precedence for negative sign in front of a value on a expression

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

Set `roArray` resizable state on `CreateObject`

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)

Use a package like `undent` to allow tests with lists to read a little nicer

          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)

Negation in Conditional Compilation is not supported

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 !

Add support for `formatJson()` undocumented flags 256 and 512

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 "".

Method chain calls causes Interpreter to trigger the methods multiple times

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.

Roku handles hexadecimal as signed integer

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.

https://forums.roku.com/viewtopic.php?f=34&t=84652

Numbers with more than one decimal point raise error

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``

String function `val()` not behaving as in a Roku device

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

Invalid check when on the left side does not work (boxing issue)

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

`brs` doesn't automatically unbox parameters and return values

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 ๐Ÿ˜ข

Implement support for Multi-Dimensional Array access

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

Implement bitwise negation on numeric types

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)

Complex nested objects with anonymous functions access wrong object `m`

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

Print statement do not print positive numbers with leading space

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

Global functions `GetInterface()` and `FindMemberFunction()` are not properly boxing parameters

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.