Skip to content

Commit

Permalink
Implemented IfArray and IfList interface modules (#450)
Browse files Browse the repository at this point in the history
* Implemented `IfArray` interface module

* Implemented `IfList` interface

* Fixed static analysis issues
  • Loading branch information
lvcabral authored Jan 11, 2025
1 parent 216516d commit 1235f5e
Show file tree
Hide file tree
Showing 9 changed files with 540 additions and 665 deletions.
5 changes: 5 additions & 0 deletions src/core/brsTypes/components/BrsComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,9 @@ export interface BrsIterable {
* Resets the iteration sequence to the beginning of the iterable component.
*/
resetNext(): void;

/**
* Update the iteration index to the next element in the iteration sequence.
*/
updateNext(): void;
}
254 changes: 62 additions & 192 deletions src/core/brsTypes/components/RoArray.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { BrsType, isBrsString, isBrsNumber, Int32, Float } from "..";
import { BrsValue, ValueKind, BrsString, BrsBoolean, BrsInvalid, Comparable } from "../BrsType";
import { BrsComponent, BrsIterable } from "./BrsComponent";
import { BrsComponent } from "./BrsComponent";
import { Callable, StdlibArgument } from "../Callable";
import { Interpreter } from "../../interpreter";
import { RoAssociativeArray } from "./RoAssociativeArray";
import { BrsArray, IfArray, IfArrayGet, IfArraySet } from "../interfaces/IfArray";
import { IfEnum } from "../interfaces/IfEnum";

export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
export class RoArray extends BrsComponent implements BrsValue, BrsArray {
readonly kind = ValueKind.Object;
private readonly resizable: boolean = true;
private maxSize = 0;
readonly resizable: boolean = true;
maxSize = 0;
elements: BrsType[];
enumIndex: number;

Expand All @@ -36,25 +37,28 @@ export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
);
}
this.enumIndex = this.elements.length ? 0 : -1;
const ifArray = new IfArray(this);
const ifArrayGet = new IfArrayGet(this);
const ifArraySet = new IfArraySet(this);
const ifEnum = new IfEnum(this);
this.registerMethods({
ifArray: [
this.peek,
this.pop,
this.push,
this.shift,
this.unshift,
this.delete,
this.count,
this.clear,
this.append,
ifArray.peek,
ifArray.pop,
ifArray.push,
ifArray.shift,
ifArray.unshift,
ifArray.delete,
ifArray.count,
ifArray.clear,
ifArray.append,
],
ifArrayGet: [this.getEntry],
ifArraySet: [this.setEntry],
ifArrayGet: [ifArrayGet.getEntry],
ifArraySet: [ifArraySet.setEntry],
ifArrayJoin: [this.join],
ifArraySort: [this.sort, this.sortBy, this.reverse],
ifArraySizeInfo: [this.capacity, this.isResizable],
ifArraySlice: [this.slice],
ifArraySizeInfo: [this.capacity, this.isResizable],
ifEnum: [ifEnum.isEmpty, ifEnum.isNext, ifEnum.next, ifEnum.reset],
});
}
Expand Down Expand Up @@ -86,6 +90,42 @@ export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
return this.elements.slice();
}

add(element: BrsType, onTail: boolean = true) {
this.addChildRef(element);
if (onTail) {
this.elements.push(element);
} else {
this.elements.unshift(element);
}
this.updateNext(true);
}

remove(index: number) {
let removed;
if (index === 0) {
removed = this.elements.shift();
} else if (index === this.tail()) {
removed = this.elements.pop();
} else {
removed = this.elements.splice(index, 1)[0];
}
this.updateNext();
this.removeChildRef(removed);
return removed;
}

clear() {
this.elements.forEach((element) => {
this.removeChildRef(element);
});
this.elements.length = 0;
this.enumIndex = -1;
}

tail() {
return this.elements.length - 1;
}

get(index: BrsType) {
switch (index.kind) {
case ValueKind.Float:
Expand Down Expand Up @@ -128,16 +168,19 @@ export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
this.enumIndex = this.elements.length > 0 ? 0 : -1;
}

updateNext() {
updateNext(grow?: boolean, factor?: number) {
const hasItems = this.elements.length > 0;
if (this.enumIndex === -1 && hasItems) {
this.enumIndex = 0;
} else if (this.enumIndex >= this.elements.length || !hasItems) {
this.enumIndex = -1;
}
if (grow) {
this.updateCapacity(factor ?? 1.25);
}
}

updateCapacity(growthFactor = 0) {
private updateCapacity(growthFactor = 0) {
if (this.resizable && growthFactor > 0) {
if (this.elements.length > 0 && this.elements.length > this.maxSize) {
let count = this.elements.length - 1;
Expand All @@ -153,7 +196,7 @@ export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
}
}

aaCompare(
private aaCompare(
fieldName: BrsString,
flags: BrsString,
a: RoAssociativeArray,
Expand Down Expand Up @@ -196,179 +239,6 @@ export class RoArray extends BrsComponent implements BrsValue, BrsIterable {
}
}

// ifArray
private readonly peek = new Callable("peek", {
signature: {
args: [],
returns: ValueKind.Dynamic,
},
impl: (_: Interpreter) => {
return this.elements[this.elements.length - 1] || BrsInvalid.Instance;
},
});

private readonly pop = new Callable("pop", {
signature: {
args: [],
returns: ValueKind.Dynamic,
},
impl: (_: Interpreter) => {
const removed = this.elements.pop();
this.removeChildRef(removed);
this.updateNext();
return removed || BrsInvalid.Instance;
},
});

private readonly push = new Callable("push", {
signature: {
args: [new StdlibArgument("talue", ValueKind.Dynamic)],
returns: ValueKind.Void,
},
impl: (interpreter: Interpreter, tvalue: BrsType) => {
if (this.resizable || this.elements.length < this.maxSize) {
this.addChildRef(tvalue);
this.elements.push(tvalue);
this.updateNext();
this.updateCapacity(1.25);
} else {
interpreter.stderr.write(
`warning,BRIGHTSCRIPT: ERROR: roArray.Push: set ignored for index out of bounds on non-resizable array: ${interpreter.formatLocation()}`
);
}
return BrsInvalid.Instance;
},
});

private readonly shift = new Callable("shift", {
signature: {
args: [],
returns: ValueKind.Dynamic,
},
impl: (_: Interpreter) => {
const removed = this.elements.shift();
this.removeChildRef(removed);
this.updateNext();
return removed || BrsInvalid.Instance;
},
});

private readonly unshift = new Callable("unshift", {
signature: {
args: [new StdlibArgument("tvalue", ValueKind.Dynamic)],
returns: ValueKind.Void,
},
impl: (interpreter: Interpreter, tvalue: BrsType) => {
if (this.resizable || this.elements.length < this.maxSize) {
this.addChildRef(tvalue);
this.elements.unshift(tvalue);
this.updateNext();
this.updateCapacity(1.25);
} else {
interpreter.stderr.write(
`warning,BRIGHTSCRIPT: ERROR: roArray.Unshift: set ignored for index out of bounds on non-resizable array: ${interpreter.formatLocation()}`
);
}
return BrsInvalid.Instance;
},
});

private readonly delete = new Callable("delete", {
signature: {
args: [new StdlibArgument("index", ValueKind.Int32)],
returns: ValueKind.Boolean,
},
impl: (_: Interpreter, index: Int32) => {
if (index.lessThan(new Int32(0)).toBoolean()) {
return BrsBoolean.False;
}
const deleted = this.elements.splice(index.getValue(), 1);
deleted.forEach((element) => {
this.removeChildRef(element);
});
this.updateNext();
return BrsBoolean.from(deleted.length > 0);
},
});

private readonly count = new Callable("count", {
signature: {
args: [],
returns: ValueKind.Int32,
},
impl: (_: Interpreter) => {
return new Int32(this.elements.length);
},
});

private readonly clear = new Callable("clear", {
signature: {
args: [],
returns: ValueKind.Void,
},
impl: (_: Interpreter) => {
this.elements.forEach((element) => {
this.removeChildRef(element);
});
this.elements = [];
this.enumIndex = -1;
return BrsInvalid.Instance;
},
});

private readonly append = new Callable("append", {
signature: {
args: [new StdlibArgument("array", ValueKind.Object)],
returns: ValueKind.Void,
},
impl: (interpreter: Interpreter, array: BrsComponent) => {
if (!(array instanceof RoArray)) {
interpreter.stderr.write(
`warning,BRIGHTSCRIPT: ERROR: roArray.Append: invalid parameter type ${array.getComponentName()}: ${interpreter.formatLocation()}`
);
return BrsInvalid.Instance;
}

if (this.resizable || this.elements.length + array.elements.length <= this.maxSize) {
array.elements.forEach((element) => {
this.addChildRef(element);
});
this.elements = [
...this.elements,
...array.elements.filter((element) => !!element), // don't copy "holes" where no value exists
];
this.updateNext();
this.updateCapacity();
}
return BrsInvalid.Instance;
},
});

// ifArrayGet
private readonly getEntry = new Callable("getEntry", {
signature: {
args: [new StdlibArgument("index", ValueKind.Int32 | ValueKind.Float)],
returns: ValueKind.Dynamic,
},
impl: (_: Interpreter, index: Int32 | Float) => {
return this.elements[Math.trunc(index.getValue())] || BrsInvalid.Instance;
},
});

// ifArraySet
private readonly setEntry = new Callable("setEntry", {
signature: {
args: [
new StdlibArgument("index", ValueKind.Int32 | ValueKind.Float),
new StdlibArgument("tvalue", ValueKind.Dynamic),
],
returns: ValueKind.Void,
},
impl: (_: Interpreter, index: Int32 | Float, tvalue: BrsType) => {
return this.set(index, tvalue);
},
});

// ifArrayJoin
private readonly join = new Callable("join", {
signature: {
Expand Down
Loading

0 comments on commit 1235f5e

Please sign in to comment.