From 31c0f77a430674967dac486e6c687a8e5bfe0a0c Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Tue, 12 Nov 2024 13:28:07 +0100 Subject: [PATCH] test JS compilation --- bin/hoge | 3 + hogvm/stl/compile.py | 2 +- hogvm/test.sh | 18 ++++- posthog/hogql/cli.py | 76 ++++++++++--------- .../hogql/compiler/test/test_javascript.py | 52 ++++++------- 5 files changed, 87 insertions(+), 64 deletions(-) diff --git a/bin/hoge b/bin/hoge index 897de9b985623..26e1043b6f95e 100755 --- a/bin/hoge +++ b/bin/hoge @@ -3,10 +3,13 @@ set -e if [[ "$@" == *".hog"* ]]; then exec python3 -m posthog.hogql.cli --compile "$@" +elif [[ "$@" == *".js"* ]]; then + exec python3 -m posthog.hogql.cli --compile "$@" else echo "$0 - the Hog compilër! 🦔+🕶️= Hoge" echo "" echo "Usage: bin/hoge [output.hoge] compile .hog into .hoge" + echo " bin/hoge compile .hog into .js" echo " bin/hog run .hog source code" echo " bin/hog run compiled .hoge bytecode" exit 1 diff --git a/hogvm/stl/compile.py b/hogvm/stl/compile.py index 11cb44dfde823..0ef0cf731dd46 100755 --- a/hogvm/stl/compile.py +++ b/hogvm/stl/compile.py @@ -5,7 +5,7 @@ import json from posthog.hogql import ast -from posthog.hogql.bytecode import create_bytecode, parse_program +from posthog.hogql.compiler.bytecode import create_bytecode, parse_program source = "hogvm/stl/src/*.hog" target_ts = "hogvm/typescript/src/stl/bytecode.ts" diff --git a/hogvm/test.sh b/hogvm/test.sh index 99b3f0dda37ab..caaf7e455c479 100755 --- a/hogvm/test.sh +++ b/hogvm/test.sh @@ -20,11 +20,23 @@ for file in hogvm/__tests__/*.hog; do ./bin/hoge $file $basename.hoge ./bin/hog --nodejs $basename.hoge > $basename.stdout.nodejs ./bin/hog --python $basename.hoge > $basename.stdout.python + + set +e + ./bin/hoge $file $basename.js + node $basename.js > $basename.stdout.compiledjs 2>&1 + set -e + set +e - diff $basename.stdout.nodejs $basename.stdout.python + diff $basename.stdout.nodejs $basename.stdout.compiledjs if [ $? -eq 0 ]; then - mv $basename.stdout.nodejs $basename.stdout - rm $basename.stdout.python + diff $basename.stdout.nodejs $basename.stdout.python + if [ $? -eq 0 ]; then + mv $basename.stdout.nodejs $basename.stdout + rm $basename.stdout.python + rm $basename.stdout.compiledjs + else + echo "Test failed" + fi else echo "Test failed" fi diff --git a/posthog/hogql/cli.py b/posthog/hogql/cli.py index 6de542069e0de..36be3d1f08652 100644 --- a/posthog/hogql/cli.py +++ b/posthog/hogql/cli.py @@ -3,6 +3,7 @@ from hogvm.python.execute import execute_bytecode from posthog.hogql.compiler.bytecode import create_bytecode, parse_program +from posthog.hogql.compiler.javascript import to_js_program modifiers = [arg for arg in sys.argv if arg.startswith("-")] args = [arg for arg in sys.argv if arg != "" and not arg.startswith("-")] @@ -14,46 +15,53 @@ with open(filename) as file: code = file.read() -if filename.endswith(".hog"): - bytecode = create_bytecode(parse_program(code)).bytecode -else: - bytecode = json.loads(code) +if "--compile" in modifiers and len(args) == 3 and args[2].endswith(".js"): + target = args[2] + js_program = to_js_program(code) + with open(target, "w") as file: + file.write(js_program + "\n") -if "--run" in modifiers: - if len(args) != 2: - raise ValueError("Must specify exactly one filename") +else: + if filename.endswith(".hog"): + bytecode = create_bytecode(parse_program(code)).bytecode + else: + bytecode = json.loads(code) - response = execute_bytecode(bytecode, globals=None, timeout=5, team=None, debug="--debug" in modifiers) - for line in response.stdout: - print(line) # noqa: T201 + if "--run" in modifiers: + if len(args) != 2: + raise ValueError("Must specify exactly one filename") -elif "--out" in modifiers: - if len(args) != 2: - raise ValueError("Must specify exactly one filename") - print(json.dumps(bytecode)) # noqa: T201 + response = execute_bytecode(bytecode, globals=None, timeout=5, team=None, debug="--debug" in modifiers) + for line in response.stdout: + print(line) # noqa: T201 -elif "--compile" in modifiers: - if len(args) == 3: - target = args[2] - else: - target = filename[:-4] + ".hoge" + elif "--out" in modifiers: if len(args) != 2: raise ValueError("Must specify exactly one filename") + print(json.dumps(bytecode)) # noqa: T201 - # write bytecode to file - with open(target, "w") as file: - max_length = 120 - line = "[" - for index, op in enumerate(bytecode): - encoded = json.dumps(op) - if len(line) + len(encoded) > max_length - 2: + elif "--compile" in modifiers: + if len(args) == 3: + target = args[2] + else: + target = filename[:-4] + ".hoge" + if len(args) != 2: + raise ValueError("Must specify exactly one filename") + + # write bytecode to file + with open(target, "w") as file: + max_length = 120 + line = "[" + for index, op in enumerate(bytecode): + encoded = json.dumps(op) + if len(line) + len(encoded) > max_length - 2: + file.write(line + "\n") + line = "" + line += (" " if len(line) > 1 else "") + encoded + ("]" if index == len(bytecode) - 1 else ",") + if line == "[": + file.write(line + "]\n") + elif line != "": file.write(line + "\n") - line = "" - line += (" " if len(line) > 1 else "") + encoded + ("]" if index == len(bytecode) - 1 else ",") - if line == "[": - file.write(line + "]\n") - elif line != "": - file.write(line + "\n") -else: - raise ValueError("Must specify either --run or --compile") + else: + raise ValueError("Must specify either --run or --compile") diff --git a/posthog/hogql/compiler/test/test_javascript.py b/posthog/hogql/compiler/test/test_javascript.py index c128d02ef9aba..811f137e5d232 100644 --- a/posthog/hogql/compiler/test/test_javascript.py +++ b/posthog/hogql/compiler/test/test_javascript.py @@ -6,32 +6,32 @@ class TestJavaScript(BaseTest): def test_javascript_create(self): - self.assertEqual(to_js_expr("1 + 2"), "(1 + 2)") - self.assertEqual(to_js_expr("1 and 2"), "(1 && 2)") - self.assertEqual(to_js_expr("1 or 2"), "(1 || 2)") - self.assertEqual(to_js_expr("1 or (2 and 1) or 2"), "(1 || (2 && 1) || 2)") - self.assertEqual(to_js_expr("(1 or 2) and (1 or 2)"), "((1 || 2) && (1 || 2))") - self.assertEqual(to_js_expr("not true"), "(!true)") - self.assertEqual(to_js_expr("true"), "true") - self.assertEqual(to_js_expr("false"), "false") - self.assertEqual(to_js_expr("null"), "null") - self.assertEqual(to_js_expr("3.14"), "3.14") - self.assertEqual(to_js_expr("properties.bla"), "properties.bla") - self.assertEqual(to_js_expr("concat('arg', 'another')"), '(String("arg") + String("another"))') - self.assertEqual(to_js_expr("ifNull(properties.email, false)"), "(properties.email ?? false)") - self.assertEqual(to_js_expr("1 = 2"), "(1 == 2)") - self.assertEqual(to_js_expr("1 == 2"), "(1 == 2)") - self.assertEqual(to_js_expr("1 != 2"), "(1 != 2)") - self.assertEqual(to_js_expr("1 < 2"), "(1 < 2)") - self.assertEqual(to_js_expr("1 <= 2"), "(1 <= 2)") - self.assertEqual(to_js_expr("1 > 2"), "(1 > 2)") - self.assertEqual(to_js_expr("1 >= 2"), "(1 >= 2)") - self.assertEqual(to_js_expr("1 in 2"), "(2.includes(1))") - self.assertEqual(to_js_expr("1 not in 2"), "(!2.includes(1))") - self.assertEqual(to_js_expr("match('test', 'e.*')"), 'match("test", "e.*")') - self.assertEqual(to_js_expr("not('test')"), '(!"test")') - self.assertEqual(to_js_expr("or('test', 'test2')"), '("test" || "test2")') - self.assertEqual(to_js_expr("and('test', 'test2')"), '("test" && "test2")') + assert to_js_expr("1 + 2") == "(1 + 2)" + assert to_js_expr("1 and 2") == "(1 && 2)" + assert to_js_expr("1 or 2") == "(1 || 2)" + assert to_js_expr("1 or (2 and 1) or 2") == "(1 || (2 && 1) || 2)" + assert to_js_expr("(1 or 2) and (1 or 2)") == "((1 || 2) && (1 || 2))" + assert to_js_expr("not true") == "(!true)" + assert to_js_expr("true") == "true" + assert to_js_expr("false") == "false" + assert to_js_expr("null") == "null" + assert to_js_expr("3.14") == "3.14" + assert to_js_expr("properties.bla") == "properties.bla" + assert to_js_expr("concat('arg', 'another')"), '(String("arg") + String("another"))' + assert to_js_expr("ifNull(properties.email, false)") == "(properties.email ?? false)" + assert to_js_expr("1 = 2") == "(1 == 2)" + assert to_js_expr("1 == 2") == "(1 == 2)" + assert to_js_expr("1 != 2") == "(1 != 2)" + assert to_js_expr("1 < 2") == "(1 < 2)" + assert to_js_expr("1 <= 2") == "(1 <= 2)" + assert to_js_expr("1 > 2") == "(1 > 2)" + assert to_js_expr("1 >= 2") == "(1 >= 2)" + assert to_js_expr("1 in 2") == "(2.includes(1))" + assert to_js_expr("1 not in 2") == "(!2.includes(1))" + assert to_js_expr("match('test', 'e.*')") == 'match("test", "e.*")' + assert to_js_expr("not('test')") == '(!"test")' + assert to_js_expr("or('test', 'test2')") == '("test" || "test2")' + assert to_js_expr("and('test', 'test2')") == '("test" && "test2")' def test_javascript_create_not_implemented_error(self): with self.assertRaises(NotImplementedError) as e: