Skip to content

Commit

Permalink
Implemented function toAssociativeArray() to simplify creation of A…
Browse files Browse the repository at this point in the history
…A in TypeScript code (#85)

Co-authored-by: Bronley Plumb <bronley@gmail.com>
  • Loading branch information
lvcabral and TwitchBronBron authored Jan 3, 2025
1 parent 0afd6b6 commit 0c0d4e2
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 151 deletions.
14 changes: 2 additions & 12 deletions src/brsTypes/components/ContentNode.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { FieldModel, Field, RoSGNode } from "./RoSGNode";
import { BrsType } from "..";
import { BrsType, toAssociativeArray } from "..";
import { ValueKind, BrsString, BrsBoolean } from "../BrsType";
import { Interpreter } from "../../interpreter";
import { Int32 } from "../Int32";
import { Callable, StdlibArgument } from "../Callable";
import { RoArray } from "./RoArray";
import { RoAssociativeArray } from "./RoAssociativeArray";

export class ContentNode extends RoSGNode {
readonly defaultFields: FieldModel[] = [
Expand Down Expand Up @@ -208,16 +207,7 @@ export class ContentNode extends RoSGNode {
impl: (interpreter: Interpreter) => {
return new RoArray(
this.getElements().map((key: BrsString) => {
return new RoAssociativeArray([
{
name: new BrsString("key"),
value: key,
},
{
name: new BrsString("value"),
value: this.get(key),
},
]);
return toAssociativeArray({ key: key, value: this.get(key) });
})
);
},
Expand Down
4 changes: 3 additions & 1 deletion src/brsTypes/components/RoAssociativeArray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ export class RoAssociativeArray extends BrsComponent implements BrsValue, BrsIte
let lKey = str.value.toLowerCase();
if (this.modeCaseSensitive) {
let keySet = this.keyMap.get(lKey);
keySet?.delete(key);
if (keySet && key) {
keySet.delete(key);
}
if (keySet?.size === 0) {
this.keyMap.delete(lKey);
}
Expand Down
15 changes: 3 additions & 12 deletions src/brsTypes/components/RoDeviceInfo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BrsValue, ValueKind, BrsString, BrsBoolean, BrsInvalid } from "../BrsType";
import { BrsType, Int32, RoArray } from "..";
import { BrsType, Int32, RoArray, toAssociativeArray } from "..";
import { BrsComponent } from "./BrsComponent";
import { Callable, StdlibArgument } from "../Callable";
import { RoAssociativeArray, AAMember } from "./RoAssociativeArray";
Expand Down Expand Up @@ -535,12 +535,7 @@ export class RoDeviceInfo extends BrsComponent implements BrsValue {
returns: ValueKind.Object,
},
impl: (_interpreter, videoFormat: ValueKind.Object) => {
let result = new Array<AAMember>();

result.push({ name: new BrsString("result"), value: BrsBoolean.True });
result.push({ name: new BrsString("codec"), value: new BrsString("mpeg4 avc") });

return new RoAssociativeArray(result);
return toAssociativeArray({ result: true, codec: "mpeg4 avc" });
},
});

Expand Down Expand Up @@ -601,11 +596,7 @@ export class RoDeviceInfo extends BrsComponent implements BrsValue {
returns: ValueKind.Object,
},
impl: (_interpreter, audioFormat: ValueKind.Object) => {
let result = new Array<AAMember>();

result.push({ name: new BrsString("result"), value: BrsBoolean.True });

return new RoAssociativeArray(result);
return toAssociativeArray({ result: true });
},
});

Expand Down
36 changes: 11 additions & 25 deletions src/brsTypes/components/RoPath.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BrsValue, ValueKind, BrsString, BrsBoolean, BrsInvalid, Comparable } from "../BrsType";
import { BrsComponent } from "./BrsComponent";
import { BrsType, RoAssociativeArray, AAMember, isStringComp } from "..";
import { BrsType, RoAssociativeArray, isStringComp, toAssociativeArray } from "..";
import { Callable, StdlibArgument } from "../Callable";
import { Interpreter } from "../../interpreter";
import path from "path";
Expand Down Expand Up @@ -106,8 +106,8 @@ export class RoPath extends BrsComponent implements BrsValue, Comparable {
},
});

/** Returns an roAssociativeArrays containing the significant elements of the path */
private split = new Callable("split", {
/** Returns an roAssociativeArray containing the significant elements of the path */
private readonly split = new Callable("split", {
signature: {
args: [],
returns: ValueKind.Object,
Expand All @@ -116,28 +116,14 @@ export class RoPath extends BrsComponent implements BrsValue, Comparable {
if (this.fullPath === "") {
return new RoAssociativeArray([]);
}
const parts = new Array<AAMember>();
parts.push({
name: new BrsString("basename"),
value: new BrsString(this.parsedPath.name),
});
parts.push({
name: new BrsString("extension"),
value: new BrsString(this.parsedPath.ext),
});
parts.push({
name: new BrsString("filename"),
value: new BrsString(this.parsedPath.base),
});
parts.push({
name: new BrsString("parent"),
value: new BrsString(this.getParentPart()),
});
parts.push({
name: new BrsString("phy"),
value: new BrsString(this.parsedUrl.protocol),
});
return new RoAssociativeArray(parts);
const parts = {
basename: this.parsedPath.name,
extension: this.parsedPath.ext,
filename: this.parsedPath.base,
parent: this.getParentPart(),
phy: this.parsedUrl.protocol,
};
return toAssociativeArray(parts);
},
});

Expand Down
21 changes: 3 additions & 18 deletions src/brsTypes/components/RoSGNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "../BrsType";
import { RoSGNodeEvent } from "./RoSGNodeEvent";
import { BrsComponent, BrsIterable } from "./BrsComponent";
import { BrsType, isBrsNumber } from "..";
import { BrsType, isBrsNumber, toAssociativeArray } from "..";
import { Callable, StdlibArgument } from "../Callable";
import { Interpreter } from "../../interpreter";
import { Int32 } from "../Int32";
Expand Down Expand Up @@ -807,16 +807,7 @@ export class RoSGNode extends BrsComponent implements BrsValue, BrsIterable {
impl: (interpreter: Interpreter) => {
return new RoArray(
this.getElements().map((key: BrsString) => {
return new RoAssociativeArray([
{
name: new BrsString("key"),
value: key,
},
{
name: new BrsString("value"),
value: this.get(key),
},
]);
return toAssociativeArray({ key: key, value: this.get(key) });
})
);
},
Expand Down Expand Up @@ -1532,13 +1523,7 @@ export class RoSGNode extends BrsComponent implements BrsValue, BrsIterable {
returns: ValueKind.Dynamic,
},
impl: (interpreter: Interpreter) => {
const zeroValue = new Int32(0);
return new RoAssociativeArray([
{ name: new BrsString("x"), value: zeroValue },
{ name: new BrsString("y"), value: zeroValue },
{ name: new BrsString("height"), value: zeroValue },
{ name: new BrsString("width"), value: zeroValue },
]);
return toAssociativeArray({ x: 0, y: 0, width: 0, height: 0 });
},
});

Expand Down
78 changes: 78 additions & 0 deletions src/brsTypes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ export type AllComponents = { kind: ValueKind.Object } & BrsComponent & BrsValue
/** The set of all supported types in BrightScript. */
export type BrsType = BrsPrimitive | Iterable | Callable | AllComponents | Uninitialized;

// Function to check if the value is a BrightScript Type
export function isBrsType(value: any): value is BrsType {
return (
isBrsBoolean(value) ||
isBrsString(value) ||
isBrsNumber(value) ||
value === BrsInvalid.Instance ||
value instanceof BrsComponent ||
value instanceof Callable ||
value instanceof Uninitialized
);
}

/** The valid ISO Date formats for roDateTime and roTimeSpan parsing */
export const ValidDateFormats = [
"YYYY-MM-DDTHH:mm:ss.SSS[Z]",
Expand All @@ -196,3 +209,68 @@ export const ValidDateFormats = [
"YYYY[Z]",
"YYYY",
];

/**
* Converts a JavaScript object or Map to a RoAssociativeArray, converting each property or entry to the corresponding BrightScript type.
* @param input The JavaScript object or Map to convert.
* @returns A RoAssociativeArray with the converted properties or entries.
*/
export function toAssociativeArray(input: any): RoAssociativeArray {
const associativeArray = new RoAssociativeArray([]);
if (input instanceof Map) {
input.forEach((value, key) => {
let brsKey: BrsString;
if (typeof key === "string") {
brsKey = new BrsString(key);
} else {
throw new Error(`Unsupported key type: ${typeof key}`);
}
associativeArray.set(brsKey, brsValueOf(value), true);
});
} else if (typeof input === "object" && input !== null) {
for (const key in input) {
if (input.hasOwnProperty(key)) {
const brsKey = new BrsString(key);
associativeArray.set(brsKey, brsValueOf(input[key]), true);
}
}
} else {
throw new Error(`Unsupported input type: ${typeof input}`);
}
return associativeArray;
}

/**
* Converts a value to its representation as a BrsType. If no such
* representation is possible, throws an Error.
* @param {any} x Some value.
* @return {BrsType} The BrsType representation of `x`.
* @throws {Error} If `x` cannot be represented as a BrsType.
*/
export function brsValueOf(x: any): BrsType {
if (x === null || x === undefined || Number.isNaN(x)) {
return BrsInvalid.Instance;
}
const maxInt = 0x80000000;
const t: string = typeof x;
switch (t) {
case "boolean":
return BrsBoolean.from(x);
case "string":
return new BrsString(x);
case "number":
if (Number.isInteger(x)) {
return x >= -maxInt && x < maxInt ? new Int32(x) : new Int64(x);
}
return x >= -3.4e38 && x <= 3.4e38 ? new Float(x) : new Double(x);
case "object":
if (isBrsType(x)) {
return x;
} else if (Array.isArray(x)) {
return new RoArray(x.map(brsValueOf));
}
return toAssociativeArray(x);
default:
throw new Error(`brsValueOf not implemented for: ${x} <${t}>`);
}
}
34 changes: 17 additions & 17 deletions src/extensions/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RoAssociativeArray, BrsString, mGlobal } from "../brsTypes";
import { RoAssociativeArray, BrsString, mGlobal, toAssociativeArray } from "../brsTypes";
import { mockComponent } from "./mockComponent";
import { mockFunction } from "./mockFunction";
import { mockComponentPartial } from "./mockComponentPartial";
Expand All @@ -14,22 +14,22 @@ import {
import { triggerKeyEvent } from "./triggerKeyEvent";
import { GetStackTrace } from "./GetStackTrace";

export const _brs_ = new RoAssociativeArray([
{ name: new BrsString("mockComponent"), value: mockComponent },
{ name: new BrsString("mockFunction"), value: mockFunction },
{ name: new BrsString("mockComponentPartial"), value: mockComponentPartial },
{ name: new BrsString("resetMocks"), value: resetMocks },
{ name: new BrsString("resetMockComponents"), value: resetMockComponents },
{ name: new BrsString("resetMockComponent"), value: resetMockComponent },
{ name: new BrsString("resetMockFunctions"), value: resetMockFunctions },
{ name: new BrsString("resetMockFunction"), value: resetMockFunction },
{ name: new BrsString("runInScope"), value: RunInScope },
{ name: new BrsString("process"), value: Process },
{ name: new BrsString("global"), value: mGlobal },
{ name: new BrsString("testData"), value: new RoAssociativeArray([]) },
{ name: new BrsString("triggerKeyEvent"), value: triggerKeyEvent },
{ name: new BrsString("getStackTrace"), value: GetStackTrace },
]);
export const _brs_ = toAssociativeArray({
mockComponent: mockComponent,
mockFunction: mockFunction,
mockComponentPartial: mockComponentPartial,
resetMocks: resetMocks,
resetMockComponents: resetMockComponents,
resetMockComponent: resetMockComponent,
resetMockFunctions: resetMockFunctions,
resetMockFunction: resetMockFunction,
runInScope: RunInScope,
process: Process,
global: mGlobal,
testData: new RoAssociativeArray([]),
triggerKeyEvent: triggerKeyEvent,
getStackTrace: GetStackTrace,
});

/** resets _brs_.testData values to `{}` in brightscript representation */
export function resetTestData() {
Expand Down
Loading

0 comments on commit 0c0d4e2

Please sign in to comment.