Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement component roList #688

Closed
wants to merge 37 commits into from
Closed
Changes from 4 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
d786413
refactor in preparation for adoption the project
TwitchBronBron Apr 19, 2023
fbaaed7
fix build script name
TwitchBronBron Apr 19, 2023
2ccec94
install node modules first
TwitchBronBron Apr 19, 2023
53c1aeb
break out tasks to view nicer failure spot
TwitchBronBron Apr 19, 2023
817c651
Add coverage reporting
TwitchBronBron Apr 19, 2023
e31226b
Actually add coverage reporting
TwitchBronBron Apr 19, 2023
b2d223a
fix coverage reporting
TwitchBronBron Apr 19, 2023
f1546e3
Merge pull request #1 from rokucommunity/adoption
TwitchBronBron Apr 24, 2023
94304cd
Fixed `val()` edge cases: hex without radix and `NaN`
lvcabral Sep 7, 2023
a223c2a
Fixed prettier and replaced deprecated `substr()` method
lvcabral Sep 7, 2023
ec82a06
Update README.md
TwitchBronBron Sep 8, 2023
afca557
Merge pull request #3 from rokucommunity/bugfix/string-val-function-n…
lvcabral Sep 8, 2023
f72f2dc
Add changelog
TwitchBronBron Sep 8, 2023
af0c223
Add note about the project fork.
TwitchBronBron Sep 8, 2023
202cc9f
Make tests import relative path to lib/index.js
TwitchBronBron Sep 8, 2023
ab5322a
0.45.1
TwitchBronBron Sep 9, 2023
7569e86
add logic for optional chaining (#21)
nadiapadalka Oct 25, 2023
e6c98ae
fix(interp): Preventing multiple calls for dot-chained methods (#22)
lvcabral Oct 31, 2023
98dcee2
fix(parser): Wrong negative sign precedence was causing math errors (…
lvcabral Nov 1, 2023
d8878a9
update changelog for v0.45.2
TwitchBronBron Nov 8, 2023
7ef68bb
0.45.2
TwitchBronBron Nov 8, 2023
5f13b59
fix(components): Replacing package luxon by day.js on `roDateTime` an…
lvcabral Nov 21, 2023
e98151c
fix(parser,lexer) Optional chaining implementation side effect #30 (#31)
lvcabral Dec 1, 2023
6e5b422
feat(components): Implemented missing `ifEnum` methods in `roArray` a…
lvcabral Dec 1, 2023
b8e459a
feat(lex,parse): Add stub try/catch implementation (#34)
lvcabral Dec 1, 2023
a5e0217
Adds create-package CI build for quicker iteration (#36)
TwitchBronBron Dec 1, 2023
bcda693
fix(lib): Component XML path parsing was failing on edge case (#37)
lvcabral Dec 1, 2023
60abf73
Update changelog for v0.45.3
TwitchBronBron Dec 1, 2023
6a59ad8
0.45.3
TwitchBronBron Dec 1, 2023
d66a0ed
Fixed #16 Print leading space before positive numbers (#39)
lvcabral Dec 11, 2023
7cdd4a3
Fixed #38 Improved context handling for Callables (#40)
lvcabral Dec 11, 2023
bcf6142
Fixed #41 - Global functions `GetInterface()` and `FindMemberFunction…
lvcabral Jan 18, 2024
90f0df1
Update changelog for v0.45.4
TwitchBronBron Jan 18, 2024
b5f9d5c
0.45.4
TwitchBronBron Jan 18, 2024
91af226
Fixed #43 - Implemented `roString` methods `startsWith()` and `endsWi…
lvcabral Feb 7, 2024
88627e7
Fixed Arithmetic Operator Modulo behavior to match Roku (#46)
lvcabral Mar 25, 2024
d766e37
Implemented component `roList`
lvcabral Mar 25, 2024
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
47 changes: 31 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -16,12 +16,12 @@
- [Build](#build)
- [Testing](#testing)
- [Cleaning](#cleaning)
- [All Together](#all-together)
- [Documentation](#documentation)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

# BRS: Off-Roku BrightScript

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

[![build status](https://img.shields.io/github/actions/workflow/status/rokucommunity/brs/build.yml?branch=master&logo=github)](https://github.com/rokucommunity/brs/actions?query=branch%3Amaster+workflow%3Abuild)
@@ -31,32 +31,35 @@ An interpreter for the BrightScript language that runs on non-Roku platforms.
[![license](https://img.shields.io/github/license/rokucommunity/brs.svg)](LICENSE)
[![Slack](https://img.shields.io/badge/Slack-RokuCommunity-4A154B?logo=slack)](https://join.slack.com/t/rokudevelopers/shared_invite/zt-4vw7rg6v-NH46oY7hTktpRIBM_zGvwA)


## Installation

The BRS project is published as a `node` package, so use `npm`:

```shell
$ npm install -g brs
$ npm install -g @rokucommunity/brs
```

## Usage

This repo provides the `brs` executable, which operates in two ways.

### REPL

An interactive BrightScript REPL (Read-Execute-Print Loop) is available by running `brs` with no arguments, e.g.:

```
```shell
$ brs
brs> ?"Dennis Ritchie said ""Hello, World!"""
Dennis Ritchie said "Hello, World!"
```

Quit by entering `^D` (Control-D).
Quit by pressing `^D` (Control-D) or executing `exit`.

### Executing a file

BRS can execute an arbitrary BrightScript file as well! Simply pass the file to the `brs` executable, e.g.:

```
```shell
$ cat hello-world.brs
?"Dennis Ritchie said ""Hello, World!"""

@@ -65,30 +68,45 @@ Dennis Ritchie said "Hello, World!"
```

## Sure, but why?

The [Roku](https://roku.com) series of media streaming devices are wildly popular amongst consumers, and several [very](https://netflix.com) [popular](https://hulu.com) [streaming](https://amazon.com/primevideo) [services](https://crackle.com) offer Channels for the Roku platform. Unfortunately, Roku chanels *must* be written in a language called BrightScript, which is only executable directly on a Roku device. BRS hopes to change that by allowing Roku developers to test their code on their own machines, thus improving the quality of their channels and the end-user's experience as a whole.

## So can I use this to watch TV without a Roku?

Nope! The BRS project currently has no intention of emulating the Roku user interface, integrating with the Roku store, or emulating content playback. In addition to likely getting this project in legal trouble, that sort of emulation is a ton of work.

## Building from source

The BRS project follows pretty standard `node` development patterns, with the caveat that it uses `yarn` for dependency management.

### Prerequisites

BRS builds (and runs) in `node`, so you'll need to [install that first](https://nodejs.org).

### Setup

1. Clone this repo:
```

```shell
$ git clone https://github.com/rokucommunity/brs.git
```

2. Install dependencies:

```shell
$ npm install
```

3. Get `brs` onto your `PATH`:

```shell
$ npm link
```

### The build-test-clean dance

#### Build

This project is written in TypeScript, so it needs to be compiled before it can be executed. `npm run build` compiles files in `src/` into JavaScript and TypeScript declarations, and puts them in `lib/` and `types/` respectively.

```shell
@@ -102,12 +120,15 @@ index.d.ts (and friends)
```

Alternatively, you can run the build step in "watch" mode. This will run `npm run build` for you automatically, every time it detects source file changes:

```shell
$ npm run watch
```

This is often useful for testing that local changes work in your BrightScript project, without having to run `npm run build` over and over.

#### Testing

Tests are written in plain-old JavaScript with [Facebook's Jest](http://facebook.github.io/jest/), and can be run with the `test` target:

```shell
@@ -119,10 +140,11 @@ $ npm run test
Note that only test files ending in `.test.js` will be executed by `yarn test`.

#### Cleaning

Compiled output in `lib/` and `types/` can be removed with the `clean` target:

```shell
$ yarn clean
$ npm run clean

$ ls lib/
ls: cannot access 'lib': No such file or directory
@@ -131,13 +153,6 @@ $ ls types/
ls: cannot access 'types': No such file or directory
```

#### All Together
Thanks to the [npm-run-all](https://www.npmjs.com/package/npm-run-all) package, it's trivially easy to combine these into a sequence of tasks without relying on shell semantics:

```shell
$ yarn run-s clean build test
```

## Documentation

For the most part, `brs` attempts to emulate BrightScript as closely as possible. However, there are certain implementation gaps. Also, in the spirit of unit testing, there are a few extensions that will help with testing. All of our documentation for APIs, extensions, gaps, and more is hosted on our docs site, [hulu.github.io/roca](https://hulu.github.io/roca).
For the most part, `brs` attempts to emulate BrightScript as closely as possible. However, as a work in progress, there are certain implementation gaps, please refer to the [BrightScript language reference](https://developer.roku.com/docs/references/brightscript/language/brightscript-language-reference.md) and report an issue for any gaps found. Also, in the spirit of unit testing, there are a few extensions that will help with testing. All of the roca documentation for APIs, extensions, gaps, and more is hosted on the docs site, [hulu.github.io/roca](https://hulu.github.io/roca).
41 changes: 24 additions & 17 deletions src/stdlib/String.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { BrsType, Callable, ValueKind, BrsString, Int32, Float, StdlibArgument } from "../brsTypes";
import * as Expr from "../parser/Expression";
import {
Callable,
ValueKind,
BrsString,
Int32,
Float,
StdlibArgument,
BrsInvalid,
} from "../brsTypes";
import { Interpreter } from "../interpreter";
import { BrsNumber } from "../brsTypes/BrsNumber";
import { Lexeme } from "../lexer";

/** Converts the string to all uppercase. */
export const UCase = new Callable("UCase", {
@@ -61,7 +67,7 @@ export const Left = new Callable("Left", {
returns: ValueKind.String,
},
impl: (interpreter: Interpreter, s: BrsString, n: Int32) =>
new BrsString(s.value.substr(0, n.getValue())),
new BrsString(s.value.slice(0, n.getValue())),
});

/**
@@ -73,12 +79,8 @@ export const Right = new Callable("Right", {
returns: ValueKind.String,
},
impl: (interpreter: Interpreter, s: BrsString, n: Int32) => {
let end = s.value.length - 1;
let start = end - (n.getValue() - 1);

if (n.getValue() <= 0) return new BrsString("");
else if (start < 0) return new BrsString(s.value);
return new BrsString(s.value.substr(start, end));
return new BrsString(s.value.slice(-n.getValue()));
},
});

@@ -251,20 +253,25 @@ export const Val = new Callable("Val", {
signature: {
args: [
new StdlibArgument("s", ValueKind.String),
new StdlibArgument("radix", ValueKind.Int32, new Int32(10)),
new StdlibArgument("radix", ValueKind.Int32, BrsInvalid.Instance),
],
returns: ValueKind.Dynamic,
},
impl: (interpreter: Interpreter, s: BrsString, brsRadix: Int32): BrsNumber => {
function isBrsStrFloat(str: BrsString): boolean {
return str.value.includes(".");
}
impl: (interpreter: Interpreter, s: BrsString, brsRadix: Int32 | BrsInvalid): BrsNumber => {
const isFloat = s.value.includes(".");

if (isBrsStrFloat(s)) {
return new Float(Number(s.value));
let retNumber = 0;
if (isFloat || brsRadix instanceof BrsInvalid) {
retNumber = Number(s.value);
} else {
return new Int32(parseInt(s.value, brsRadix.getValue()));
retNumber = parseInt(s.value, brsRadix.getValue());
}

if (Number.isNaN(retNumber)) {
return new Int32(0);
}

return isFloat ? new Float(retNumber) : new Int32(retNumber);
},
});

3 changes: 3 additions & 0 deletions test/e2e/StdLib.test.js
Original file line number Diff line number Diff line change
@@ -53,6 +53,9 @@ describe("end to end standard libary", () => {
" 9.7",
"-3",
"12.34",
"0",
"255",
"170",
"Mary and Bob",
"252",
"abababab",
3 changes: 3 additions & 0 deletions test/e2e/resources/stdlib/strings.brs
Original file line number Diff line number Diff line change
@@ -16,6 +16,9 @@ print Str(3.4) ' " 3.4"
print Str(9.7#) ' " 9.7"
print StrI(-3) ' "-3"
print Val("12.34") ' 12.34
print Val("") ' 0
print Val("0xFF") ' 255
print Val("0xAA") ' 170
print Substitute("{0} and {1}", "Mary", "Bob")
print StrToI("252")
print String(4, "ab") ' abababab
16 changes: 15 additions & 1 deletion test/stdlib/String.test.js
Original file line number Diff line number Diff line change
@@ -302,7 +302,21 @@ describe("global string functions", () => {
expect(Val.call(interpreter, new BrsString("0.0"))).toEqual(new Float(0.0));
});

it("returns an integer from a string an radix", () => {
it("returns zero if the string is not a number", () => {
expect(Val.call(interpreter, new BrsString(""))).toEqual(new Int32(0));

expect(Val.call(interpreter, new BrsString("Not a Number"))).toEqual(new Int32(0));

expect(Val.call(interpreter, new BrsString("10+2"))).toEqual(new Int32(0));
});

it("returns an integer from a string", () => {
expect(Val.call(interpreter, new BrsString("65535"))).toEqual(new Int32(65535));

expect(Val.call(interpreter, new BrsString("0xFA"))).toEqual(new Int32(250));
});

it("returns an integer from a string and radix", () => {
expect(Val.call(interpreter, new BrsString("7"), new Int32(10))).toEqual(new Int32(7));

expect(Val.call(interpreter, new BrsString("0x80"), new Int32(0))).toEqual(