{"size": 3479, "ext": "js", "max_stars_repo_path": "test/interpreter/While.test.js", "max_stars_repo_name": "lkipke/brs", "max_stars_repo_stars_event_min_datetime": "2018-01-04T14:26:47.000Z", "max_stars_repo_stars_event_max_datetime": "2022-03-22T20:35:01.000Z", "max_issues_repo_path": "test/interpreter/While.test.js", "max_issues_repo_name": "lkipke/brs", "max_issues_repo_issues_event_min_datetime": "2018-01-05T16:21:14.000Z", "max_issues_repo_issues_event_max_datetime": "2021-09-20T21:36:23.000Z", "max_forks_repo_path": "test/interpreter/While.test.js", "max_forks_repo_name": "lkipke/brs", "max_forks_repo_forks_event_min_datetime": "2018-03-16T04:16:21.000Z", "max_forks_repo_forks_event_max_datetime": "2021-11-24T21:34:26.000Z"}
{"max_stars_count": 90, "max_issues_count": 396, "max_forks_count": 42, "avg_line_length": 31.3423423423, "max_line_length": 100, "alphanum_fraction": 0.5061799368}
const Expr = require("../../lib/parser/Expression");
const Stmt = require("../../lib/parser/Statement");
const { Interpreter } = require("../../lib/interpreter");
const brs = require("brs");
const { Lexeme } = brs.lexer;
const { Int32 } = brs.types;

const { token, identifier } = require("../parser/ParserTests");

let interpreter;
let decrementSpy;

describe("interpreter while loops", () => {
    const initializeFoo = new Stmt.Assignment(
        { equals: token(Lexeme.Equals, "=") },
        identifier("foo"),
        new Expr.Literal(new Int32(5))
    );

    const decrementFoo = new Stmt.Assignment(
        { equals: token(Lexeme.Equals, "=") },
        identifier("foo"),
        new Expr.Binary(
            new Expr.Variable(identifier("foo")),
            token(Lexeme.Minus, "-"),
            new Expr.Literal(new Int32(1))
        )
    );

    beforeEach(() => {
        decrementSpy = jest.spyOn(decrementFoo, "accept");

        interpreter = new Interpreter();
    });

    afterEach(() => {
        decrementSpy.mockReset();
        decrementSpy.mockRestore();
    });

    it("loops until 'condition' is false", () => {
        const statements = [
            initializeFoo,
            new Stmt.While(
                {
                    while: token(Lexeme.While, "while"),
                    endWhile: token(Lexeme.EndWhile, "end while"),
                },
                new Expr.Binary(
                    new Expr.Variable(identifier("foo")),
                    token(Lexeme.Greater, ">"),
                    new Expr.Literal(new Int32(0))
                ),
                new Stmt.Block([decrementFoo])
            ),
        ];

        interpreter.exec(statements);
        expect(decrementSpy).toHaveBeenCalledTimes(5);
    });

    it("evaluates 'condition' before every loop", () => {
        const greaterThanZero = new Expr.Binary(
            new Expr.Variable(identifier("foo")),
            token(Lexeme.Greater, ">"),
            new Expr.Literal(new Int32(0))
        );
        jest.spyOn(greaterThanZero, "accept");

        const statements = [
            initializeFoo,
            new Stmt.While(
                {
                    while: token(Lexeme.While, "while"),
                    endWhile: token(Lexeme.EndWhile, "end while"),
                },
                greaterThanZero,
                new Stmt.Block([decrementFoo])
            ),
        ];

        let results = interpreter.exec(statements);
        // body executes five times, but the condition is evaluated once more to know it should exit
        expect(greaterThanZero.accept).toHaveBeenCalledTimes(6);
    });

    it("exits early when it encounters 'exit while'", () => {
        const statements = [
            initializeFoo,
            new Stmt.While(
                {
                    while: token(Lexeme.While, "while"),
                    endWhile: token(Lexeme.EndWhile, "end while"),
                },
                new Expr.Binary(
                    new Expr.Variable(identifier("foo")),
                    token(Lexeme.Greater, ">"),
                    new Expr.Literal(new Int32(0))
                ),
                new Stmt.Block([
                    decrementFoo,
                    new Stmt.ExitWhile({ exitWhile: token(Lexeme.ExitWhile, "exit while") }),
                ])
            ),
        ];

        interpreter.exec(statements);
        expect(decrementSpy).toHaveBeenCalledTimes(1);
    });
});
