• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brs / #213

27 Jan 2025 05:30PM UTC coverage: 86.996% (-2.2%) from 89.205%
#213

push

web-flow
Implemented several improvements to SceneGraph (#87)

* Implemented several improvements to SceneGraph

* Fixed most test cases

* Reduced unnecessary code

* Fixed typo

* Added Warning when trying to create a non-existent Node

* Fixed parser

* Fixed unit tests

* Implemented support for `infoFields`

* Prettier fix

* Simplified execute callback code and matched behavior with Roku

* Adding comment to clarify the exception

2240 of 2807 branches covered (79.8%)

Branch coverage included in aggregate %.

139 of 304 new or added lines in 18 files covered. (45.72%)

2 existing lines in 1 file now uncovered.

6129 of 6813 relevant lines covered (89.96%)

27562.41 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.44
/src/LexerParser.ts
1
import * as fs from "fs";
135✔
2
import * as path from "path";
135✔
3
import { promisify } from "util";
135✔
4
import pSettle from "p-settle";
135✔
5
const readFile = promisify(fs.readFile);
135✔
6

7
import { Lexer } from "./lexer";
135✔
8
import { Parser, Stmt } from "./parser/";
135✔
9
import * as PP from "./preprocessor";
135✔
10

11
import * as BrsTypes from "./brsTypes";
135✔
12
export { BrsTypes as types };
135✔
13
export { PP as preprocessor };
135✔
14
import * as BrsError from "./Error";
135✔
15
import { defaultExecutionOptions, ExecutionOptions } from "./interpreter";
135✔
16
import { ComponentScript } from "./scenegraph";
17
import { ManifestValue } from "./preprocessor/Manifest";
18

19
export function getLexerParserFn(
135✔
20
    manifest: Map<string, ManifestValue>,
21
    options: Partial<ExecutionOptions>
22
) {
23
    const executionOptions = { ...defaultExecutionOptions, ...options };
231✔
24
    /**
25
     * Map file URIs or Source Content to promises. The promises resolve to an array of that script's statements.
26
     * This allows us to only parse each shared file once.
27
     */
28
    let memoizedStatements = new Map<string, Promise<Stmt.Statement[]>>();
231✔
29
    return async function parse(scripts: ComponentScript[]): Promise<Stmt.Statement[]> {
231✔
30
        async function lexAndParseScript(script: ComponentScript) {
31
            let contents;
32
            let filename;
33
            if (script.uri !== undefined) {
1,489!
34
                filename = script.uri.replace(/[\/\\]+/g, path.posix.sep);
1,489✔
35
                try {
1,489✔
36
                    contents = await readFile(filename, "utf-8");
1,489✔
37
                } catch (err) {
NEW
38
                    let errno = (err as NodeJS.ErrnoException)?.errno || -4858;
×
NEW
39
                    return Promise.reject({
×
40
                        message: `brs: can't open file '${filename}': [Errno ${errno}]`,
41
                    });
42
                }
NEW
43
            } else if (script.content !== undefined) {
×
NEW
44
                contents = script.content;
×
NEW
45
                filename = script.xmlPath || "xml";
×
46
            } else {
NEW
47
                return Promise.reject({ message: "brs: invalid script object" });
×
48
            }
49
            let lexer = new Lexer();
1,489✔
50
            let preprocessor = new PP.Preprocessor();
1,489✔
51
            let parser = new Parser();
1,489✔
52
            [lexer, preprocessor, parser].forEach((emitter) =>
1,489✔
53
                emitter.onError(BrsError.getLoggerUsing(executionOptions.stderr))
4,467✔
54
            );
55

56
            let scanResults = lexer.scan(contents, filename);
1,489✔
57
            if (scanResults.errors.length > 0) {
1,489!
58
                return Promise.reject({
×
59
                    message: "Error occurred during lexing",
60
                });
61
            }
62

63
            let preprocessResults = preprocessor.preprocess(scanResults.tokens, manifest);
1,489✔
64
            if (preprocessResults.errors.length > 0) {
1,488!
65
                return Promise.reject({
×
66
                    message: "Error occurred during pre-processing",
67
                });
68
            }
69

70
            let parseResults = parser.parse(preprocessResults.processedTokens);
1,488✔
71
            if (parseResults.errors.length > 0) {
1,488!
72
                return Promise.reject({
×
73
                    message: "Error occurred parsing",
74
                });
75
            }
76

77
            return Promise.resolve(parseResults.statements);
1,488✔
78
        }
79

80
        let promises: Promise<Stmt.Statement[]>[] = [];
2,227✔
81
        for (let script of scripts) {
2,227✔
82
            if (script.uri !== undefined) {
2,133!
83
                let maybeStatements = memoizedStatements.get(script.uri);
2,133✔
84
                if (maybeStatements) {
2,133✔
85
                    promises.push(maybeStatements);
644✔
86
                } else {
87
                    let statementsPromise = lexAndParseScript(script);
1,489✔
88
                    if (!memoizedStatements.has(script.uri)) {
1,489✔
89
                        memoizedStatements.set(script.uri, statementsPromise);
1,489✔
90
                    }
91
                    promises.push(statementsPromise);
1,489✔
92
                }
NEW
93
            } else if (script.content !== undefined) {
×
NEW
94
                promises.push(lexAndParseScript(script));
×
95
            }
96
        }
97
        let parsedScripts = await pSettle(promises);
2,227✔
98

99
        // don't execute anything if there were reading, lexing, or parsing errors
100
        if (parsedScripts.some((script) => script.isRejected)) {
2,227✔
101
            return Promise.reject({
1✔
102
                messages: parsedScripts
103
                    .filter((script) => script.isRejected)
1✔
104
                    .map((rejection) => rejection.reason.message),
1✔
105
            });
106
        }
107

108
        // combine statements from all scripts into one array
109
        return parsedScripts
2,226✔
110
            .map((script) => script.value || [])
2,132!
111
            .reduce((allStatements, fileStatements) => [...allStatements, ...fileStatements], []);
2,132✔
112
    };
113
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc