def test_for_loop(self, capsys): line = 'for (var i = 0; i < 10; i = i + 1) print i;' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "0.0\n1.0\n2.0\n3.0\n4.0\n5.0\n6.0\n7.0\n8.0\n9.0\n"
class LoxTests_Execute_Statements(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_print_literal(self): self.lox.execute('print "yo";') self.assertEqual(self.output.last_sent, "yo")
def test_if(self, capsys): line = "var num = 9; \ if(num >= 0 and num <= 10) \ print(true);" lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "True\n"
def test_of(self, capsys): line = 'var s = "hi"; \ if(s == "hi" or s == "hello") \ print("Greetings");' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "Greetings\n"
def test_num_parameters(self, capsys): line = 'fun add(a, b){ \ return(a + b); \ } \ print(add(10, 5));' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "15.0\n"
def test_string_parameters(self, capsys): line = 'fun strAppend(str1, str2) { \ var str = str1 + str2; \ return str;} \ print(strAppend("foo", "bar"));' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "foobar\n"
def test_while_loop(self, capsys): line = 'var i = 0; \ while(i < 10) { \ print(i); \ i = i + 1; \ }' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "0.0\n1.0\n2.0\n3.0\n4.0\n5.0\n6.0\n7.0\n8.0\n9.0\n"
class LoxTests_LogicalOperators(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_print_false_and_1(self): self.lox.execute('print false and 1;') self.assertEqual(self.output.last_sent, False) def test_print_true_and_1(self): self.lox.execute('print true and 1;') self.assertEqual(self.output.last_sent, 1)
def test_fun_with_conditional(self, capsys): line = 'fun checkNegative(num){ \ if(num <= 0) \ return(true); \ else \ return(false); \ } \ print(checkNegative(-1));' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "True\n"
def test_recursion(self, capsys): line = 'fun isOdd(n) { \ if (n == 0) return false; \ return isEven(n - 1); \ } \ fun isEven(n){ \ if (n == 0) return true; \ return isOdd(n - 1); \ } \ print(isEven(3));' lox = Lox() lox.run(line) out, err = capsys.readouterr() assert err == '' assert out == "False\n"
class LoxTests_Scoping(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_reuse_name_in_inner_scope(self): self.lox.execute('var a = 1;') self.lox.execute('{ var a = 2; print a; }') self.assertEqual(self.output.last_sent, 2) self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 1) def test_assign_outer_in_inner_scope(self): self.lox.execute('var a = 1;') self.lox.execute('{ a = 2; }') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 2)
class LoxTests_WhileLoops(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_while_var_one_loop(self): self.lox.execute('var do_loop = true;') self.lox.execute('while (do_loop) { print 1; do_loop = false; }') self.assertEqual(self.output.last_sent, 1) def test_while_false_does_nothing(self): self.lox.execute('while (false) print 1;') self.assertEqual(self.output.num_sent(), 0) def test_while_loops_multiple_times(self): self.lox.execute('var temp = 5;') self.lox.execute('while (temp > 0) { print 1; temp = temp - 1; }') self.assertEqual(self.output.num_sent(), 5)
def execute(self, lox_instance: Lox, out_buf: StringIO) -> bool: lox_instance.interpreter.reinitialize_environment() out_capture = StringIO() err_capture = StringIO() with self.path.open("r") as fil: source = fil.read() self._compute_expected_output(source) with suppress(LoxExit), redirect_stderr(err_capture), redirect_stdout( out_capture): lox_instance.run(source) out = tuple(line.strip() for line in out_capture.getvalue().splitlines()) err = tuple(line.strip() for line in err_capture.getvalue().splitlines()) if not (message := self._verify(out, err)): print(f"[{green('PASS')}]: {self.path}", file=out_buf) return True
class LoxTests_Execute_Expressions(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_numerical(self): params = [ ('1 + 1', 2), ('2 * 2', 4), ('1 + 4 / 2', 3), ('(1 + 4) / 2', 2.5), (""" ( 1 + 4/2) * 3*2 - 10 == ( 1 + 2) * 6 - 10 """, True), ] for expression, expected in params: with self.subTest(): self.lox.execute(f'print {expression};') self.assertEqual(self.output.last_sent, expected) def test_strings(self): params = [ ('"cat" == "cat"', True), ('"dog" == "cat"', False) ] for expression, expected in params: with self.subTest(): self.lox.execute(f'print {expression};') self.assertEqual(self.output.last_sent, expected) def test_bools(self): params = [ ('true == 1 < 2', True), ('false != 1 < 2 + 4', True) ] for expression, expected in params: with self.subTest(): self.lox.execute(f'print {expression};') self.assertEqual(self.output.last_sent, expected) def test_incomplete_expression(self): self.lox.execute('1 +') self.assertTrue('Expected expression' in self.output.last_sent)
def __init__(self) -> None: self._queued_tests: List[Test] = list() self._lox_instance = Lox(Debug.JAVA_STYLE_TOKENS | Debug.REDUCED_ERROR_REPORTING) self._fails_output = StringIO() self._test_root = Path( os.path.realpath(__file__)).parent / "test_suite" if not self._test_root.is_dir(): raise FileNotFoundError("Test suite not found!") print(f"Test suite discovered at '{self._test_root}'.") ignored_paths_full = tuple(self._test_root / path for path in self.IGNORED_PATHS) for path in self.TEST_PATHS: self._discover_and_queue_tests(self._test_root / path, ignored_paths_full) print(f"{len(self._queued_tests)} tests found.")
class LoxTests_IfElse(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_if_true_else(self): self.lox.execute('if (true) print "true"; else print "false";') self.assertEqual(self.output.last_sent, "true") def test_if_false_else(self): self.lox.execute('if (false) print "true"; else print "false";') self.assertEqual(self.output.last_sent, "false") def test_nested_if_else_should_grab_earliest_else(self): self.lox.execute(""" if (true) if (true) print "this should get printed"; else print "this should not get printed"; """) self.assertEqual(self.output.last_sent, "this should get printed")
def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output)
class LoxTests_Variables(unittest.TestCase): def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) def test_declare_then_print(self): self.lox.execute('var a;') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, "nil") def test_declare_with_initialiser_then_print(self): self.lox.execute('var a = 1;') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 1) def test_declare_with_initialiser_expression_then_print(self): self.lox.execute('var a = 1 + 1;') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 2) def test_declare_then_assign(self): self.lox.execute('var a = 1;') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 1) self.lox.execute('a = "asdf";') self.lox.execute('print a;') self.assertEqual(self.output.last_sent, 'asdf') def test_can_print_assignment(self): # assignment is an expression, so has a value self.lox.execute('var a = 1;') self.lox.execute('print a = 2;') self.assertEqual(self.output.last_sent, 2) def test_bad_assign_target_should_print_error(self): self.lox.execute('var a; var b;') self.lox.execute('a + b = 3;') self.assertIn('Invalid assignment target', self.output.last_sent)
description= "Yet another implementation of the Lox interpreter in Python", allow_abbrev=False) parser.add_argument("-c", metavar="STRING", type=str, required=False, help="source string to execute") parser.add_argument("source", metavar="FILE", nargs="?", type=str, default=None, help="the .lox file to interpret") parser.add_argument( "--dbg", choices=tuple(option.name for option in Debug), default=list(), action="append", help="pylox debugging options, multiple --dbg arguments can be passed") args, extra_args = parser.parse_known_args() lox = Lox(reduce(lambda a, b: a | Debug[b], args.dbg, Debug.BACKTRACE)) # Collapse all flags passed. if args.c: lox.run(args.c) elif args.source: lox.run_file(args.source) else: lox.run_interactive()
import sys from pylox.lox import Lox, LoxRepl DEBUG = False if __name__ == "__main__": args = sys.argv[1:] # arg 0 is the name of this script lox = Lox(debug=DEBUG) if len(args) == 0: LoxRepl(lox).run() elif len(args) == 1: lox.run_file(args[0]) else: print('nah')
def setUp(self): self.output = TestOutputStream() self.lox = Lox(output = self.output) self.runner = LoxFileRunner(self.lox)