Skip to content

Commit

Permalink
Merge branch 'master' into add-support-flags-on-formatJson
Browse files Browse the repository at this point in the history
  • Loading branch information
TwitchBronBron authored Jun 14, 2024
2 parents a3028ef + 68faa72 commit 7b0adf1
Show file tree
Hide file tree
Showing 18 changed files with 437 additions and 264 deletions.
12 changes: 10 additions & 2 deletions src/coverage/FileCoverage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,11 @@ export class FileCoverage implements Expr.Visitor<BrsType>, Stmt.Visitor<BrsType

visitIndexedSet(statement: Stmt.IndexedSet) {
this.evaluate(statement.obj);
this.evaluate(statement.index);
statement.indexes.forEach((exprOrToken) => {
if (!isToken(exprOrToken)) {
this.evaluate(exprOrToken);
}
});
this.evaluate(statement.value);

return BrsInvalid.Instance;
Expand Down Expand Up @@ -370,7 +374,11 @@ export class FileCoverage implements Expr.Visitor<BrsType>, Stmt.Visitor<BrsType

visitIndexedGet(expression: Expr.IndexedGet) {
this.evaluate(expression.obj);
this.evaluate(expression.index);
expression.indexes.forEach((exprOrToken) => {
if (!isToken(exprOrToken)) {
this.evaluate(exprOrToken);
}
});
return BrsInvalid.Instance;
}

Expand Down
195 changes: 153 additions & 42 deletions src/interpreter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ import {
roInvalid,
PrimitiveKinds,
Signature,
RoByteArray,
RoList,
RoXMLElement,
RoSGNode,
} from "../brsTypes";

import { Lexeme, Location } from "../lexer";
Expand Down Expand Up @@ -1309,29 +1313,72 @@ export class Interpreter implements Expr.Visitor<BrsType>, Stmt.Visitor<BrsType>
this.addError(new RuntimeError(RuntimeErrorDetail.UndimmedArray, expression.location));
}

let index = this.evaluate(expression.index);
if (!isBrsNumber(index) && !isBrsString(index)) {
this.addError(
new TypeMismatch({
message:
"Attempting to retrieve property from iterable with illegal index type",
left: {
type: source,
location: expression.obj.location,
},
right: {
type: index,
location: expression.index.location,
},
})
);
if (
source instanceof RoAssociativeArray ||
source instanceof RoXMLElement ||
source instanceof RoSGNode
) {
if (expression.indexes.length !== 1) {
this.addError(
new RuntimeError(
RuntimeErrorDetail.WrongNumberOfParams,
expression.closingSquare.location
)
);
}
let index = this.evaluate(expression.indexes[0]);
if (!isBrsString(index)) {
this.addError(
new TypeMismatch({
message: `"String" should be used as key, but received`,
left: {
type: index,
location: expression.indexes[0].location,
},
})
);
}
try {
return source.get(index, true);
} catch (err: any) {
this.addError(new BrsError(err.message, expression.closingSquare.location));
}
}

try {
return source.get(index, true);
} catch (err: any) {
this.addError(new BrsError(err.message, expression.closingSquare.location));
if (source instanceof RoByteArray) {
if (expression.indexes.length !== 1) {
this.addError(
new RuntimeError(
RuntimeErrorDetail.BadNumberOfIndexes,
expression.closingSquare.location
)
);
}
}
let current: BrsType = source;
for (let index of expression.indexes) {
let dimIndex = this.evaluate(index);
if (!isBrsNumber(dimIndex)) {
this.addError(
new RuntimeError(RuntimeErrorDetail.NonNumericArrayIndex, index.location)
);
}
if (
current instanceof RoArray ||
current instanceof RoByteArray ||
current instanceof RoList
) {
try {
current = current.get(dimIndex);
} catch (err: any) {
this.addError(new BrsError(err.message, index.location));
}
} else {
this.addError(
new RuntimeError(RuntimeErrorDetail.BadNumberOfIndexes, expression.location)
);
}
}
return current;
}

visitGrouping(expr: Expr.Grouping) {
Expand Down Expand Up @@ -1549,35 +1596,99 @@ export class Interpreter implements Expr.Visitor<BrsType>, Stmt.Visitor<BrsType>
}

visitIndexedSet(statement: Stmt.IndexedSet) {
let value = this.evaluate(statement.value);
let source = this.evaluate(statement.obj);

if (!isIterable(source)) {
this.addError(new RuntimeError(RuntimeErrorDetail.BadLHS, statement.obj.location));
}

let index = this.evaluate(statement.index);
if (!isBrsNumber(index) && !isBrsString(index)) {
this.addError(
new TypeMismatch({
message: "Attempting to set property on iterable with illegal index type",
left: {
type: source,
location: statement.obj.location,
},
right: {
type: index,
location: statement.index.location,
},
})
);
if (
source instanceof RoAssociativeArray ||
source instanceof RoXMLElement ||
source instanceof RoSGNode
) {
if (statement.indexes.length !== 1) {
this.addError(
new RuntimeError(
RuntimeErrorDetail.WrongNumberOfParams,
statement.closingSquare.location
)
);
}
let index = this.evaluate(statement.indexes[0]);
if (!isBrsString(index)) {
this.addError(
new TypeMismatch({
message: `"String" should be used as key, but received`,
left: {
type: index,
location: statement.indexes[0].location,
},
})
);
}
try {
source.set(index, value, true);
} catch (err: any) {
this.addError(new BrsError(err.message, statement.closingSquare.location));
}
return BrsInvalid.Instance;
}
if (source instanceof RoByteArray) {
if (statement.indexes.length !== 1) {
this.addError(
new RuntimeError(
RuntimeErrorDetail.BadNumberOfIndexes,
statement.closingSquare.location
)
);
}
}

let value = this.evaluate(statement.value);
let current: BrsType = source;
for (let i = 0; i < statement.indexes.length; i++) {
let index = this.evaluate(statement.indexes[i]);
if (!isBrsNumber(index)) {
this.addError(
new RuntimeError(
RuntimeErrorDetail.NonNumericArrayIndex,
statement.indexes[i].location
)
);
}

try {
source.set(index, value, true);
} catch (err: any) {
this.addError(new BrsError(err.message, statement.closingSquare.location));
if (i < statement.indexes.length - 1) {
if (
current instanceof RoArray ||
current instanceof RoByteArray ||
current instanceof RoList
) {
try {
current = current.get(index);
} catch (err: any) {
this.addError(new BrsError(err.message, statement.closingSquare.location));
}
} else {
this.addError(
new RuntimeError(RuntimeErrorDetail.BadNumberOfIndexes, statement.location)
);
}
} else if (
current instanceof RoArray ||
current instanceof RoByteArray ||
current instanceof RoList
) {
try {
current.set(index, value);
} catch (err: any) {
this.addError(new BrsError(err.message, statement.closingSquare.location));
}
} else {
this.addError(
new RuntimeError(RuntimeErrorDetail.BadNumberOfIndexes, statement.location)
);
}
}

return BrsInvalid.Instance;
Expand Down Expand Up @@ -1626,7 +1737,7 @@ export class Interpreter implements Expr.Visitor<BrsType>, Stmt.Visitor<BrsType>
this.execute(
new Stmt.IndexedSet(
statement.value.obj,
statement.value.index,
statement.value.indexes,
new Expr.Literal(result, statement.location),
statement.value.closingSquare
)
Expand Down
2 changes: 1 addition & 1 deletion src/parser/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export class DottedGet extends AstNode implements Expression {
export class IndexedGet extends AstNode implements Expression {
constructor(
readonly obj: Expression,
readonly index: Expression,
readonly indexes: Expression[],
readonly closingSquare: Token
) {
super("IndexedGet");
Expand Down
25 changes: 20 additions & 5 deletions src/parser/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1114,7 +1114,7 @@ export class Parser {

return new Stmt.IndexedSet(
left.obj,
left.index,
left.indexes,
operator.kind === Lexeme.Equal
? right
: new Expr.Binary(left, operator, right),
Expand Down Expand Up @@ -1353,7 +1353,7 @@ export class Parser {
return undefined;
}

//the block's location starts at the end of the preceeding token, and stops at the beginning of the `end` token
//the block's location starts at the end of the preceding token, and stops at the beginning of the `end` token
const location: Location = {
file: startingToken.location.file,
start: startingToken.location.start,
Expand Down Expand Up @@ -1477,17 +1477,32 @@ export class Parser {
let expr = primary();

function indexedGet() {
let elements: Expression[] = [];

while (match(Lexeme.Newline));

let index = expression();
if (!match(Lexeme.RightSquare)) {
elements.push(expression());

while (match(Lexeme.Newline));
while (match(Lexeme.Comma, Lexeme.Newline)) {
while (match(Lexeme.Newline));

if (check(Lexeme.RightSquare)) {
break;
}

elements.push(expression());
}
}
if (elements.length === 0) {
throw addError(peek(), "Expected expression inside brackets []");
}
let closingSquare = consume(
"Expected ']' after array or object index",
Lexeme.RightSquare
);

expr = new Expr.IndexedGet(expr, index, closingSquare);
expr = new Expr.IndexedGet(expr, elements, closingSquare);
}

function dottedGet() {}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/Statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ export class DottedSet extends AstNode implements Statement {
export class IndexedSet extends AstNode implements Statement {
constructor(
readonly obj: Expr.Expression,
readonly index: Expr.Expression,
readonly indexes: Expr.Expression[],
readonly value: Expr.Expression,
readonly closingSquare: Token
) {
Expand Down
2 changes: 1 addition & 1 deletion test/coverage/FileCoverage/expressions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ describe("FileCoverage expressions", () => {
checkSimpleExpression(
new Expr.IndexedGet(
new Expr.Variable(identifier("array")),
new Expr.Literal(new Int32(4), fakeLocation),
[new Expr.Literal(new Int32(4), fakeLocation)],
RIGHT_SQUARE
),
/* expected number of internal statements (IndexedGet, Variable, Literal) */ 3
Expand Down
2 changes: 1 addition & 1 deletion test/coverage/FileCoverage/statements.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ describe("FileCoverage statements", () => {
checkSimpleStatement(
new Stmt.IndexedSet(
new Expr.Variable(identifier("aa")),
new Expr.Literal(new BrsString("bar"), generateLocation(1)),
[new Expr.Literal(new BrsString("bar"), generateLocation(1))],
new Expr.Literal(new BrsString("added bar"), generateLocation(2)),
token(Lexeme.RightBrace, "}")
)
Expand Down
4 changes: 1 addition & 3 deletions test/e2e/Errors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@ describe("Runtime errors", () => {

let errOutput = allArgs(outputStreams.stderr.write).filter((arg) => arg !== "\n");
expect(
errOutput[0].includes(
"Attempting to retrieve property from iterable with illegal index type"
)
errOutput[0].includes("Attempt to use a non-numeric array index not allowed.")
).toBeTruthy();
});
});
6 changes: 6 additions & 0 deletions test/e2e/Iterables.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ describe("end to end iterables", () => {
" 27", // two-dimensional index
" 16", // 2 ^ 4
"foo bar", // oneDimensional[0] += " bar"
"f",
"f",
"f",
"f",
"invalid",
"true",
]);
});

Expand Down
Loading

0 comments on commit 7b0adf1

Please sign in to comment.