diff --git a/ee/api/test/test_hooks.py b/ee/api/test/test_hooks.py index 2870b2ecce10b..0017079f4a77d 100644 --- a/ee/api/test/test_hooks.py +++ b/ee/api/test/test_hooks.py @@ -4,6 +4,7 @@ from ee.api.hooks import valid_domain from ee.api.test.base import APILicensedTest from ee.models.hook import Hook +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.models.action.action import Action from posthog.models.hog_functions.hog_function import HogFunction from posthog.test.base import ClickhouseTestMixin @@ -113,7 +114,7 @@ def test_create_hog_function_via_hook(self): assert hog_function.filters == { "actions": [{"id": str(self.action.id), "name": "", "type": "actions", "order": 0}], - "bytecode": ["_h", 32, "$pageview", 32, "event", 1, 1, 11, 3, 1, 4, 1], + "bytecode": ["_H", HOGQL_BYTECODE_VERSION, 32, "$pageview", 32, "event", 1, 1, 11, 3, 1, 4, 1], } assert hog_function.inputs == { @@ -142,7 +143,8 @@ def test_create_hog_function_via_hook(self): "debug": {}, "hook": { "bytecode": [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, 32, "hooks/standard/1234/abcd", ], diff --git a/ee/clickhouse/models/test/test_action.py b/ee/clickhouse/models/test/test_action.py index 1c32ece5e1e94..7495d3eddc4e5 100644 --- a/ee/clickhouse/models/test/test_action.py +++ b/ee/clickhouse/models/test/test_action.py @@ -13,7 +13,7 @@ _create_event, _create_person, ) -from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H +from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H, HOGQL_BYTECODE_VERSION @dataclasses.dataclass @@ -289,6 +289,15 @@ def test_filter_with_hogql(self): action1.bytecode, [ _H, + HOGQL_BYTECODE_VERSION, + # event = 'insight viewed' + op.STRING, + "insight viewed", + op.STRING, + "event", + op.GET_GLOBAL, + 1, + op.EQ, # toInt(properties.filters_count) > 10 op.INTEGER, 10, @@ -298,18 +307,10 @@ def test_filter_with_hogql(self): "properties", op.GET_GLOBAL, 2, - op.CALL, + op.CALL_GLOBAL, "toInt", 1, op.GT, - # event = 'insight viewed' - op.STRING, - "insight viewed", - op.STRING, - "event", - op.GET_GLOBAL, - 1, - op.EQ, # and op.AND, 2, diff --git a/hogvm/README.md b/hogvm/README.md index 9c44d22a518d9..a2eeab5f0f6d6 100644 --- a/hogvm/README.md +++ b/hogvm/README.md @@ -12,7 +12,7 @@ HogQL Bytecode is a compact representation of a subset of the HogQL AST nodes. I 1 or 2 # [_H, op.INTEGER, 2, op.INTEGER, 1, op.OR, 2] not true # [_H, op.TRUE, op.NOT] properties.bla # [_H, op.STRING, "bla", op.STRING, "properties", op.GET_GLOBAL, 2] -call('arg', 'another') # [_H, op.STRING, "another", op.STRING, "arg", op.CALL, "call", 2] +call('arg', 'another') # [_H, op.STRING, "another", op.STRING, "arg", op.CALL_GLOBAL, "call", 2] 1 = 2 # [_H, op.INTEGER, 2, op.INTEGER, 1, op.EQ] 'bla' !~ 'a' # [_H, op.STRING, 'a', op.STRING, 'bla', op.NOT_REGEX] ``` diff --git a/hogvm/__tests__/__snapshots__/arrays.hoge b/hogvm/__tests__/__snapshots__/arrays.hoge index 6a8603832d3c1..4afcc23ad537a 100644 --- a/hogvm/__tests__/__snapshots__/arrays.hoge +++ b/hogvm/__tests__/__snapshots__/arrays.hoge @@ -1,4 +1,4 @@ -["_h", 43, 0, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 2, "print", 1, 35, 33, 1, 32, "2", 33, 3, 43, 3, 2, +["_H", 1, 43, 0, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 2, "print", 1, 35, 33, 1, 32, "2", 33, 3, 43, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 2, 33, 4, 43, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 33, 4, 43, 2, 43, 2, 33, 5, 43, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 36, 0, 33, 2, 45, 2, "print", 1, 35, 36, 0, 33, 2, 48, 2, "print", 1, 35, 36, 0, 33, 2, 48, 2, "print", 1, 35, 36, 0, 33, 7, 48, 2, "print", 1, 35, 36, 0, 33, 7, 48, 2, "print", @@ -11,17 +11,17 @@ 35, 33, 1, 33, 2, 33, 3, 33, 4, 43, 2, 43, 2, 33, 5, 43, 3, 33, 2, 45, 33, 2, 45, 33, 2, 45, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, 33, 1, 33, 2, 33, 1, 33, 2, 33, 3, 43, 3, 43, 3, 36, 1, 33, 2, 33, 4, 46, 36, 1, 33, 1, 45, 2, "print", 1, 35, 36, 1, 33, 2, 45, 2, "print", 1, 35, 36, 1, 33, 3, 45, 2, "print", 1, 35, 36, 1, 33, 3, 45, 33, 3, -33, 8, 46, 36, 1, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, 33, 4, 33, 1, 33, 2, 33, 3, 43, 3, 2, -"arrayPushBack", 2, 2, "print", 1, 35, 33, 0, 33, 1, 33, 2, 33, 3, 43, 3, 2, "arrayPushFront", 2, 2, "print", 1, 35, 33, +33, 8, 46, 36, 1, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 33, 4, 2, +"arrayPushBack", 2, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 33, 0, 2, "arrayPushFront", 2, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 2, "arrayPopBack", 1, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 2, "arrayPopFront", 1, 2, "print", 1, 35, 33, 3, 33, 2, 33, 1, 43, 3, 2, "arraySort", 1, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 2, -"arrayReverse", 1, 2, "print", 1, 35, 33, 3, 33, 2, 33, 1, 43, 3, 2, "arrayReverseSort", 1, 2, "print", 1, 35, 32, ",", -33, 1, 33, 2, 33, 3, 43, 3, 2, "arrayStringConcat", 2, 2, "print", 1, 35, 32, "-----", 2, "print", 1, 35, 33, 1, 33, 2, -33, 3, 33, 4, 43, 4, 36, 2, 2, "print", 1, 35, 33, 5, 36, 2, 2, "arrayPushBack", 2, 35, 36, 2, 2, "print", 1, 35, 33, 0, -36, 2, 2, "arrayPushFront", 2, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arrayPopBack", 1, 35, 36, 2, 2, "print", 1, 35, -36, 2, 2, "arrayPopFront", 1, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arraySort", 1, 35, 36, 2, 2, "print", 1, 35, 36, -2, 2, "arrayReverse", 1, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arrayReverseSort", 1, 35, 36, 2, 2, "print", 1, 35, -32, "------", 2, "print", 1, 35, 33, 0, 36, 2, 2, "has", 2, 2, "print", 1, 35, 33, 2, 36, 2, 2, "has", 2, 2, "print", 1, -35, 32, "banana", 36, 2, 2, "has", 2, 2, "print", 1, 35, 32, "banana", 32, "banananas", 2, "has", 2, 2, "print", 1, 35, -32, "foo", 32, "banananas", 2, "has", 2, 2, "print", 1, 35, 32, "1", 32, "1", 32, "2", 43, 2, 2, "has", 2, 2, "print", -1, 35, 35, 35, 35] +"arrayReverse", 1, 2, "print", 1, 35, 33, 3, 33, 2, 33, 1, 43, 3, 2, "arrayReverseSort", 1, 2, "print", 1, 35, 33, 1, +33, 2, 33, 3, 43, 3, 32, ",", 2, "arrayStringConcat", 2, 2, "print", 1, 35, 32, "-----", 2, "print", 1, 35, 33, 1, 33, +2, 33, 3, 33, 4, 43, 4, 36, 2, 2, "print", 1, 35, 36, 2, 33, 5, 2, "arrayPushBack", 2, 35, 36, 2, 2, "print", 1, 35, 36, +2, 33, 0, 2, "arrayPushFront", 2, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arrayPopBack", 1, 35, 36, 2, 2, "print", 1, +35, 36, 2, 2, "arrayPopFront", 1, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arraySort", 1, 35, 36, 2, 2, "print", 1, 35, +36, 2, 2, "arrayReverse", 1, 35, 36, 2, 2, "print", 1, 35, 36, 2, 2, "arrayReverseSort", 1, 35, 36, 2, 2, "print", 1, +35, 32, "------", 2, "print", 1, 35, 36, 2, 33, 0, 2, "has", 2, 2, "print", 1, 35, 36, 2, 33, 2, 2, "has", 2, 2, +"print", 1, 35, 36, 2, 32, "banana", 2, "has", 2, 2, "print", 1, 35, 32, "banananas", 32, "banana", 2, "has", 2, 2, +"print", 1, 35, 32, "banananas", 32, "foo", 2, "has", 2, 2, "print", 1, 35, 32, "1", 32, "2", 43, 2, 32, "1", 2, "has", +2, 2, "print", 1, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/catch.hoge b/hogvm/__tests__/__snapshots__/catch.hoge index 3fbbdda162693..e3588d7a1cdf9 100644 --- a/hogvm/__tests__/__snapshots__/catch.hoge +++ b/hogvm/__tests__/__snapshots__/catch.hoge @@ -1,11 +1,11 @@ -["_h", 41, "FishError", 1, 9, 31, 36, 0, 32, "FishError", 2, "HogError", 3, 38, 41, "FoodError", 1, 9, 31, 36, 0, 32, -"FoodError", 2, "HogError", 3, 38, 50, 10, 32, "You forgot to feed your fish", 2, "FishError", 1, 49, 51, 39, 55, 36, 0, -32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, -"concat", 2, 2, "print", 1, 35, 39, 25, 32, "FishError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, -"Problem with your fish: ", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 10, 32, -"Your fish are hungry", 2, "FoodError", 1, 49, 51, 39, 55, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, -36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, "concat", 2, 2, "print", 1, 35, 39, 25, 32, "FishError", -36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your fish: ", 2, "concat", 2, 2, "print", 1, 35, 39, 2, -35, 49, 35, 35, 50, 11, 31, 32, "Your fish are hungry", 2, "NotImplementedError", 2, 49, 51, 39, 45, 36, 0, 32, "type", -45, 32, "FoodError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, "concat", 2, 2, -"print", 1, 35, 39, 15, 36, 0, 32, "Unknown problem: ", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35] +["_H", 1, 41, "FishError", 1, 9, 32, "FishError", 36, 0, 31, 2, "HogError", 3, 38, 41, "FoodError", 1, 9, 32, +"FoodError", 36, 0, 31, 2, "HogError", 3, 38, 50, 10, 32, "You forgot to feed your fish", 2, "FishError", 1, 49, 51, 39, +55, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, +2, "concat", 2, 2, "print", 1, 35, 39, 25, 32, "FishError", 36, 1, 11, 40, 16, 32, "Problem with your fish: ", 36, 0, +32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 10, 32, "Your fish are hungry", 2, +"FoodError", 1, 49, 51, 39, 55, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, 32, +"Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 25, 32, "FishError", 36, 1, +11, 40, 16, 32, "Problem with your fish: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, +35, 35, 50, 11, 32, "Your fish are hungry", 31, 2, "NotImplementedError", 2, 49, 51, 39, 45, 36, 0, 32, "type", 45, 32, +"FoodError", 36, 1, 11, 40, 16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, +35, 39, 15, 32, "Unknown problem: ", 36, 0, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/catch2.hoge b/hogvm/__tests__/__snapshots__/catch2.hoge index 52aa53597834c..d99e3cd75ae3a 100644 --- a/hogvm/__tests__/__snapshots__/catch2.hoge +++ b/hogvm/__tests__/__snapshots__/catch2.hoge @@ -1,15 +1,15 @@ -["_h", 50, 50, 50, 13, 31, 32, "You forgot to feed your fish", 32, "FishError", 2, "HogError", 3, 49, 51, 39, 32, 36, 0, -32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, +["_H", 1, 50, 50, 50, 13, 32, "FishError", 32, "You forgot to feed your fish", 31, 2, "HogError", 3, 49, 51, 39, 32, 36, +0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, 16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 51, 39, 48, 36, 0, 32, "type", 45, 32, "FishError", 36, 1, 11, -40, 16, 36, 0, 32, "message", 45, 32, "FishError: ", 2, "concat", 2, 2, "print", 1, 35, 39, 18, 36, 0, 32, "message", -45, 32, "Error: ", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 50, 50, 13, 31, 32, -"You forgot to feed your fish", 32, "FunkyError", 2, "HogError", 3, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, -"FoodError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, "concat", 2, 2, "print", 1, -35, 39, 2, 35, 49, 35, 35, 51, 39, 55, 36, 0, 32, "type", 45, 32, "FishError", 36, 1, 11, 40, 16, 36, 0, 32, "message", -45, 32, "FishError: ", 2, "concat", 2, 2, "print", 1, 35, 39, 25, 36, 0, 32, "message", 45, 32, ": ", 36, 0, 32, "name", -45, 32, "Error of type ", 2, "concat", 4, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 50, 50, 13, 31, 32, -"You forgot to feed your fish", 32, "FishError", 2, "HogError", 3, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, -"FoodError", 36, 1, 11, 40, 16, 36, 0, 32, "message", 45, 32, "Problem with your food: ", 2, "concat", 2, 2, "print", 1, -35, 39, 2, 35, 49, 35, 35, 51, 39, 55, 36, 0, 32, "type", 45, 36, 0, 32, "message", 45, 32, ": ", 36, 0, 32, "name", 45, -32, "Error of type ", 2, "concat", 4, 2, "print", 1, 35, 39, 25, 32, "FishError", 36, 1, 11, 40, 16, 36, 0, 32, -"message", 45, 32, "FishError: ", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35] +40, 16, 32, "FishError: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 18, 32, "Error: ", 36, 0, +32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 50, 50, 13, 32, "FunkyError", 32, +"You forgot to feed your fish", 31, 2, "HogError", 3, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, +40, 16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, +35, 51, 39, 55, 36, 0, 32, "type", 45, 32, "FishError", 36, 1, 11, 40, 16, 32, "FishError: ", 36, 0, 32, "message", 45, +2, "concat", 2, 2, "print", 1, 35, 39, 25, 32, "Error of type ", 36, 0, 32, "name", 45, 32, ": ", 36, 0, 32, "message", +45, 2, "concat", 4, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 50, 50, 13, 32, "FishError", 32, +"You forgot to feed your fish", 31, 2, "HogError", 3, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, +40, 16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, +35, 51, 39, 55, 36, 0, 32, "type", 45, 32, "Error of type ", 36, 0, 32, "name", 45, 32, ": ", 36, 0, 32, "message", 45, +2, "concat", 4, 2, "print", 1, 35, 39, 25, 32, "FishError", 36, 1, 11, 40, 16, 32, "FishError: ", 36, 0, 32, "message", +45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/crypto.hoge b/hogvm/__tests__/__snapshots__/crypto.hoge index 5100cc860009a..e273437821c50 100644 --- a/hogvm/__tests__/__snapshots__/crypto.hoge +++ b/hogvm/__tests__/__snapshots__/crypto.hoge @@ -1,4 +1,4 @@ -["_h", 32, "this is a secure string", 36, 0, 32, "string:", 2, "print", 2, 35, 36, 0, 2, "md5Hex", 1, 32, -"md5Hex(string):", 2, "print", 2, 35, 36, 0, 2, "sha256Hex", 1, 32, "sha256Hex(string):", 2, "print", 2, 35, 32, "1", -32, "string", 32, "more", 32, "keys", 43, 4, 36, 1, 32, "data:", 2, "print", 2, 35, 36, 1, 2, "sha256HmacChainHex", 1, -32, "sha256HmacChainHex(data):", 2, "print", 2, 35, 35, 35] +["_H", 1, 32, "this is a secure string", 32, "string:", 36, 0, 2, "print", 2, 35, 32, "md5Hex(string):", 36, 0, 2, +"md5Hex", 1, 2, "print", 2, 35, 32, "sha256Hex(string):", 36, 0, 2, "sha256Hex", 1, 2, "print", 2, 35, 32, "1", 32, +"string", 32, "more", 32, "keys", 43, 4, 32, "data:", 36, 1, 2, "print", 2, 35, 32, "sha256HmacChainHex(data):", 36, 1, +2, "sha256HmacChainHex", 1, 2, "print", 2, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/date.hoge b/hogvm/__tests__/__snapshots__/date.hoge index a655406038a05..ab77e9efa77b3 100644 --- a/hogvm/__tests__/__snapshots__/date.hoge +++ b/hogvm/__tests__/__snapshots__/date.hoge @@ -1,28 +1,28 @@ -["_h", 33, 1234334543, 2, "fromUnixTimestamp", 1, 36, 0, 2, "print", 1, 35, 36, 0, 2, "toString", 1, 2, "print", 1, 35, -36, 0, 2, "toUnixTimestamp", 1, 2, "toInt", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 32, "2024-05-03", 2, +["_H", 1, 33, 1234334543, 2, "fromUnixTimestamp", 1, 36, 0, 2, "print", 1, 35, 36, 0, 2, "toString", 1, 2, "print", 1, +35, 36, 0, 2, "toUnixTimestamp", 1, 2, "toInt", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 32, "2024-05-03", 2, "toDate", 1, 36, 1, 2, "print", 1, 35, 36, 1, 2, "toString", 1, 2, "print", 1, 35, 36, 1, 2, "toUnixTimestamp", 1, 2, "toInt", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 32, "2024-05-03T12:34:56Z", 2, "toDateTime", 1, 36, 2, 2, "print", 1, 35, 36, 2, 2, "toString", 1, 2, "print", 1, 35, 36, 2, 2, "toUnixTimestamp", 1, 2, "toInt", 1, 2, "print", -1, 35, 32, "------", 2, "print", 1, 35, 32, "Europe/Brussels", 36, 2, 2, "toTimeZone", 2, 2, "print", 1, 35, 32, -"Europe/Brussels", 36, 2, 2, "toTimeZone", 2, 2, "toString", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 32, -"Europe/Tallinn", 36, 2, 2, "toTimeZone", 2, 2, "print", 1, 35, 32, "Europe/Tallinn", 36, 2, 2, "toTimeZone", 2, 2, -"toString", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 32, "America/New_York", 36, 2, 2, "toTimeZone", 2, 2, -"print", 1, 35, 32, "America/New_York", 36, 2, 2, "toTimeZone", 2, 2, "toString", 1, 2, "print", 1, 35, 32, "------", 2, -"print", 1, 35, 34, 1234334543.123, 2, "fromUnixTimestamp", 1, 36, 3, 32, "timestamp: ", -2, "print", 2, 35, 36, 3, 2, "toString", 1, 32, "toString(timestamp): ", 2, "print", 2, 35, 36, 3, -2, "toInt", 1, 32, "toInt(timestamp): ", 2, "print", 2, 35, 36, 3, 2, "toInt", 1, 2, -"toDateTime", 1, 32, "toDateTime(toInt(timestamp)): ", 2, "print", 2, 35, 36, 3, 2, "toInt", 1, 2, -"toDateTime", 1, 2, "toInt", 1, 32, "toInt(toDateTime(toInt(timestamp))): ", 2, "print", 2, 35, 36, 3, 2, "toInt", -1, 2, "toDateTime", 1, 2, "toString", 1, 32, "toString(toDateTime(toInt(timestamp))): ", 2, "print", 2, 35, 36, 3, 2, -"toFloat", 1, 32, "toFloat(timestamp): ", 2, "print", 2, 35, 36, 3, 2, "toFloat", 1, 2, -"toDateTime", 1, 32, "toDateTime(toFloat(timestamp)): ", 2, "print", 2, 35, 36, 3, 2, "toFloat", 1, 2, -"toDateTime", 1, 2, "toFloat", 1, 32, "toFloat(toDateTime(toFloat(timestamp))): ", 2, "print", 2, 35, 36, 3, 2, -"toFloat", 1, 2, "toDateTime", 1, 2, "toString", 1, 32, "toString(toDateTime(toFloat(timestamp))): ", 2, "print", 2, 35, -32, "------", 2, "print", 1, 35, 33, 1234334543123, 2, "fromUnixTimestampMilli", 1, 36, 4, 2, "toString", 1, 32, -"millisTs: ", 2, "print", 2, 35, 36, 4, 2, "toString", 1, 32, -"toString(millisTs): ", 2, "print", 2, 35, 36, 4, 2, "toInt", 1, 32, -"toInt(millisTs): ", 2, "print", 2, 35, 36, 4, 2, "toFloat", 1, 32, -"toFloat(millisTs): ", 2, "print", 2, 35, 36, 4, 2, "toUnixTimestampMilli", 1, 32, -"toUnixTimestampMilli(millisTs): ", 2, "print", 2, 35, 32, "------", 2, "print", 1, 35, 32, "2024-05-03", 2, -"toDate", 1, 36, 5, 2, "print", 1, 35, 36, 5, 2, "toString", 1, 2, "print", 1, 35, 36, 5, 2, "toInt", 1, 2, "print", 1, -35, 35, 35, 35, 35, 35, 35] +1, 35, 32, "------", 2, "print", 1, 35, 36, 2, 32, "Europe/Brussels", 2, "toTimeZone", 2, 2, "print", 1, 35, 36, 2, 32, +"Europe/Brussels", 2, "toTimeZone", 2, 2, "toString", 1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 36, 2, 32, +"Europe/Tallinn", 2, "toTimeZone", 2, 2, "print", 1, 35, 36, 2, 32, "Europe/Tallinn", 2, "toTimeZone", 2, 2, "toString", +1, 2, "print", 1, 35, 32, "-", 2, "print", 1, 35, 36, 2, 32, "America/New_York", 2, "toTimeZone", 2, 2, "print", 1, 35, +36, 2, 32, "America/New_York", 2, "toTimeZone", 2, 2, "toString", 1, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, +34, 1234334543.123, 2, "fromUnixTimestamp", 1, 32, "timestamp: ", 36, 3, 2, "print", 2, +35, 32, "toString(timestamp): ", 36, 3, 2, "toString", 1, 2, "print", 2, 35, 32, +"toInt(timestamp): ", 36, 3, 2, "toInt", 1, 2, "print", 2, 35, 32, +"toDateTime(toInt(timestamp)): ", 36, 3, 2, "toInt", 1, 2, "toDateTime", 1, 2, "print", 2, 35, 32, +"toInt(toDateTime(toInt(timestamp))): ", 36, 3, 2, "toInt", 1, 2, "toDateTime", 1, 2, "toInt", 1, 2, "print", 2, +35, 32, "toString(toDateTime(toInt(timestamp))): ", 36, 3, 2, "toInt", 1, 2, "toDateTime", 1, 2, "toString", 1, 2, +"print", 2, 35, 32, "toFloat(timestamp): ", 36, 3, 2, "toFloat", 1, 2, "print", 2, 35, 32, +"toDateTime(toFloat(timestamp)): ", 36, 3, 2, "toFloat", 1, 2, "toDateTime", 1, 2, "print", 2, 35, 32, +"toFloat(toDateTime(toFloat(timestamp))): ", 36, 3, 2, "toFloat", 1, 2, "toDateTime", 1, 2, "toFloat", 1, 2, "print", +2, 35, 32, "toString(toDateTime(toFloat(timestamp))): ", 36, 3, 2, "toFloat", 1, 2, "toDateTime", 1, 2, "toString", 1, +2, "print", 2, 35, 32, "------", 2, "print", 1, 35, 33, 1234334543123, 2, "fromUnixTimestampMilli", 1, 32, +"millisTs: ", 36, 4, 2, "toString", 1, 2, "print", 2, 35, 32, +"toString(millisTs): ", 36, 4, 2, "toString", 1, 2, "print", 2, 35, 32, +"toInt(millisTs): ", 36, 4, 2, "toInt", 1, 2, "print", 2, 35, 32, +"toFloat(millisTs): ", 36, 4, 2, "toFloat", 1, 2, "print", 2, 35, 32, +"toUnixTimestampMilli(millisTs): ", 36, 4, 2, "toUnixTimestampMilli", 1, 2, "print", 2, 35, 32, "------", 2, +"print", 1, 35, 32, "2024-05-03", 2, "toDate", 1, 36, 5, 2, "print", 1, 35, 36, 5, 2, "toString", 1, 2, "print", 1, 35, +36, 5, 2, "toInt", 1, 2, "print", 1, 35, 35, 35, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/dateFormat.hoge b/hogvm/__tests__/__snapshots__/dateFormat.hoge index eea4f091c7fbd..98706577e6cb1 100644 --- a/hogvm/__tests__/__snapshots__/dateFormat.hoge +++ b/hogvm/__tests__/__snapshots__/dateFormat.hoge @@ -1,32 +1,32 @@ -["_h", 34, 1234377543.123456, 2, "fromUnixTimestamp", 1, 32, "%Y-%m-%d %H:%i:%S", 36, 0, 2, "formatDateTime", 2, 2, -"print", 1, 35, 32, "Europe/Brussels", 32, "%Y-%m-%d %H:%i:%S", 36, 0, 2, "formatDateTime", 3, 2, "print", 1, 35, 32, -"America/New_York", 32, "%Y-%m-%d %H:%i:%S", 36, 0, 2, "formatDateTime", 3, 2, "print", 1, 35, 32, "%Y%m%dT%H%i%sZ", 36, -0, 2, "formatDateTime", 2, 2, "print", 1, 35, 32, "-----", 2, "print", 1, 35, 32, "%a", 36, 0, 2, "formatDateTime", 2, -32, "%a: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%b", 36, 0, 2, "formatDateTime", 2, 32, "%b: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%c", 36, 0, 2, "formatDateTime", 2, 32, "%c: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%C", 36, 0, -2, "formatDateTime", 2, 32, "%C: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%d", 36, 0, 2, "formatDateTime", 2, 32, -"%d: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%D", 36, 0, 2, "formatDateTime", 2, 32, "%D: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%e", 36, 0, 2, "formatDateTime", 2, 32, "%e: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%F", 36, 0, -2, "formatDateTime", 2, 32, "%F: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%g", 36, 0, 2, "formatDateTime", 2, 32, -"%g: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%G", 36, 0, 2, "formatDateTime", 2, 32, "%G: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%h", 36, 0, 2, "formatDateTime", 2, 32, "%h: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%H", 36, 0, -2, "formatDateTime", 2, 32, "%H: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%i", 36, 0, 2, "formatDateTime", 2, 32, -"%i: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%I", 36, 0, 2, "formatDateTime", 2, 32, "%I: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%j", 36, 0, 2, "formatDateTime", 2, 32, "%j: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%k", 36, 0, -2, "formatDateTime", 2, 32, "%k: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%l", 36, 0, 2, "formatDateTime", 2, 32, -"%l: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%m", 36, 0, 2, "formatDateTime", 2, 32, "%m: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%M", 36, 0, 2, "formatDateTime", 2, 32, "%M: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%n", 36, 0, -2, "formatDateTime", 2, 32, "%n: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%p", 36, 0, 2, "formatDateTime", 2, 32, -"%p: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%r", 36, 0, 2, "formatDateTime", 2, 32, "%r: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%R", 36, 0, 2, "formatDateTime", 2, 32, "%R: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%s", 36, 0, -2, "formatDateTime", 2, 32, "%s: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%S", 36, 0, 2, "formatDateTime", 2, 32, -"%S: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%t", 36, 0, 2, "formatDateTime", 2, 32, "%t: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%T", 36, 0, 2, "formatDateTime", 2, 32, "%T: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%u", 36, 0, -2, "formatDateTime", 2, 32, "%u: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%V", 36, 0, 2, "formatDateTime", 2, 32, -"%V: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%w", 36, 0, 2, "formatDateTime", 2, 32, "%w: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%W", 36, 0, 2, "formatDateTime", 2, 32, "%W: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%y", 36, 0, -2, "formatDateTime", 2, 32, "%y: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%Y", 36, 0, 2, "formatDateTime", 2, 32, -"%Y: ", 2, "concat", 2, 2, "print", 1, 35, 32, "%z", 36, 0, 2, "formatDateTime", 2, 32, "%z: ", 2, "concat", 2, 2, -"print", 1, 35, 32, "%%", 36, 0, 2, "formatDateTime", 2, 32, "%%: ", 2, "concat", 2, 2, "print", 1, 35, 32, "-----", 2, -"print", 1, 35, 32, "one banana", 36, 0, 2, "formatDateTime", 2, 2, "print", 1, 35, 32, -"%Y no way %m is this %d a %H real %i time %S", 36, 0, 2, "formatDateTime", 2, 2, "print", 1, 35, 35] +["_H", 1, 34, 1234377543.123456, 2, "fromUnixTimestamp", 1, 36, 0, 32, "%Y-%m-%d %H:%i:%S", 2, "formatDateTime", 2, 2, +"print", 1, 35, 36, 0, 32, "%Y-%m-%d %H:%i:%S", 32, "Europe/Brussels", 2, "formatDateTime", 3, 2, "print", 1, 35, 36, 0, +32, "%Y-%m-%d %H:%i:%S", 32, "America/New_York", 2, "formatDateTime", 3, 2, "print", 1, 35, 36, 0, 32, "%Y%m%dT%H%i%sZ", +2, "formatDateTime", 2, 2, "print", 1, 35, 32, "-----", 2, "print", 1, 35, 32, "%a: ", 36, 0, 32, "%a", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%b: ", 36, 0, 32, "%b", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%c: ", 36, 0, 32, "%c", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%C: ", 36, 0, 32, "%C", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%d: ", 36, 0, 32, "%d", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%D: ", 36, 0, 32, "%D", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%e: ", 36, 0, 32, "%e", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%F: ", 36, 0, 32, "%F", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%g: ", 36, 0, 32, "%g", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%G: ", 36, 0, 32, "%G", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%h: ", 36, 0, 32, "%h", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%H: ", 36, 0, 32, "%H", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%i: ", 36, 0, 32, "%i", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%I: ", 36, 0, 32, "%I", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%j: ", 36, 0, 32, "%j", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%k: ", 36, 0, 32, "%k", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%l: ", 36, 0, 32, "%l", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%m: ", 36, 0, 32, "%m", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%M: ", 36, 0, 32, "%M", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%n: ", 36, 0, 32, "%n", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%p: ", 36, 0, 32, "%p", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%r: ", 36, 0, 32, "%r", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%R: ", 36, 0, 32, "%R", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%s: ", 36, 0, 32, "%s", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%S: ", 36, 0, 32, "%S", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%t: ", 36, 0, 32, "%t", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%T: ", 36, 0, 32, "%T", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%u: ", 36, 0, 32, "%u", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%V: ", 36, 0, 32, "%V", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%w: ", 36, 0, 32, "%w", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%W: ", 36, 0, 32, "%W", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "%y: ", 36, 0, 32, "%y", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%Y: ", 36, 0, 32, "%Y", 2, +"formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, 32, "%z: ", 36, 0, 32, "%z", 2, "formatDateTime", 2, 2, +"concat", 2, 2, "print", 1, 35, 32, "%%: ", 36, 0, 32, "%%", 2, "formatDateTime", 2, 2, "concat", 2, 2, "print", 1, 35, +32, "-----", 2, "print", 1, 35, 36, 0, 32, "one banana", 2, "formatDateTime", 2, 2, "print", 1, 35, 36, 0, 32, +"%Y no way %m is this %d a %H real %i time %S", 2, "formatDateTime", 2, 2, "print", 1, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/dicts.hoge b/hogvm/__tests__/__snapshots__/dicts.hoge index 91aee718f176b..f0c4895e60b94 100644 --- a/hogvm/__tests__/__snapshots__/dicts.hoge +++ b/hogvm/__tests__/__snapshots__/dicts.hoge @@ -1,6 +1,6 @@ -["_h", 42, 0, 2, "print", 1, 35, 32, "key", 32, "value", 42, 1, 2, "print", 1, 35, 32, "key", 32, "value", 32, "other", -32, "thing", 42, 2, 2, "print", 1, 35, 32, "key", 32, "otherKey", 32, "value", 42, 1, 42, 1, 2, "print", 1, 35, 32, -"key", 1, 1, 32, "value", 42, 1, 2, "print", 1, 35, 33, 3, 36, 0, 32, "value", 42, 1, 2, "print", 1, 35, 32, "key", 32, -"value", 42, 1, 32, "key", 45, 2, "print", 1, 35, 32, "key", 32, "value", 42, 1, 32, "key", 45, 2, "print", 1, 35, 32, -"key", 32, "otherKey", 32, "value", 42, 1, 42, 1, 32, "key", 45, 32, "otherKey", 45, 2, "print", 1, 35, 32, "key", 32, -"otherKey", 32, "value", 42, 1, 42, 1, 32, "key", 45, 32, "otherKey", 45, 2, "print", 1, 35, 35] +["_H", 1, 42, 0, 2, "print", 1, 35, 32, "key", 32, "value", 42, 1, 2, "print", 1, 35, 32, "key", 32, "value", 32, +"other", 32, "thing", 42, 2, 2, "print", 1, 35, 32, "key", 32, "otherKey", 32, "value", 42, 1, 42, 1, 2, "print", 1, 35, +33, 3, 36, 0, 32, "value", 42, 1, 2, "print", 1, 35, 32, "key", 32, "value", 42, 1, 32, "key", 45, 2, "print", 1, 35, +32, "key", 32, "value", 42, 1, 32, "key", 45, 2, "print", 1, 35, 32, "key", 32, "otherKey", 32, "value", 42, 1, 42, 1, +32, "key", 45, 32, "otherKey", 45, 2, "print", 1, 35, 32, "key", 32, "otherKey", 32, "value", 42, 1, 42, 1, 32, "key", +45, 32, "otherKey", 45, 2, "print", 1, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/dicts.stdout b/hogvm/__tests__/__snapshots__/dicts.stdout index 6a8cdfef1b6eb..33e60af57d4e5 100644 --- a/hogvm/__tests__/__snapshots__/dicts.stdout +++ b/hogvm/__tests__/__snapshots__/dicts.stdout @@ -2,7 +2,6 @@ {'key': 'value'} {'key': 'value', 'other': 'thing'} {'key': {'otherKey': 'value'}} -{null: 'value'} {3: 'value'} value value diff --git a/hogvm/__tests__/__snapshots__/exceptions.hoge b/hogvm/__tests__/__snapshots__/exceptions.hoge index 41aa587028ab3..a4a5b1d16a4b2 100644 --- a/hogvm/__tests__/__snapshots__/exceptions.hoge +++ b/hogvm/__tests__/__snapshots__/exceptions.hoge @@ -1,14 +1,14 @@ -["_h", 32, "start", 2, "print", 1, 35, 50, 10, 32, "try", 2, "print", 1, 35, 51, 39, 22, 36, 0, 32, "type", 45, 32, -" was the exception", 36, 0, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, +["_H", 1, 32, "start", 2, "print", 1, 35, 50, 10, 32, "try", 2, "print", 1, 35, 51, 39, 22, 36, 0, 32, "type", 45, 36, +0, 32, " was the exception", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, "print", 1, 35, 32, "start", 2, "print", 1, 35, 50, 10, 32, "try", 2, "print", 1, 35, 51, 39, 17, 36, 0, 32, "type", 45, 32, "No var for error, but no error", 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, "print", 1, -35, 50, 16, 32, "try again", 2, "print", 1, 35, 31, 31, 2, "Error", 2, 49, 51, 39, 22, 36, 0, 32, "type", 45, 32, -" was the exception", 36, 0, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, -"print", 1, 35, 50, 16, 32, "try again", 2, "print", 1, 35, 31, 31, 2, "Error", 2, 49, 51, 39, 17, 36, 0, 32, "type", -45, 32, "No var for error", 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, "print", 1, 35, 41, -"third", 0, 15, 32, "Throwing in third", 2, "print", 1, 35, 31, 32, "Threw in third", 2, "Error", 2, 49, 31, 38, 41, -"second", 0, 12, 32, "second", 2, "print", 1, 35, 2, "third", 0, 35, 31, 38, 41, "first", 0, 12, 32, "first", 2, -"print", 1, 35, 2, "second", 0, 35, 31, 38, 41, "base", 0, 42, 32, "base", 2, "print", 1, 35, 50, 8, 2, "first", 0, 35, -51, 39, 25, 36, 0, 32, "type", 45, 36, 0, 32, "Caught in base: ", 2, "concat", 2, 2, "print", 1, 35, 36, 0, 49, 39, 2, -35, 49, 35, 35, 31, 38, 50, 8, 2, "base", 0, 35, 51, 39, 22, 36, 0, 32, "type", 45, 36, 0, 32, "Caught in root: ", 2, -"concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "The end", 2, "print", 1, 35] +35, 50, 16, 32, "try again", 2, "print", 1, 35, 31, 31, 2, "Error", 2, 49, 51, 39, 22, 36, 0, 32, "type", 45, 36, 0, 32, +" was the exception", 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, "print", 1, +35, 50, 16, 32, "try again", 2, "print", 1, 35, 31, 31, 2, "Error", 2, 49, 51, 39, 17, 36, 0, 32, "type", 45, 32, +"No var for error", 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "------------------", 2, "print", 1, 35, 41, "third", +0, 15, 32, "Throwing in third", 2, "print", 1, 35, 32, "Threw in third", 31, 2, "Error", 2, 49, 31, 38, 41, "second", 0, +12, 32, "second", 2, "print", 1, 35, 2, "third", 0, 35, 31, 38, 41, "first", 0, 12, 32, "first", 2, "print", 1, 35, 2, +"second", 0, 35, 31, 38, 41, "base", 0, 42, 32, "base", 2, "print", 1, 35, 50, 8, 2, "first", 0, 35, 51, 39, 25, 36, 0, +32, "type", 45, 32, "Caught in base: ", 36, 0, 2, "concat", 2, 2, "print", 1, 35, 36, 0, 49, 39, 2, 35, 49, 35, 35, 31, +38, 50, 8, 2, "base", 0, 35, 51, 39, 22, 36, 0, 32, "type", 45, 32, "Caught in root: ", 36, 0, 2, "concat", 2, 2, +"print", 1, 35, 39, 2, 35, 49, 35, 35, 32, "The end", 2, "print", 1, 35] diff --git a/hogvm/__tests__/__snapshots__/functions.hoge b/hogvm/__tests__/__snapshots__/functions.hoge index aa61602e4a1c7..0351cb7ce179d 100644 --- a/hogvm/__tests__/__snapshots__/functions.hoge +++ b/hogvm/__tests__/__snapshots__/functions.hoge @@ -1,14 +1,14 @@ -["_h", 32, "-- test functions --", 2, "print", 1, 35, 41, "add", 2, 6, 36, 0, 36, 1, 6, 38, 41, "add2", 2, 9, 36, 0, 36, -1, 6, 36, 2, 38, 35, 41, "mult", 2, 6, 36, 0, 36, 1, 8, 38, 41, "noArgs", 0, 12, 32, "basdfasdf", 33, 3, 33, 2, 6, 36, -1, 38, 35, 35, 41, "empty", 0, 2, 31, 38, 41, "empty2", 0, 2, 31, 38, 41, "empty3", 0, 2, 31, 38, 41, "noReturn", 0, 14, -33, 1, 33, 2, 36, 1, 36, 0, 6, 31, 38, 35, 35, 35, 41, "emptyReturn", 0, 2, 31, 38, 41, "emptyReturnBeforeOtherStuff", -0, 10, 31, 38, 33, 2, 33, 2, 6, 35, 31, 38, 41, "emptyReturnBeforeOtherStuffNoSemicolon", 0, 6, 33, 2, 33, 2, 6, 38, 41, -"ifThenReturn", 0, 8, 30, 40, 2, 31, 38, 33, 4, 38, 33, 4, 33, 3, 2, "add", 2, 2, "print", 1, 35, 33, 1, 33, 1, 2, -"add", 2, 33, 100, 33, 4, 33, 3, 2, "add", 2, 6, 6, 2, "print", 1, 35, 2, "noArgs", 0, 47, 3, 35, 33, -1, 2, "print", 1, -35, 2, "empty", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "empty2", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, -"empty3", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "noReturn", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, -"emptyReturn", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "emptyReturnBeforeOtherStuff", 0, 47, 3, 35, 33, -1, 2, -"print", 1, 35, 2, "emptyReturnBeforeOtherStuffNoSemicolon", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "ifThenReturn", -0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 33, 2, 33, 1, 33, 2, 2, "add", 2, 33, 100, 33, 4, 33, 3, 2, "add", 2, 6, 6, 2, -"mult", 2, 2, "print", 1, 35, 33, 10, 33, 1, 33, 2, 2, "add2", 2, 33, 100, 33, 4, 33, 3, 2, "add2", 2, 6, 6, 2, "mult", -2, 2, "print", 1, 35] +["_H", 1, 32, "-- test functions --", 2, "print", 1, 35, 41, "add", 2, 6, 36, 1, 36, 0, 6, 38, 41, "add2", 2, 9, 36, 1, +36, 0, 6, 36, 2, 38, 35, 41, "mult", 2, 6, 36, 1, 36, 0, 8, 38, 41, "noArgs", 0, 12, 32, "basdfasdf", 33, 3, 33, 2, 6, +36, 1, 38, 35, 35, 41, "empty", 0, 2, 31, 38, 41, "empty2", 0, 2, 31, 38, 41, "empty3", 0, 2, 31, 38, 41, "noReturn", 0, +14, 33, 1, 33, 2, 36, 1, 36, 0, 6, 31, 38, 35, 35, 35, 41, "emptyReturn", 0, 2, 31, 38, 41, +"emptyReturnBeforeOtherStuff", 0, 10, 31, 38, 33, 2, 33, 2, 6, 35, 31, 38, 41, "emptyReturnBeforeOtherStuffNoSemicolon", +0, 6, 33, 2, 33, 2, 6, 38, 41, "ifThenReturn", 0, 8, 30, 40, 2, 31, 38, 33, 4, 38, 33, 3, 33, 4, 2, "add", 2, 2, +"print", 1, 35, 33, 1, 33, 1, 2, "add", 2, 33, 100, 33, 3, 33, 4, 2, "add", 2, 6, 6, 2, "print", 1, 35, 2, "noArgs", 0, +47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "empty", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "empty2", 0, 47, 3, 35, +33, -1, 2, "print", 1, 35, 2, "empty3", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "noReturn", 0, 47, 3, 35, 33, -1, 2, +"print", 1, 35, 2, "emptyReturn", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, "emptyReturnBeforeOtherStuff", 0, 47, 3, +35, 33, -1, 2, "print", 1, 35, 2, "emptyReturnBeforeOtherStuffNoSemicolon", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 2, +"ifThenReturn", 0, 47, 3, 35, 33, -1, 2, "print", 1, 35, 33, 2, 33, 1, 2, "add", 2, 33, 100, 33, 3, 33, 4, 2, "add", 2, +6, 6, 33, 2, 2, "mult", 2, 2, "print", 1, 35, 33, 2, 33, 1, 2, "add2", 2, 33, 100, 33, 3, 33, 4, 2, "add2", 2, 6, 6, 33, +10, 2, "mult", 2, 2, "print", 1, 35] diff --git a/hogvm/__tests__/__snapshots__/ifElse.hoge b/hogvm/__tests__/__snapshots__/ifElse.hoge index 0aaef24a74f80..b5deffdd6f5f7 100644 --- a/hogvm/__tests__/__snapshots__/ifElse.hoge +++ b/hogvm/__tests__/__snapshots__/ifElse.hoge @@ -1,4 +1,4 @@ -["_h", 32, "-- test if else --", 2, "print", 1, 35, 29, 40, 8, 33, 1, 2, "print", 1, 35, 39, 6, 33, 2, 2, "print", 1, +["_H", 1, 32, "-- test if else --", 2, "print", 1, 35, 29, 40, 8, 33, 1, 2, "print", 1, 35, 39, 6, 33, 2, 2, "print", 1, 35, 29, 40, 8, 33, 1, 2, "print", 1, 35, 39, 6, 33, 2, 2, "print", 1, 35, 30, 40, 8, 33, 1, 2, "print", 1, 35, 39, 6, 33, 2, 2, "print", 1, 35, 29, 40, 8, 33, 1, 2, "print", 1, 35, 39, 6, 33, 2, 2, "print", 1, 35, 29, 36, 0, 40, 14, 33, 3, 33, 2, 36, 1, 6, 2, "print", 1, 35, 35, 39, 6, 33, 2, 2, "print", 1, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/ifJump.hoge b/hogvm/__tests__/__snapshots__/ifJump.hoge index 0e946f4101f07..f90184dea86fd 100644 --- a/hogvm/__tests__/__snapshots__/ifJump.hoge +++ b/hogvm/__tests__/__snapshots__/ifJump.hoge @@ -1,3 +1,3 @@ -["_h", 42, 0, 36, 0, 32, "email", 45, 32, "", 36, 1, 11, 40, 12, 32, "ERROR - Email not found!", 2, "print", 1, 35, 32, -"3", 2, "print", 1, 35, 32, "1", 2, "print", 1, 35, 32, "", 36, 1, 11, 40, 14, 32, "ERROR - Email not found!", 2, +["_H", 1, 42, 0, 36, 0, 32, "email", 45, 32, "", 36, 1, 11, 40, 12, 32, "ERROR - Email not found!", 2, "print", 1, 35, +32, "3", 2, "print", 1, 35, 32, "1", 2, "print", 1, 35, 32, "", 36, 1, 11, 40, 14, 32, "ERROR - Email not found!", 2, "print", 1, 35, 32, "3", 2, "print", 1, 35, 39, 6, 32, "else", 2, "print", 1, 35, 32, "1", 2, "print", 1, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/json.hoge b/hogvm/__tests__/__snapshots__/json.hoge index df9948388e519..e23e7a772b6b9 100644 --- a/hogvm/__tests__/__snapshots__/json.hoge +++ b/hogvm/__tests__/__snapshots__/json.hoge @@ -1,3 +1,3 @@ -["_h", 32, "[1,2,3]", 2, "jsonParse", 1, 2, "print", 1, 35, 32, "event", 32, "$pageview", 32, "properties", 32, +["_H", 1, 32, "[1,2,3]", 2, "jsonParse", 1, 2, "print", 1, 35, 32, "event", 32, "$pageview", 32, "properties", 32, "$browser", 32, "Chrome", 32, "$os", 32, "Windows", 42, 2, 42, 2, 36, 0, 2, "jsonStringify", 1, 36, 1, 2, "jsonParse", 1, 2, "print", 1, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/keysValues.hoge b/hogvm/__tests__/__snapshots__/keysValues.hoge index 594b0869a792b..83f13a3825362 100644 --- a/hogvm/__tests__/__snapshots__/keysValues.hoge +++ b/hogvm/__tests__/__snapshots__/keysValues.hoge @@ -1,4 +1,4 @@ -["_h", 33, 3, 33, 4, 33, 5, 43, 3, 33, 3, 33, 4, 33, 5, 44, 3, 32, "key", 32, "value", 32, "other", 32, "val", 42, 2, +["_H", 1, 33, 3, 33, 4, 33, 5, 43, 3, 33, 3, 33, 4, 33, 5, 44, 3, 32, "key", 32, "value", 32, "other", 32, "val", 42, 2, 32, ">> A", 2, "print", 1, 35, 36, 0, 2, "keys", 1, 2, "print", 1, 35, 36, 0, 2, "values", 1, 2, "print", 1, 35, 32, ">> B", 2, "print", 1, 35, 36, 1, 2, "keys", 1, 2, "print", 1, 35, 36, 1, 2, "values", 1, 2, "print", 1, 35, 32, ">> C", 2, "print", 1, 35, 36, 2, 2, "keys", 1, 2, "print", 1, 35, 36, 2, 2, "values", 1, 2, "print", 1, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/loops.hoge b/hogvm/__tests__/__snapshots__/loops.hoge index a3328220d6bd9..51d41fb4ceadd 100644 --- a/hogvm/__tests__/__snapshots__/loops.hoge +++ b/hogvm/__tests__/__snapshots__/loops.hoge @@ -1,22 +1,22 @@ -["_h", 32, "-- test while loop --", 2, "print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 33, 1, 36, 0, 6, 37, 0, 36, 0, -2, "print", 1, 35, 39, -22, 36, 0, 2, "print", 1, 35, 35, 32, "-- test for loop --", 2, "print", 1, 35, 33, 0, 33, 3, -36, 0, 15, 40, 15, 36, 0, 2, "print", 1, 35, 33, 1, 36, 0, 6, 37, 0, 39, -22, 35, 32, "i", 1, 1, 2, "print", 1, 35, 32, -"-- test emptier for loop --", 2, "print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 32, "woo", 2, "print", 1, 35, 33, 1, -36, 0, 6, 37, 0, 39, -22, 32, "hoo", 2, "print", 1, 35, 35, 32, "-- for in loop with arrays --", 2, "print", 1, 35, 33, -1, 33, 2, 33, 3, 43, 3, 36, 0, 36, 1, 2, "values", 1, 33, 1, 36, 2, 2, "length", 1, 31, 36, 4, 36, 3, 16, 40, 22, 36, 2, -36, 3, 45, 37, 5, 36, 5, 2, "print", 1, 35, 36, 3, 33, 1, 6, 37, 3, 39, -29, 35, 35, 35, 35, 35, 35, 32, -"-- for in loop with arrays and keys --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 36, 0, 36, 1, 2, "keys", 1, 36, -1, 2, "values", 1, 33, 1, 36, 3, 2, "length", 1, 31, 31, 36, 5, 36, 4, 16, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, -4, 45, 37, 7, 36, 7, 36, 6, 2, "print", 2, 35, 36, 4, 33, 1, 6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35, 32, -"-- for in loop with tuples --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 36, 0, 36, 1, 2, "values", 1, 33, 1, 36, -2, 2, "length", 1, 31, 36, 4, 36, 3, 16, 40, 22, 36, 2, 36, 3, 45, 37, 5, 36, 5, 2, "print", 1, 35, 36, 3, 33, 1, 6, 37, -3, 39, -29, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with tuples and keys --", 2, "print", 1, 35, 33, 1, 33, 2, 33, -3, 44, 3, 36, 0, 36, 1, 2, "keys", 1, 36, 1, 2, "values", 1, 33, 1, 36, 3, 2, "length", 1, 31, 31, 36, 5, 36, 4, 16, 40, -31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 7, 36, 6, 2, "print", 2, 35, 36, 4, 33, 1, 6, 37, 4, 39, -38, -35, 35, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with dicts --", 2, "print", 1, 35, 32, "first", 32, "v1", 32, -"second", 32, "v2", 32, "third", 32, "v3", 42, 3, 36, 0, 36, 1, 2, "values", 1, 33, 1, 36, 2, 2, "length", 1, 31, 36, 4, -36, 3, 16, 40, 22, 36, 2, 36, 3, 45, 37, 5, 36, 5, 2, "print", 1, 35, 36, 3, 33, 1, 6, 37, 3, 39, -29, 35, 35, 35, 35, -35, 35, 32, "-- for in loop with dicts and keys --", 2, "print", 1, 35, 32, "first", 32, "v1", 32, "second", 32, "v2", -32, "third", 32, "v3", 42, 3, 36, 0, 36, 1, 2, "keys", 1, 36, 1, 2, "values", 1, 33, 1, 36, 3, 2, "length", 1, 31, 31, -36, 5, 36, 4, 16, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 7, 36, 6, 2, "print", 2, 35, 36, 4, 33, -1, 6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35] +["_H", 1, 32, "-- test while loop --", 2, "print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 33, 1, 36, 0, 6, 37, 0, 36, +0, 2, "print", 1, 35, 39, -22, 36, 0, 2, "print", 1, 35, 35, 32, "-- test for loop --", 2, "print", 1, 35, 33, 0, 33, 3, +36, 0, 15, 40, 15, 36, 0, 2, "print", 1, 35, 33, 1, 36, 0, 6, 37, 0, 39, -22, 35, 32, "-- test emptier for loop --", 2, +"print", 1, 35, 33, 0, 33, 3, 36, 0, 15, 40, 15, 32, "woo", 2, "print", 1, 35, 33, 1, 36, 0, 6, 37, 0, 39, -22, 32, +"hoo", 2, "print", 1, 35, 35, 32, "-- for in loop with arrays --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 36, 0, +36, 1, 2, "values", 1, 33, 1, 36, 2, 2, "length", 1, 31, 36, 4, 36, 3, 16, 40, 22, 36, 2, 36, 3, 45, 37, 5, 36, 5, 2, +"print", 1, 35, 36, 3, 33, 1, 6, 37, 3, 39, -29, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with arrays and keys --", +2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 43, 3, 36, 0, 36, 1, 2, "keys", 1, 36, 1, 2, "values", 1, 33, 1, 36, 3, 2, +"length", 1, 31, 31, 36, 5, 36, 4, 16, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 6, 36, 7, 2, +"print", 2, 35, 36, 4, 33, 1, 6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, 35, 35, 32, "-- for in loop with tuples --", 2, +"print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 36, 0, 36, 1, 2, "values", 1, 33, 1, 36, 2, 2, "length", 1, 31, 36, 4, 36, +3, 16, 40, 22, 36, 2, 36, 3, 45, 37, 5, 36, 5, 2, "print", 1, 35, 36, 3, 33, 1, 6, 37, 3, 39, -29, 35, 35, 35, 35, 35, +35, 32, "-- for in loop with tuples and keys --", 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 36, 0, 36, 1, 2, +"keys", 1, 36, 1, 2, "values", 1, 33, 1, 36, 3, 2, "length", 1, 31, 31, 36, 5, 36, 4, 16, 40, 31, 36, 2, 36, 4, 45, 37, +6, 36, 3, 36, 4, 45, 37, 7, 36, 6, 36, 7, 2, "print", 2, 35, 36, 4, 33, 1, 6, 37, 4, 39, -38, 35, 35, 35, 35, 35, 35, +35, 35, 32, "-- for in loop with dicts --", 2, "print", 1, 35, 32, "first", 32, "v1", 32, "second", 32, "v2", 32, +"third", 32, "v3", 42, 3, 36, 0, 36, 1, 2, "values", 1, 33, 1, 36, 2, 2, "length", 1, 31, 36, 4, 36, 3, 16, 40, 22, 36, +2, 36, 3, 45, 37, 5, 36, 5, 2, "print", 1, 35, 36, 3, 33, 1, 6, 37, 3, 39, -29, 35, 35, 35, 35, 35, 35, 32, +"-- for in loop with dicts and keys --", 2, "print", 1, 35, 32, "first", 32, "v1", 32, "second", 32, "v2", 32, "third", +32, "v3", 42, 3, 36, 0, 36, 1, 2, "keys", 1, 36, 1, 2, "values", 1, 33, 1, 36, 3, 2, "length", 1, 31, 31, 36, 5, 36, 4, +16, 40, 31, 36, 2, 36, 4, 45, 37, 6, 36, 3, 36, 4, 45, 37, 7, 36, 6, 36, 7, 2, "print", 2, 35, 36, 4, 33, 1, 6, 37, 4, +39, -38, 35, 35, 35, 35, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/loops.stdout b/hogvm/__tests__/__snapshots__/loops.stdout index 270cb30b7ea47..0f4ad9c7493c5 100644 --- a/hogvm/__tests__/__snapshots__/loops.stdout +++ b/hogvm/__tests__/__snapshots__/loops.stdout @@ -7,7 +7,6 @@ 0 1 2 -null -- test emptier for loop -- woo woo diff --git a/hogvm/__tests__/__snapshots__/mandelbrot.hoge b/hogvm/__tests__/__snapshots__/mandelbrot.hoge index 211995efe2bc9..51895c2281458 100644 --- a/hogvm/__tests__/__snapshots__/mandelbrot.hoge +++ b/hogvm/__tests__/__snapshots__/mandelbrot.hoge @@ -1,8 +1,8 @@ -["_h", 41, "mandelbrot", 3, 93, 34, 0.0, 34, 0.0, 33, 0, 36, 0, 36, 5, 15, 33, 4, 36, 4, 36, 4, 8, 36, 3, 36, 3, 8, 6, -16, 3, 2, 40, 44, 36, 2, 36, 4, 36, 4, 8, 36, 3, 36, 3, 8, 7, 6, 36, 1, 36, 4, 36, 3, 33, 2, 8, 8, 6, 36, 6, 37, 3, 36, -7, 37, 4, 33, 1, 36, 5, 6, 37, 5, 35, 35, 39, -67, 36, 0, 36, 5, 11, 40, 5, 32, " ", 38, 39, 3, 32, "#", 38, 31, 38, 35, -35, 35, 41, "main", 0, 119, 33, 80, 33, 24, 34, -2.0, 34, 1.0, 34, -1.0, 34, 1.0, 33, 30, 33, 0, 36, 1, 36, 7, 15, 40, -86, 32, "", 33, 0, 36, 0, 36, 9, 15, 40, 58, 36, 2, 36, 2, 36, 3, 7, 36, 0, 36, 9, 9, 8, 6, 36, 4, 36, 4, 36, 5, 7, 36, -1, 36, 7, 9, 8, 6, 36, 6, 36, 11, 36, 10, 2, "mandelbrot", 3, 36, 12, 36, 8, 2, "concat", 2, 37, 8, 33, 1, 36, 9, 6, 37, -9, 35, 35, 35, 39, -65, 36, 8, 2, "print", 1, 35, 33, 1, 36, 7, 6, 37, 7, 35, 35, 39, -93, 31, 38, 35, 35, 35, 35, 35, -35, 35, 35, 2, "main", 0, 35] +["_H", 1, 41, "mandelbrot", 3, 93, 34, 0.0, 34, 0.0, 33, 0, 33, 4, 36, 4, 36, 4, 8, 36, 3, 36, 3, 8, 6, 16, 36, 2, 36, +5, 15, 3, 2, 40, 44, 36, 0, 36, 4, 36, 4, 8, 36, 3, 36, 3, 8, 7, 6, 36, 1, 36, 4, 36, 3, 33, 2, 8, 8, 6, 36, 6, 37, 3, +36, 7, 37, 4, 33, 1, 36, 5, 6, 37, 5, 35, 35, 39, -67, 36, 2, 36, 5, 11, 40, 5, 32, " ", 38, 39, 3, 32, "#", 38, 31, 38, +35, 35, 35, 41, "main", 0, 119, 33, 80, 33, 24, 34, -2.0, 34, 1.0, 34, -1.0, 34, 1.0, 33, 30, 33, 0, 36, 1, 36, 7, 15, +40, 86, 32, "", 33, 0, 36, 0, 36, 9, 15, 40, 58, 36, 2, 36, 2, 36, 3, 7, 36, 0, 36, 9, 9, 8, 6, 36, 4, 36, 4, 36, 5, 7, +36, 1, 36, 7, 9, 8, 6, 36, 10, 36, 11, 36, 6, 2, "mandelbrot", 3, 36, 8, 36, 12, 2, "concat", 2, 37, 8, 33, 1, 36, 9, 6, +37, 9, 35, 35, 35, 39, -65, 36, 8, 2, "print", 1, 35, 33, 1, 36, 7, 6, 37, 7, 35, 35, 39, -93, 31, 38, 35, 35, 35, 35, +35, 35, 35, 35, 2, "main", 0, 35] diff --git a/hogvm/__tests__/__snapshots__/operations.hoge b/hogvm/__tests__/__snapshots__/operations.hoge index 82156f1818f45..3abc0604f4040 100644 --- a/hogvm/__tests__/__snapshots__/operations.hoge +++ b/hogvm/__tests__/__snapshots__/operations.hoge @@ -1,26 +1,23 @@ -["_h", 41, "test", 1, 11, 36, 0, 2, "jsonStringify", 1, 2, "print", 1, 35, 31, 38, 32, +["_H", 1, 41, "test", 1, 11, 36, 0, 2, "jsonStringify", 1, 2, "print", 1, 35, 31, 38, 32, "-- test the most common expressions --", 2, "print", 1, 35, 33, 2, 33, 1, 6, 2, "test", 1, 35, 33, 2, 33, 1, 7, 2, "test", 1, 35, 33, 2, 33, 3, 8, 2, "test", 1, 35, 33, 2, 33, 3, 9, 2, "test", 1, 35, 33, 2, 33, 3, 10, 2, "test", 1, 35, -33, 2, 33, 1, 3, 2, 2, "test", 1, 35, 33, 0, 33, 1, 4, 2, 2, "test", 1, 35, 33, 0, 33, 1, 3, 2, 2, "test", 1, 35, 33, 2, -33, 1, 33, 0, 3, 2, 33, 1, 4, 3, 2, "test", 1, 35, 33, 1, 33, 0, 33, 1, 3, 3, 2, "test", 1, 35, 33, 2, 33, 1, 4, 2, 33, -2, 33, 1, 4, 2, 3, 2, 2, "test", 1, 35, 29, 2, "test", 1, 35, 29, 5, 2, "test", 1, 35, 30, 2, "test", 1, 35, 31, 2, +33, 1, 33, 2, 3, 2, 2, "test", 1, 35, 33, 1, 33, 0, 4, 2, 2, "test", 1, 35, 33, 1, 33, 0, 3, 2, 2, "test", 1, 35, 33, 1, +33, 0, 33, 1, 3, 2, 33, 2, 4, 3, 2, "test", 1, 35, 33, 1, 33, 0, 33, 1, 3, 3, 2, "test", 1, 35, 33, 1, 33, 2, 4, 2, 33, +1, 33, 2, 4, 2, 3, 2, 2, "test", 1, 35, 29, 2, "test", 1, 35, 29, 5, 2, "test", 1, 35, 30, 2, "test", 1, 35, 31, 2, "test", 1, 35, 34, 3.14, 2, "test", 1, 35, 33, 2, 33, 1, 11, 2, "test", 1, 35, 33, 2, 33, 1, 11, 2, "test", 1, 35, 33, 2, 33, 1, 12, 2, "test", 1, 35, 33, 2, 33, 1, 15, 2, "test", 1, 35, 33, 2, 33, 1, 16, 2, "test", 1, 35, 33, 2, 33, 1, 13, 2, "test", 1, 35, 33, 2, 33, 1, 14, 2, "test", 1, 35, 32, "b", 32, "a", 17, 2, "test", 1, 35, 32, "%a%", 32, "baa", 17, 2, "test", 1, 35, 32, "%x%", 32, "baa", 17, 2, "test", 1, 35, 32, "%A%", 32, "baa", 18, 2, "test", 1, 35, 32, "%C%", 32, "baa", 18, 2, "test", 1, 35, 32, "b", 32, "a", 18, 2, "test", 1, 35, 32, "b", 32, "a", 19, 2, "test", 1, 35, 32, "b", 32, "a", 20, 2, "test", 1, 35, 32, "car", 32, "a", 21, 2, "test", 1, 35, 32, "foo", 32, "a", 21, 2, "test", 1, 35, -32, "car", 32, "a", 22, 2, "test", 1, 35, 32, "bla", 32, "properties", 1, 2, 2, "test", 1, 35, 32, "foo", 32, -"properties", 1, 2, 2, "test", 1, 35, 32, "foo", 32, "properties", 1, 2, 47, 2, 35, 30, 2, "test", 1, 35, 32, -"nullValue", 32, "properties", 1, 2, 47, 2, 35, 30, 2, "test", 1, 35, 32, "another", 32, "arg", 2, "concat", 2, 2, -"test", 1, 35, 31, 33, 1, 2, "concat", 2, 2, "test", 1, 35, 30, 29, 2, "concat", 2, 2, "test", 1, 35, 32, "e.*", 32, -"test", 2, "match", 2, 2, "test", 1, 35, 32, "^e.*", 32, "test", 2, "match", 2, 2, "test", 1, 35, 32, "x.*", 32, "test", -2, "match", 2, 2, "test", 1, 35, 32, "e.*", 32, "test", 23, 2, "test", 1, 35, 32, "e.*", 32, "test", 24, 2, "test", 1, -35, 32, "^e.*", 32, "test", 23, 2, "test", 1, 35, 32, "^e.*", 32, "test", 24, 2, "test", 1, 35, 32, "x.*", 32, "test", -23, 2, "test", 1, 35, 32, "x.*", 32, "test", 24, 2, "test", 1, 35, 32, "EST", 32, "test", 25, 2, "test", 1, 35, 32, -"EST", 32, "test", 25, 2, "test", 1, 35, 32, "EST", 32, "test", 26, 2, "test", 1, 35, 33, 1, 2, "toString", 1, 2, -"test", 1, 35, 34, 1.5, 2, "toString", 1, 2, "test", 1, 35, 29, 2, "toString", 1, 2, "test", 1, 35, 31, 2, "toString", -1, 2, "test", 1, 35, 32, "string", 2, "toString", 1, 2, "test", 1, 35, 32, "1", 2, "toInt", 1, 2, "test", 1, 35, 32, -"bla", 2, "toInt", 1, 2, "test", 1, 35, 32, "1.2", 2, "toFloat", 1, 2, "test", 1, 35, 32, "bla", 2, "toFloat", 1, 2, -"test", 1, 35, 32, "asd", 2, "toUUID", 1, 2, "test", 1, 35, 31, 33, 1, 11, 2, "test", 1, 35, 31, 33, 1, 12, 2, "test", -1, 35] +32, "car", 32, "a", 22, 2, "test", 1, 35, 32, "arg", 32, "another", 2, "concat", 2, 2, "test", 1, 35, 33, 1, 31, 2, +"concat", 2, 2, "test", 1, 35, 29, 30, 2, "concat", 2, 2, "test", 1, 35, 32, "test", 32, "e.*", 2, "match", 2, 2, +"test", 1, 35, 32, "test", 32, "^e.*", 2, "match", 2, 2, "test", 1, 35, 32, "test", 32, "x.*", 2, "match", 2, 2, "test", +1, 35, 32, "e.*", 32, "test", 23, 2, "test", 1, 35, 32, "e.*", 32, "test", 24, 2, "test", 1, 35, 32, "^e.*", 32, "test", +23, 2, "test", 1, 35, 32, "^e.*", 32, "test", 24, 2, "test", 1, 35, 32, "x.*", 32, "test", 23, 2, "test", 1, 35, 32, +"x.*", 32, "test", 24, 2, "test", 1, 35, 32, "EST", 32, "test", 25, 2, "test", 1, 35, 32, "EST", 32, "test", 25, 2, +"test", 1, 35, 32, "EST", 32, "test", 26, 2, "test", 1, 35, 33, 1, 2, "toString", 1, 2, "test", 1, 35, 34, 1.5, 2, +"toString", 1, 2, "test", 1, 35, 29, 2, "toString", 1, 2, "test", 1, 35, 31, 2, "toString", 1, 2, "test", 1, 35, 32, +"string", 2, "toString", 1, 2, "test", 1, 35, 32, "1", 2, "toInt", 1, 2, "test", 1, 35, 32, "bla", 2, "toInt", 1, 2, +"test", 1, 35, 32, "1.2", 2, "toFloat", 1, 2, "test", 1, 35, 32, "bla", 2, "toFloat", 1, 2, "test", 1, 35, 32, "asd", 2, +"toUUID", 1, 2, "test", 1, 35, 31, 33, 1, 11, 2, "test", 1, 35, 31, 33, 1, 12, 2, "test", 1, 35] diff --git a/hogvm/__tests__/__snapshots__/operations.stdout b/hogvm/__tests__/__snapshots__/operations.stdout index 26b21bc43634d..5017b0e3005c7 100644 --- a/hogvm/__tests__/__snapshots__/operations.stdout +++ b/hogvm/__tests__/__snapshots__/operations.stdout @@ -33,10 +33,6 @@ true true false false -null -null -false -false "arganother" "1" "truefalse" diff --git a/hogvm/__tests__/__snapshots__/printLoops.hoge b/hogvm/__tests__/__snapshots__/printLoops.hoge index a53576adf8f3e..db0bc1d43e494 100644 --- a/hogvm/__tests__/__snapshots__/printLoops.hoge +++ b/hogvm/__tests__/__snapshots__/printLoops.hoge @@ -1,4 +1,4 @@ -["_h", 32, "key", 32, "value", 32, "key2", 32, "value2", 42, 2, 32, "na", 33, 0, 33, 100, 36, 2, 15, 40, 45, 32, "na", -36, 1, 2, "concat", 2, 37, 1, 36, 0, 36, 2, 32, "key_", 2, "concat", 2, 32, "wasted", 32, " batman!", 36, 1, 32, -"memory: ", 2, "concat", 3, 32, "something", 36, 0, 42, 2, 46, 33, 1, 36, 2, 6, 37, 2, 39, -52, 35, 36, 0, 2, "print", +["_H", 1, 32, "key", 32, "value", 32, "key2", 32, "value2", 42, 2, 32, "na", 33, 0, 33, 100, 36, 2, 15, 40, 45, 36, 1, +32, "na", 2, "concat", 2, 37, 1, 36, 0, 32, "key_", 36, 2, 2, "concat", 2, 32, "wasted", 32, "memory: ", 36, 1, 32, +" batman!", 2, "concat", 3, 32, "something", 36, 0, 42, 2, 46, 33, 1, 36, 2, 6, 37, 2, 39, -52, 35, 36, 0, 2, "print", 1, 35, 36, 0, 2, "jsonStringify", 1, 36, 2, 2, "jsonParse", 1, 2, "print", 1, 35, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/printLoops2.hoge b/hogvm/__tests__/__snapshots__/printLoops2.hoge index 487604661c03e..071363c073c55 100644 --- a/hogvm/__tests__/__snapshots__/printLoops2.hoge +++ b/hogvm/__tests__/__snapshots__/printLoops2.hoge @@ -1,4 +1,4 @@ -["_h", 32, "key", 32, "value", 32, "key2", 32, "value2", 42, 2, 32, "key", 32, "value", 32, "key2", 32, "value2", 42, 2, -33, 0, 33, 30, 36, 2, 15, 40, 25, 36, 0, 36, 2, 32, "key_", 2, "concat", 2, 32, "something", 36, 1, 42, 1, 46, 33, 1, +["_H", 1, 32, "key", 32, "value", 32, "key2", 32, "value2", 42, 2, 32, "key", 32, "value", 32, "key2", 32, "value2", 42, +2, 33, 0, 33, 30, 36, 2, 15, 40, 25, 36, 0, 32, "key_", 36, 2, 2, "concat", 2, 32, "something", 36, 1, 42, 1, 46, 33, 1, 36, 2, 6, 37, 2, 39, -32, 35, 36, 0, 2, "print", 1, 35, 36, 0, 2, "jsonStringify", 1, 2, "jsonParse", 1, 2, "print", 1, 35, 35, 35] diff --git a/hogvm/__tests__/__snapshots__/properties.hoge b/hogvm/__tests__/__snapshots__/properties.hoge index 39ec0648c4063..4ce7c2821449d 100644 --- a/hogvm/__tests__/__snapshots__/properties.hoge +++ b/hogvm/__tests__/__snapshots__/properties.hoge @@ -1,4 +1,4 @@ -["_h", 33, 1, 33, 2, 32, "d", 33, 1, 33, 3, 33, 42, 33, 6, 44, 4, 42, 1, 43, 3, 36, 0, 33, 3, 45, 32, "d", 45, 33, 2, +["_H", 1, 33, 1, 33, 2, 32, "d", 33, 1, 33, 3, 33, 42, 33, 6, 44, 4, 42, 1, 43, 3, 36, 0, 33, 3, 45, 32, "d", 45, 33, 2, 45, 2, "print", 1, 35, 35, 33, 1, 33, 2, 32, "d", 33, 1, 33, 3, 33, 42, 33, 6, 44, 4, 42, 1, 43, 3, 36, 0, 33, 3, 45, 32, "d", 45, 33, 3, 45, 2, "print", 1, 35, 35, 33, 1, 33, 2, 32, "d", 33, 1, 33, 3, 33, 42, 33, 6, 44, 4, 42, 1, 43, 3, 36, 0, 33, 3, 45, 32, "d", 45, 33, 4, 45, 2, "print", 1, 35, 35, 32, "d", 33, 1, 33, 3, 33, 42, 33, 6, 44, 4, 42, 1, 36, diff --git a/hogvm/__tests__/__snapshots__/stl.hoge b/hogvm/__tests__/__snapshots__/stl.hoge index d9e6ec9c41f51..3f842a7d4156f 100644 --- a/hogvm/__tests__/__snapshots__/stl.hoge +++ b/hogvm/__tests__/__snapshots__/stl.hoge @@ -1,26 +1,26 @@ -["_h", 32, "-- empty, notEmpty, length, lower, upper, reverse --", 2, "print", 1, 35, 32, "234", 2, "notEmpty", 1, 32, -"", 2, "empty", 1, 3, 2, 40, 9, 32, "123", 2, "length", 1, 2, "print", 1, 35, 32, "tdd4gh", 32, "Tdd4gh", 2, "lower", 1, -11, 40, 9, 32, "test", 2, "upper", 1, 2, "print", 1, 35, 32, "spinner", 2, "reverse", 1, 2, "print", 1, 35, 32, "", 2, -"print", 1, 35, 32, "-- encodeURLComponent, decodeURLComponent --", 2, "print", 1, 35, 32, "http://www.google.com", 2, -"encodeURLComponent", 1, 2, "print", 1, 35, 32, "tom & jerry", 2, "encodeURLComponent", 1, 2, "print", 1, 35, 32, -"http://www.google.com", 2, "encodeURLComponent", 1, 2, "decodeURLComponent", 1, 2, "print", 1, 35, 32, "tom & jerry", -2, "encodeURLComponent", 1, 2, "decodeURLComponent", 1, 2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, -"-- base64Encode, base64Decode --", 2, "print", 1, 35, 32, "http://www.google.com", 2, "base64Encode", 1, 2, "print", 1, -35, 32, "tom & jerry", 2, "base64Encode", 1, 2, "print", 1, 35, 32, "http://www.google.com", 2, "base64Encode", 1, 2, -"base64Decode", 1, 2, "print", 1, 35, 32, "tom & jerry", 2, "base64Encode", 1, 2, "base64Decode", 1, 2, "print", 1, 35, -32, "", 2, "print", 1, 35, 32, "-- empty --", 2, "print", 1, 35, 31, 2, "empty", 1, 2, "print", 1, 35, 33, 0, 2, -"empty", 1, 2, "print", 1, 35, 33, 1, 2, "empty", 1, 2, "print", 1, 35, 33, -1, 2, "empty", 1, 2, "print", 1, 35, 34, -0.0, 2, "empty", 1, 2, "print", 1, 35, 34, 0.01, 2, "empty", 1, 2, "print", 1, 35, 32, "", 2, "empty", 1, 2, "print", 1, -35, 32, "string", 2, "empty", 1, 2, "print", 1, 35, 32, "0", 2, "empty", 1, 2, "print", 1, 35, 43, 0, 2, "empty", 1, 2, -"print", 1, 35, 42, 0, 2, "empty", 1, 2, "print", 1, 35, 2, "tuple", 0, 2, "empty", 1, 2, "print", 1, 35, 33, 0, 2, -"tuple", 1, 2, "empty", 1, 2, "print", 1, 35, 33, 2, 33, 1, 2, "tuple", 2, 2, "empty", 1, 2, "print", 1, 35, 32, "", 2, -"print", 1, 35, 32, "-- notEmpty --", 2, "print", 1, 35, 31, 2, "notEmpty", 1, 2, "print", 1, 35, 33, 0, 2, "notEmpty", -1, 2, "print", 1, 35, 33, 1, 2, "notEmpty", 1, 2, "print", 1, 35, 33, -1, 2, "notEmpty", 1, 2, "print", 1, 35, 34, 0.0, -2, "notEmpty", 1, 2, "print", 1, 35, 34, 0.01, 2, "notEmpty", 1, 2, "print", 1, 35, 32, "", 2, "notEmpty", 1, 2, -"print", 1, 35, 32, "string", 2, "notEmpty", 1, 2, "print", 1, 35, 32, "0", 2, "notEmpty", 1, 2, "print", 1, 35, 43, 0, -2, "notEmpty", 1, 2, "print", 1, 35, 42, 0, 2, "notEmpty", 1, 2, "print", 1, 35, 2, "tuple", 0, 2, "notEmpty", 1, 2, -"print", 1, 35, 33, 0, 2, "tuple", 1, 2, "notEmpty", 1, 2, "print", 1, 35, 33, 2, 33, 1, 2, "tuple", 2, 2, "notEmpty", -1, 2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, "-- replaceAll, replaceOne --", 2, "print", 1, 35, 32, "L", 32, -"l", 32, "hello world", 2, "replaceAll", 3, 2, "print", 1, 35, 32, "L", 32, "l", 32, "hello world", 2, "replaceOne", 3, -2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, "-- generateUUIDv4 --", 2, "print", 1, 35, 2, "generateUUIDv4", 0, 2, -"length", 1, 2, "print", 1, 35] +["_H", 1, 32, "-- empty, notEmpty, length, lower, upper, reverse --", 2, "print", 1, 35, 32, "", 2, "empty", 1, 32, +"234", 2, "notEmpty", 1, 3, 2, 40, 9, 32, "123", 2, "length", 1, 2, "print", 1, 35, 32, "tdd4gh", 32, "Tdd4gh", 2, +"lower", 1, 11, 40, 9, 32, "test", 2, "upper", 1, 2, "print", 1, 35, 32, "spinner", 2, "reverse", 1, 2, "print", 1, 35, +32, "", 2, "print", 1, 35, 32, "-- encodeURLComponent, decodeURLComponent --", 2, "print", 1, 35, 32, +"http://www.google.com", 2, "encodeURLComponent", 1, 2, "print", 1, 35, 32, "tom & jerry", 2, "encodeURLComponent", 1, +2, "print", 1, 35, 32, "http://www.google.com", 2, "encodeURLComponent", 1, 2, "decodeURLComponent", 1, 2, "print", 1, +35, 32, "tom & jerry", 2, "encodeURLComponent", 1, 2, "decodeURLComponent", 1, 2, "print", 1, 35, 32, "", 2, "print", 1, +35, 32, "-- base64Encode, base64Decode --", 2, "print", 1, 35, 32, "http://www.google.com", 2, "base64Encode", 1, 2, +"print", 1, 35, 32, "tom & jerry", 2, "base64Encode", 1, 2, "print", 1, 35, 32, "http://www.google.com", 2, +"base64Encode", 1, 2, "base64Decode", 1, 2, "print", 1, 35, 32, "tom & jerry", 2, "base64Encode", 1, 2, "base64Decode", +1, 2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, "-- empty --", 2, "print", 1, 35, 31, 2, "empty", 1, 2, "print", 1, +35, 33, 0, 2, "empty", 1, 2, "print", 1, 35, 33, 1, 2, "empty", 1, 2, "print", 1, 35, 33, -1, 2, "empty", 1, 2, "print", +1, 35, 34, 0.0, 2, "empty", 1, 2, "print", 1, 35, 34, 0.01, 2, "empty", 1, 2, "print", 1, 35, 32, "", 2, "empty", 1, 2, +"print", 1, 35, 32, "string", 2, "empty", 1, 2, "print", 1, 35, 32, "0", 2, "empty", 1, 2, "print", 1, 35, 43, 0, 2, +"empty", 1, 2, "print", 1, 35, 42, 0, 2, "empty", 1, 2, "print", 1, 35, 2, "tuple", 0, 2, "empty", 1, 2, "print", 1, 35, +33, 0, 2, "tuple", 1, 2, "empty", 1, 2, "print", 1, 35, 33, 1, 33, 2, 2, "tuple", 2, 2, "empty", 1, 2, "print", 1, 35, +32, "", 2, "print", 1, 35, 32, "-- notEmpty --", 2, "print", 1, 35, 31, 2, "notEmpty", 1, 2, "print", 1, 35, 33, 0, 2, +"notEmpty", 1, 2, "print", 1, 35, 33, 1, 2, "notEmpty", 1, 2, "print", 1, 35, 33, -1, 2, "notEmpty", 1, 2, "print", 1, +35, 34, 0.0, 2, "notEmpty", 1, 2, "print", 1, 35, 34, 0.01, 2, "notEmpty", 1, 2, "print", 1, 35, 32, "", 2, "notEmpty", +1, 2, "print", 1, 35, 32, "string", 2, "notEmpty", 1, 2, "print", 1, 35, 32, "0", 2, "notEmpty", 1, 2, "print", 1, 35, +43, 0, 2, "notEmpty", 1, 2, "print", 1, 35, 42, 0, 2, "notEmpty", 1, 2, "print", 1, 35, 2, "tuple", 0, 2, "notEmpty", 1, +2, "print", 1, 35, 33, 0, 2, "tuple", 1, 2, "notEmpty", 1, 2, "print", 1, 35, 33, 1, 33, 2, 2, "tuple", 2, 2, +"notEmpty", 1, 2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, "-- replaceAll, replaceOne --", 2, "print", 1, 35, 32, +"hello world", 32, "l", 32, "L", 2, "replaceAll", 3, 2, "print", 1, 35, 32, "hello world", 32, "l", 32, "L", 2, +"replaceOne", 3, 2, "print", 1, 35, 32, "", 2, "print", 1, 35, 32, "-- generateUUIDv4 --", 2, "print", 1, 35, 2, +"generateUUIDv4", 0, 2, "length", 1, 2, "print", 1, 35] diff --git a/hogvm/__tests__/__snapshots__/strings.hoge b/hogvm/__tests__/__snapshots__/strings.hoge index 11abe250f3a09..0c3118d3a043e 100644 --- a/hogvm/__tests__/__snapshots__/strings.hoge +++ b/hogvm/__tests__/__snapshots__/strings.hoge @@ -1,11 +1,11 @@ -["_h", 32, " hello world ", 2, "trim", 1, 2, "print", 1, 35, 32, " hello world ", 2, "trimLeft", 1, 2, "print", 1, -35, 32, " hello world ", 2, "trimRight", 1, 2, "print", 1, 35, 32, "x", 32, "xxxx hello world xx", 2, "trim", 2, 2, -"print", 1, 35, 32, "x", 32, "xxxx hello world xx", 2, "trimLeft", 2, 2, "print", 1, 35, 32, "x", 32, -"xxxx hello world xx", 2, "trimRight", 2, 2, "print", 1, 35, 32, "hello world and more", 32, " ", 2, "splitByString", -2, 2, "print", 1, 35, 33, 1, 32, "hello world and more", 32, " ", 2, "splitByString", 3, 2, "print", 1, 35, 33, 2, 32, -"hello world and more", 32, " ", 2, "splitByString", 3, 2, "print", 1, 35, 33, 10, 32, "hello world and more", 32, " ", -2, "splitByString", 3, 2, "print", 1, 35, 32, "N", 32, "banana", 2, "like", 2, 2, "print", 1, 35, 32, "n", 32, "banana", -2, "like", 2, 2, "print", 1, 35, 32, "naan", 32, "banana", 2, "like", 2, 2, "print", 1, 35, 32, "N", 32, "banana", 2, -"ilike", 2, 2, "print", 1, 35, 32, "n", 32, "banana", 2, "ilike", 2, 2, "print", 1, 35, 32, "naan", 32, "banana", 2, -"ilike", 2, 2, "print", 1, 35, 32, "N", 32, "banana", 2, "notLike", 2, 2, "print", 1, 35, 32, "NO", 32, "banana", 2, -"notILike", 2, 2, "print", 1, 35] +["_H", 1, 32, " hello world ", 2, "trim", 1, 2, "print", 1, 35, 32, " hello world ", 2, "trimLeft", 1, 2, "print", +1, 35, 32, " hello world ", 2, "trimRight", 1, 2, "print", 1, 35, 32, "xxxx hello world xx", 32, "x", 2, "trim", 2, +2, "print", 1, 35, 32, "xxxx hello world xx", 32, "x", 2, "trimLeft", 2, 2, "print", 1, 35, 32, +"xxxx hello world xx", 32, "x", 2, "trimRight", 2, 2, "print", 1, 35, 32, " ", 32, "hello world and more", 2, +"splitByString", 2, 2, "print", 1, 35, 32, " ", 32, "hello world and more", 33, 1, 2, "splitByString", 3, 2, "print", 1, +35, 32, " ", 32, "hello world and more", 33, 2, 2, "splitByString", 3, 2, "print", 1, 35, 32, " ", 32, +"hello world and more", 33, 10, 2, "splitByString", 3, 2, "print", 1, 35, 32, "banana", 32, "N", 2, "like", 2, 2, +"print", 1, 35, 32, "banana", 32, "n", 2, "like", 2, 2, "print", 1, 35, 32, "banana", 32, "naan", 2, "like", 2, 2, +"print", 1, 35, 32, "banana", 32, "N", 2, "ilike", 2, 2, "print", 1, 35, 32, "banana", 32, "n", 2, "ilike", 2, 2, +"print", 1, 35, 32, "banana", 32, "naan", 2, "ilike", 2, 2, "print", 1, 35, 32, "banana", 32, "N", 2, "notLike", 2, 2, +"print", 1, 35, 32, "banana", 32, "NO", 2, "notILike", 2, 2, "print", 1, 35] diff --git a/hogvm/__tests__/__snapshots__/tuples.hoge b/hogvm/__tests__/__snapshots__/tuples.hoge index 027d8ad266374..eb8c97ec6bf03 100644 --- a/hogvm/__tests__/__snapshots__/tuples.hoge +++ b/hogvm/__tests__/__snapshots__/tuples.hoge @@ -1,5 +1,5 @@ -["_h", 2, "tuple", 0, 2, "print", 1, 35, 33, 1, 44, 1, 2, "print", 1, 35, 33, 1, 33, 2, 44, 2, 2, "print", 1, 35, 33, 1, -33, 2, 44, 2, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 2, "print", 1, 35, 33, 1, 32, "2", 33, 3, 44, 3, 2, +["_H", 1, 2, "tuple", 0, 2, "print", 1, 35, 33, 1, 44, 1, 2, "print", 1, 35, 33, 1, 33, 2, 44, 2, 2, "print", 1, 35, 33, +1, 33, 2, 44, 2, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 2, "print", 1, 35, 33, 1, 32, "2", 33, 3, 44, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 2, 33, 4, 44, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 33, 4, 44, 2, 44, 2, 33, 5, 44, 3, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 44, 3, 36, 0, 33, 2, 45, 2, "print", 1, 35, 36, 0, 33, 2, 48, 2, "print", 1, 35, 36, 0, 33, 8, 48, 2, "print", 1, 35, 33, 1, 33, 2, 33, 3, 33, 4, 44, 2, 44, 2, 33, 5, 44, 3, 33, 2, 45, diff --git a/hogvm/__tests__/__snapshots__/variables.hoge b/hogvm/__tests__/__snapshots__/variables.hoge index b3f91623a1b0d..91f17e1f63b04 100644 --- a/hogvm/__tests__/__snapshots__/variables.hoge +++ b/hogvm/__tests__/__snapshots__/variables.hoge @@ -1,3 +1,3 @@ -["_h", 32, "-- test variables --", 2, "print", 1, 35, 33, 2, 33, 1, 6, 36, 0, 2, "print", 1, 35, 33, 4, 36, 0, 6, 36, 1, -2, "print", 1, 35, 35, 35, 32, "-- test variable reassignment --", 2, "print", 1, 35, 33, 1, 33, 3, 36, 0, 6, 37, 0, 33, -2, 36, 0, 8, 37, 0, 36, 0, 2, "print", 1, 35, 35] +["_H", 1, 32, "-- test variables --", 2, "print", 1, 35, 33, 2, 33, 1, 6, 36, 0, 2, "print", 1, 35, 33, 4, 36, 0, 6, 36, +1, 2, "print", 1, 35, 35, 35, 32, "-- test variable reassignment --", 2, "print", 1, 35, 33, 1, 33, 3, 36, 0, 6, 37, 0, +33, 2, 36, 0, 8, 37, 0, 36, 0, 2, "print", 1, 35, 35] diff --git a/hogvm/__tests__/dicts.hog b/hogvm/__tests__/dicts.hog index ccd081190b0da..8523c26cb5f8d 100644 --- a/hogvm/__tests__/dicts.hog +++ b/hogvm/__tests__/dicts.hog @@ -2,7 +2,6 @@ print({}) print({'key': 'value'}) print({'key': 'value', 'other': 'thing'}) print({'key': {'otherKey': 'value'}}) -print({key: 'value'}) let key := 3 print({key: 'value'}) diff --git a/hogvm/__tests__/loops.hog b/hogvm/__tests__/loops.hog index 369baf58a2339..f43fe7fcc1c39 100644 --- a/hogvm/__tests__/loops.hog +++ b/hogvm/__tests__/loops.hog @@ -11,9 +11,8 @@ print('-- test while loop --') print('-- test for loop --') { for (let i := 0; i < 3; i := i + 1) { - print(i) -- prints 3 times + print(i) // prints 3 times } - print(i) -- global does not print } print('-- test emptier for loop --') diff --git a/hogvm/__tests__/mandelbrot.hog b/hogvm/__tests__/mandelbrot.hog index 6ac17b2989d57..144ddf701ab87 100644 --- a/hogvm/__tests__/mandelbrot.hog +++ b/hogvm/__tests__/mandelbrot.hog @@ -41,4 +41,4 @@ fn main() { } } -main() \ No newline at end of file +main() diff --git a/hogvm/__tests__/operations.hog b/hogvm/__tests__/operations.hog index 25eca892ab190..8445bf17cee67 100644 --- a/hogvm/__tests__/operations.hog +++ b/hogvm/__tests__/operations.hog @@ -3,68 +3,64 @@ fn test(val) { } print('-- test the most common expressions --') -test(1 + 2) -- 3 -test(1 - 2) -- -1 -test(3 * 2) -- 6 -test(3 / 2) -- 1.5 -test(3 % 2) -- 1 -test(1 and 2) -- true -test(1 or 0) -- true -test(1 and 0) -- false -test(1 or (0 and 1) or 2) -- true -test((1 and 0) and 1) -- false -test((1 or 2) and (1 or 2)) -- true -test(true) -- true -test(not true) -- false -test(false) -- false -test(null) -- null -test(3.14) -- 3.14 -test(1 = 2) -- false -test(1 == 2) -- false -test(1 != 2) -- true -test(1 < 2) -- true -test(1 <= 2) -- true -test(1 > 2) -- false -test(1 >= 2) -- false -test('a' like 'b') -- false -test('baa' like '%a%') -- true -test('baa' like '%x%') -- false -test('baa' ilike '%A%') -- true -test('baa' ilike '%C%') -- false -test('a' ilike 'b') -- false -test('a' not like 'b') -- true -test('a' not ilike 'b') -- true -test('a' in 'car') -- true -test('a' in 'foo') -- false -test('a' not in 'car') -- false -test(properties.bla) -- null -test(properties.foo) -- "bar" -test(ifNull(properties.foo, false)) -- "bar" -test(ifNull(properties.nullValue, false)) -- false -test(concat('arg', 'another')) -- 'arganother' -test(concat(1, NULL)) -- '1' -test(concat(true, false)) -- 'truefalse' -test(match('test', 'e.*')) -- true -test(match('test', '^e.*')) -- false -test(match('test', 'x.*')) -- false -test('test' =~ 'e.*') -- true -test('test' !~ 'e.*') -- false -test('test' =~ '^e.*') -- false -test('test' !~ '^e.*') -- true -test('test' =~ 'x.*') -- false -test('test' !~ 'x.*') -- true -test('test' ~* 'EST') -- true -test('test' =~* 'EST') -- true -test('test' !~* 'EST') -- false -test(toString(1)) -- '1' -test(toString(1.5)) -- '1.5' -test(toString(true)) -- 'true' -test(toString(null)) -- 'null' -test(toString('string')) -- 'string' -test(toInt('1')) -- 1 -test(toInt('bla')) -- null -test(toFloat('1.2')) -- 1.2 -test(toFloat('bla')) -- null -test(toUUID('asd')) -- 'asd' -test(1 == null) -- false -test(1 != null) -- true +test(1 + 2) // 3 +test(1 - 2) // -1 +test(3 * 2) // 6 +test(3 / 2) // 1.5 +test(3 % 2) // 1 +test(1 and 2) // true +test(1 or 0) // true +test(1 and 0) // false +test(1 or (0 and 1) or 2) // true +test((1 and 0) and 1) // false +test((1 or 2) and (1 or 2)) // true +test(true) // true +test(not true) // false +test(false) // false +test(null) // null +test(3.14) // 3.14 +test(1 = 2) // false +test(1 == 2) // false +test(1 != 2) // true +test(1 < 2) // true +test(1 <= 2) // true +test(1 > 2) // false +test(1 >= 2) // false +test('a' like 'b') // false +test('baa' like '%a%') // true +test('baa' like '%x%') // false +test('baa' ilike '%A%') // true +test('baa' ilike '%C%') // false +test('a' ilike 'b') // false +test('a' not like 'b') // true +test('a' not ilike 'b') // true +test('a' in 'car') // true +test('a' in 'foo') // false +test('a' not in 'car') // false +test(concat('arg', 'another')) // 'arganother' +test(concat(1, NULL)) // '1' +test(concat(true, false)) // 'truefalse' +test(match('test', 'e.*')) // true +test(match('test', '^e.*')) // false +test(match('test', 'x.*')) // false +test('test' =~ 'e.*') // true +test('test' !~ 'e.*') // false +test('test' =~ '^e.*') // false +test('test' !~ '^e.*') // true +test('test' =~ 'x.*') // false +test('test' !~ 'x.*') // true +test('test' ~* 'EST') // true +test('test' =~* 'EST') // true +test('test' !~* 'EST') // false +test(toString(1)) // '1' +test(toString(1.5)) // '1.5' +test(toString(true)) // 'true' +test(toString(null)) // 'null' +test(toString('string')) // 'string' +test(toInt('1')) // 1 +test(toInt('bla')) // null +test(toFloat('1.2')) // 1.2 +test(toFloat('bla')) // null +test(toUUID('asd')) // 'asd' +test(1 == null) // false +test(1 != null) // true diff --git a/hogvm/python/debugger.py b/hogvm/python/debugger.py index 270c1a5233c01..10d4355e8c1c4 100644 --- a/hogvm/python/debugger.py +++ b/hogvm/python/debugger.py @@ -163,8 +163,8 @@ def print_symbol(symbol: Operation, ip: int, bytecode: list, stack: list, call_s ) case Operation.DECLARE_FN: return f"DECLARE_FN({bytecode[ip+1]}, args={bytecode[ip+2]}, ops={bytecode[ip+3]})" - case Operation.CALL: - return f"CALL({bytecode[ip+1]} {', '.join(str(stack[-i]) for i in range(bytecode[ip+2]))})" + case Operation.CALL_GLOBAL: + return f"CALL_GLOBAL({bytecode[ip+1]} {', '.join(str(stack[-(bytecode[ip+2] - i)]) for i in range(bytecode[ip+2]))})" case Operation.TRY: return f"TRY(+{bytecode[ip+1]})" case Operation.POP_TRY: @@ -177,8 +177,8 @@ def print_symbol(symbol: Operation, ip: int, bytecode: list, stack: list, call_s def color_bytecode(bytecode: list) -> list: - colored = ["op.START"] - ip = 1 + colored = ["op.START", f"version: {bytecode[1]}"] if bytecode[0] == "_H" else ["op.START"] + ip = len(colored) while ip < len(bytecode): symbol = bytecode[ip] match symbol: @@ -274,8 +274,8 @@ def color_bytecode(bytecode: list) -> list: add = ["op.JUMP_IF_STACK_NOT_NULL", f"offset: {'+' if bytecode[ip+1] >= 0 else ''}{bytecode[ip+1]}"] case Operation.DECLARE_FN: add = ["op.DECLARE_FN", f"name: {bytecode[ip+1]}", f"args: {bytecode[ip+2]}", f"ops: {bytecode[ip+3]}"] - case Operation.CALL: - add = ["op.CALL", f"name: {bytecode[ip+1]}", f"args: {bytecode[ip+2]}"] + case Operation.CALL_GLOBAL: + add = ["op.CALL_GLOBAL", f"name: {bytecode[ip+1]}", f"args: {bytecode[ip+2]}"] case Operation.TRY: add = ["op.TRY", f"catch: +{bytecode[ip+1]}"] case Operation.POP_TRY: diff --git a/hogvm/python/execute.py b/hogvm/python/execute.py index c137868d026e5..1f0a47e1bd1d9 100644 --- a/hogvm/python/execute.py +++ b/hogvm/python/execute.py @@ -7,7 +7,7 @@ from hogvm.python.debugger import debugger, color_bytecode from hogvm.python.objects import is_hog_error -from hogvm.python.operation import Operation, HOGQL_BYTECODE_IDENTIFIER +from hogvm.python.operation import Operation, HOGQL_BYTECODE_IDENTIFIER, HOGQL_BYTECODE_IDENTIFIER_V0 from hogvm.python.stl import STL from dataclasses import dataclass @@ -41,6 +41,9 @@ def execute_bytecode( team: Optional["Team"] = None, debug=False, ) -> BytecodeResult: + if len(bytecode) == 0 or (bytecode[0] not in (HOGQL_BYTECODE_IDENTIFIER, HOGQL_BYTECODE_IDENTIFIER_V0)): + raise HogVMException(f"Invalid bytecode. Must start with '{HOGQL_BYTECODE_IDENTIFIER}'") + version = bytecode[1] if len(bytecode) >= 2 and bytecode[0] == HOGQL_BYTECODE_IDENTIFIER else 0 result = None start_time = time.time() last_op = len(bytecode) - 1 @@ -51,13 +54,19 @@ def execute_bytecode( declared_functions: dict[str, tuple[int, int]] = {} mem_used = 0 max_mem_used = 0 - ip = -1 + ip = 1 if version > 0 else 0 ops = 0 stdout: list[str] = [] colored_bytecode = color_bytecode(bytecode) if debug else [] if isinstance(timeout, int): timeout = timedelta(seconds=timeout) + def stack_keep_first_elements(count: int): + nonlocal stack, mem_stack, mem_used + stack = stack[0:count] + mem_used -= sum(mem_stack[count:]) + mem_stack = mem_stack[0:count] + def next_token(): nonlocal ip ip += 1 @@ -82,10 +91,7 @@ def push_stack(value): if mem_used > MAX_MEMORY: raise HogVMException(f"Memory limit of {MAX_MEMORY} bytes exceeded. Tried to allocate {mem_used} bytes.") - if next_token() != HOGQL_BYTECODE_IDENTIFIER: - raise HogVMException(f"Invalid bytecode. Must start with '{HOGQL_BYTECODE_IDENTIFIER}'") - - if len(bytecode) == 1: + if len(bytecode) <= 2: return BytecodeResult(result=None, stdout=stdout, bytecode=bytecode) def check_timeout(): @@ -170,16 +176,17 @@ def check_timeout(): push_stack(not bool(re.search(re.compile(args[1], re.RegexFlag.IGNORECASE), args[0]))) case Operation.GET_GLOBAL: chain = [pop_stack() for _ in range(next_token())] - push_stack(deepcopy(get_nested_value(globals, chain))) + if globals and chain[0] in globals: + push_stack(deepcopy(get_nested_value(globals, chain))) + else: + raise HogVMException(f"Global variable not found: {chain[0]}") case Operation.POP: pop_stack() case Operation.RETURN: if call_stack: ip, stack_start, arg_len = call_stack.pop() response = pop_stack() - stack = stack[0:stack_start] - mem_used -= sum(mem_stack[stack_start:]) - mem_stack = mem_stack[0:stack_start] + stack_keep_first_elements(stack_start) push_stack(response) else: return BytecodeResult(result=pop_stack(), stdout=stdout, bytecode=bytecode) @@ -252,7 +259,7 @@ def check_timeout(): body_len = next_token() declared_functions[name] = (ip, arg_len) ip += body_len - case Operation.CALL: + case Operation.CALL_GLOBAL: check_timeout() name = next_token() if name in declared_functions: @@ -260,16 +267,17 @@ def check_timeout(): call_stack.append((ip + 1, len(stack) - arg_len, arg_len)) ip = func_ip else: - args = [pop_stack() for _ in range(next_token())] + arg_count = next_token() + args = [pop_stack() for _ in range(arg_count)] + if version > 0: + args = list(reversed(args)) if functions is not None and name in functions: push_stack(functions[name](*args)) - continue - - if name not in STL: + elif name in STL: + push_stack(STL[name](args, team, stdout, timeout.total_seconds())) + else: raise HogVMException(f"Unsupported function call: {name}") - - push_stack(STL[name](args, team, stdout, timeout.total_seconds())) case Operation.TRY: throw_stack.append((len(call_stack), len(stack), ip + next_token())) case Operation.POP_TRY: @@ -283,9 +291,7 @@ def check_timeout(): raise HogVMException("Can not throw: value is not of type Error") if throw_stack: call_stack_len, stack_len, catch_ip = throw_stack.pop() - stack = stack[0:stack_len] - mem_used -= sum(mem_stack[stack_len:]) - mem_stack = mem_stack[0:stack_len] + stack_keep_first_elements(stack_len) call_stack = call_stack[0:call_stack_len] push_stack(exception) ip = catch_ip diff --git a/hogvm/python/operation.py b/hogvm/python/operation.py index 319a450fc37b1..0feb79ebfac2e 100644 --- a/hogvm/python/operation.py +++ b/hogvm/python/operation.py @@ -1,12 +1,18 @@ from enum import Enum -HOGQL_BYTECODE_IDENTIFIER = "_h" +HOGQL_BYTECODE_IDENTIFIER_V0 = "_h" +HOGQL_BYTECODE_IDENTIFIER = "_H" HOGQL_BYTECODE_FUNCTION = "_f" +# Version history: +# 0 - initial version +# 1 - added version numbers; reversed order of function args in stack +HOGQL_BYTECODE_VERSION = 1 + class Operation(int, Enum): GET_GLOBAL = 1 - CALL = 2 + CALL_GLOBAL = 2 AND = 3 OR = 4 NOT = 5 diff --git a/hogvm/python/test/test_execute.py b/hogvm/python/test/test_execute.py index b5c1a3d8ee2ae..7289a1b585eff 100644 --- a/hogvm/python/test/test_execute.py +++ b/hogvm/python/test/test_execute.py @@ -4,7 +4,7 @@ from hogvm.python.execute import execute_bytecode, get_nested_value -from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H +from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H, HOGQL_BYTECODE_VERSION as VERSION from hogvm.python.utils import UncaughtHogVMException from posthog.hogql.bytecode import create_bytecode from posthog.hogql.parser import parse_expr, parse_program @@ -115,21 +115,21 @@ def test_nested_value(self): def test_errors(self): try: - execute_bytecode([_H, op.TRUE, op.CALL, "notAFunction", 1], {}) + execute_bytecode([_H, VERSION, op.TRUE, op.CALL_GLOBAL, "notAFunction", 1], {}) except Exception as e: assert str(e) == "Unsupported function call: notAFunction" else: raise AssertionError("Expected Exception not raised") try: - execute_bytecode([_H, op.CALL, "notAFunction", 1], {}) + execute_bytecode([_H, VERSION, op.CALL_GLOBAL, "notAFunction", 1], {}) except Exception as e: assert str(e) == "Stack underflow" else: raise AssertionError("Expected Exception not raised") try: - execute_bytecode([_H, op.TRUE, op.TRUE, op.NOT], {}) + execute_bytecode([_H, VERSION, op.TRUE, op.TRUE, op.NOT], {}) except Exception as e: assert str(e) == "Invalid bytecode. More than one value left on stack" else: @@ -182,10 +182,6 @@ def test_memory_limits_1(self): raise AssertionError("Expected Exception not raised") def test_memory_limits_2(self): - # let string := 'banana' - # for (let i := 0; i < 100; i := i + 1) { - # string := string || string - # } bytecode = [ "_h", 32, @@ -281,27 +277,40 @@ def stringify(*args): return "zero" functions = {"stringify": stringify} - assert execute_bytecode([_H, op.INTEGER, 1, op.CALL, "stringify", 1, op.RETURN], {}, functions).result == "one" - assert execute_bytecode([_H, op.INTEGER, 2, op.CALL, "stringify", 1, op.RETURN], {}, functions).result == "two" assert ( - execute_bytecode([_H, op.STRING, "2", op.CALL, "stringify", 1, op.RETURN], {}, functions).result == "zero" + execute_bytecode( + [_H, VERSION, op.INTEGER, 1, op.CALL_GLOBAL, "stringify", 1, op.RETURN], {}, functions + ).result + == "one" + ) + assert ( + execute_bytecode( + [_H, VERSION, op.INTEGER, 2, op.CALL_GLOBAL, "stringify", 1, op.RETURN], {}, functions + ).result + == "two" + ) + assert ( + execute_bytecode( + [_H, VERSION, op.STRING, "2", op.CALL_GLOBAL, "stringify", 1, op.RETURN], {}, functions + ).result + == "zero" + ) + + def test_version_0_and_1(self): + # version 0 of HogQL bytecode had arguments in a different order + assert ( + execute_bytecode(["_h", op.STRING, "1", op.STRING, "2", op.CALL_GLOBAL, "concat", 2, op.RETURN]).result + == "21" + ) + assert ( + execute_bytecode(["_H", 1, op.STRING, "1", op.STRING, "2", op.CALL_GLOBAL, "concat", 2, op.RETURN]).result + == "12" ) def test_bytecode_variable_assignment(self): program = parse_program("let a := 1 + 2; return a;") bytecode = create_bytecode(program) - assert bytecode == [ - _H, - op.INTEGER, - 2, - op.INTEGER, - 1, - op.PLUS, - op.GET_LOCAL, - 0, - op.RETURN, - op.POP, - ] + assert bytecode == ["_H", 1, op.INTEGER, 2, op.INTEGER, 1, op.PLUS, op.GET_LOCAL, 0, op.RETURN, op.POP] assert self._run_program("let a := 1 + 2; return a;") == 3 assert ( @@ -319,7 +328,8 @@ def test_bytecode_if_else(self): program = parse_program("if (true) return 1; else return 2;") bytecode = create_bytecode(program) assert bytecode == [ - _H, + "_H", + 1, op.TRUE, op.JUMP_IF_FALSE, 5, @@ -371,7 +381,8 @@ def test_bytecode_while(self): program = parse_program("while (true) 1 + 1;") bytecode = create_bytecode(program) assert bytecode == [ - _H, + "_H", + 1, op.TRUE, op.JUMP_IF_FALSE, 8, @@ -388,10 +399,11 @@ def test_bytecode_while(self): program = parse_program("while (toString('a')) { 1 + 1; } return 3;") bytecode = create_bytecode(program) assert bytecode == [ - _H, + "_H", + 1, op.STRING, "a", - op.CALL, + op.CALL_GLOBAL, "toString", 1, op.JUMP_IF_FALSE, @@ -466,7 +478,7 @@ def test_bytecode_for(self): print(i) -- prints 3 times j := j + 2 } - print(i) -- global does not print + // print(i) -- global does not print return j """ ) @@ -484,22 +496,23 @@ def test_bytecode_functions(self): ) bytecode = create_bytecode(program) assert bytecode == [ - _H, + "_H", + 1, op.DECLARE_FN, "add", 2, 6, op.GET_LOCAL, - 0, - op.GET_LOCAL, 1, + op.GET_LOCAL, + 0, op.PLUS, op.RETURN, op.INTEGER, - 4, - op.INTEGER, 3, - op.CALL, + op.INTEGER, + 4, + op.CALL_GLOBAL, "add", 2, op.RETURN, @@ -620,7 +633,12 @@ def test_bytecode_dicts(self): assert self._run_program("return {'key': 'value'};") == {"key": "value"} assert self._run_program("return {'key': 'value', 'other': 'thing'};") == {"key": "value", "other": "thing"} assert self._run_program("return {'key': {'otherKey': 'value'}};") == {"key": {"otherKey": "value"}} - assert self._run_program("return {key: 'value'};") == {None: "value"} + try: + self._run_program("return {key: 'value'};") + except Exception as e: + assert str(e) == "Global variable not found: key" + else: + raise AssertionError("Expected Exception not raised") assert self._run_program("let key := 3; return {key: 'value'};") == {3: "value"} assert self._run_program("return {'key': 'value'}.key;") == "value" diff --git a/hogvm/typescript/package.json b/hogvm/typescript/package.json index b6e47b51cbcbc..90291d4afa369 100644 --- a/hogvm/typescript/package.json +++ b/hogvm/typescript/package.json @@ -1,6 +1,6 @@ { "name": "@posthog/hogvm", - "version": "1.0.35", + "version": "1.0.36", "description": "PostHog Hog Virtual Machine", "types": "dist/index.d.ts", "main": "dist/index.js", diff --git a/hogvm/typescript/src/__tests__/execute.test.ts b/hogvm/typescript/src/__tests__/execute.test.ts index 7de7f42d2d3a2..9efe09b2dd9b7 100644 --- a/hogvm/typescript/src/__tests__/execute.test.ts +++ b/hogvm/typescript/src/__tests__/execute.test.ts @@ -63,24 +63,24 @@ describe('hogvm execute', () => { expect(execSync(['_h', op.STRING, 'AL', op.STRING, 'kala', op.NOT_IREGEX], options)).toBe(false) expect(execSync(['_h', op.STRING, 'bla', op.STRING, 'properties', op.GET_GLOBAL, 2], options)).toBe(null) expect(execSync(['_h', op.STRING, 'foo', op.STRING, 'properties', op.GET_GLOBAL, 2], options)).toBe('bar') - expect(execSync(['_h', op.STRING, 'another', op.STRING, 'arg', op.CALL, 'concat', 2], options)).toBe( + expect(execSync(['_h', op.STRING, 'another', op.STRING, 'arg', op.CALL_GLOBAL, 'concat', 2], options)).toBe( 'arganother' ) - expect(execSync(['_h', op.NULL, op.INTEGER, 1, op.CALL, 'concat', 2], options)).toBe('1') - expect(execSync(['_h', op.FALSE, op.TRUE, op.CALL, 'concat', 2], options)).toBe('truefalse') - expect(execSync(['_h', op.STRING, 'e.*', op.STRING, 'test', op.CALL, 'match', 2], options)).toBe(true) - expect(execSync(['_h', op.STRING, '^e.*', op.STRING, 'test', op.CALL, 'match', 2], options)).toBe(false) - expect(execSync(['_h', op.STRING, 'x.*', op.STRING, 'test', op.CALL, 'match', 2], options)).toBe(false) - expect(execSync(['_h', op.INTEGER, 1, op.CALL, 'toString', 1], options)).toBe('1') - expect(execSync(['_h', op.FLOAT, 1.5, op.CALL, 'toString', 1], options)).toBe('1.5') - expect(execSync(['_h', op.TRUE, op.CALL, 'toString', 1], options)).toBe('true') - expect(execSync(['_h', op.NULL, op.CALL, 'toString', 1], options)).toBe('null') - expect(execSync(['_h', op.STRING, 'string', op.CALL, 'toString', 1], options)).toBe('string') - expect(execSync(['_h', op.STRING, '1', op.CALL, 'toInt', 1], options)).toBe(1) - expect(execSync(['_h', op.STRING, 'bla', op.CALL, 'toInt', 1], options)).toBe(null) - expect(execSync(['_h', op.STRING, '1.2', op.CALL, 'toFloat', 1], options)).toBe(1.2) - expect(execSync(['_h', op.STRING, 'bla', op.CALL, 'toFloat', 1], options)).toBe(null) - expect(execSync(['_h', op.STRING, 'asd', op.CALL, 'toUUID', 1], options)).toBe('asd') + expect(execSync(['_h', op.NULL, op.INTEGER, 1, op.CALL_GLOBAL, 'concat', 2], options)).toBe('1') + expect(execSync(['_h', op.FALSE, op.TRUE, op.CALL_GLOBAL, 'concat', 2], options)).toBe('truefalse') + expect(execSync(['_h', op.STRING, 'e.*', op.STRING, 'test', op.CALL_GLOBAL, 'match', 2], options)).toBe(true) + expect(execSync(['_h', op.STRING, '^e.*', op.STRING, 'test', op.CALL_GLOBAL, 'match', 2], options)).toBe(false) + expect(execSync(['_h', op.STRING, 'x.*', op.STRING, 'test', op.CALL_GLOBAL, 'match', 2], options)).toBe(false) + expect(execSync(['_h', op.INTEGER, 1, op.CALL_GLOBAL, 'toString', 1], options)).toBe('1') + expect(execSync(['_h', op.FLOAT, 1.5, op.CALL_GLOBAL, 'toString', 1], options)).toBe('1.5') + expect(execSync(['_h', op.TRUE, op.CALL_GLOBAL, 'toString', 1], options)).toBe('true') + expect(execSync(['_h', op.NULL, op.CALL_GLOBAL, 'toString', 1], options)).toBe('null') + expect(execSync(['_h', op.STRING, 'string', op.CALL_GLOBAL, 'toString', 1], options)).toBe('string') + expect(execSync(['_h', op.STRING, '1', op.CALL_GLOBAL, 'toInt', 1], options)).toBe(1) + expect(execSync(['_h', op.STRING, 'bla', op.CALL_GLOBAL, 'toInt', 1], options)).toBe(null) + expect(execSync(['_h', op.STRING, '1.2', op.CALL_GLOBAL, 'toFloat', 1], options)).toBe(1.2) + expect(execSync(['_h', op.STRING, 'bla', op.CALL_GLOBAL, 'toFloat', 1], options)).toBe(null) + expect(execSync(['_h', op.STRING, 'asd', op.CALL_GLOBAL, 'toUUID', 1], options)).toBe('asd') expect(execSync(['_h', op.NULL, op.INTEGER, 1, op.EQ], options)).toBe(false) expect(execSync(['_h', op.NULL, op.INTEGER, 1, op.NOT_EQ], options)).toBe(true) @@ -89,19 +89,34 @@ describe('hogvm execute', () => { test('error handling', async () => { const globals = { properties: { foo: 'bar' } } const options = { globals } - expect(() => execSync([], options)).toThrow("Invalid HogQL bytecode, must start with '_h'") - await expect(execAsync([], options)).rejects.toThrow("Invalid HogQL bytecode, must start with '_h'") + expect(() => execSync([], options)).toThrow("Invalid HogQL bytecode, must start with '_H'") + await expect(execAsync([], options)).rejects.toThrow("Invalid HogQL bytecode, must start with '_H'") + expect(() => execSync(['_h', op.INTEGER, 2, op.INTEGER, 1, 'InvalidOp'], options)).toThrow( 'Unexpected node while running bytecode: InvalidOp' ) expect(() => - execSync(['_h', op.STRING, 'another', op.STRING, 'arg', op.CALL, 'invalidFunc', 2], options) + execSync(['_h', op.STRING, 'another', op.STRING, 'arg', op.CALL_GLOBAL, 'invalidFunc', 2], options) ).toThrow('Unsupported function call: invalidFunc') expect(() => execSync(['_h', op.INTEGER], options)).toThrow('Unexpected end of bytecode') - expect(() => execSync(['_h', op.CALL, 'match', 1], options)).toThrow('Not enough arguments on the stack') + expect(() => execSync(['_h', op.CALL_GLOBAL, 'match', 1], options)).toThrow('Not enough arguments on the stack') expect(() => execSync(['_h', op.TRUE, op.TRUE, op.NOT], options)).toThrow( 'Invalid bytecode. More than one value left on stack' ) + + expect(() => execSync(['_H', 1, op.INTEGER, 2, op.INTEGER, 1, 'InvalidOp'], options)).toThrow( + 'Unexpected node while running bytecode: InvalidOp' + ) + expect(() => + execSync(['_H', 1, op.STRING, 'another', op.STRING, 'arg', op.CALL_GLOBAL, 'invalidFunc', 2], options) + ).toThrow('Unsupported function call: invalidFunc') + expect(() => execSync(['_H', 1, op.INTEGER], options)).toThrow('Unexpected end of bytecode') + expect(() => execSync(['_H', 1, op.CALL_GLOBAL, 'match', 1], options)).toThrow( + 'Not enough arguments on the stack' + ) + expect(() => execSync(['_H', 1, op.TRUE, op.TRUE, op.NOT], options)).toThrow( + 'Invalid bytecode. More than one value left on stack' + ) }) test('async limits', async () => { @@ -288,9 +303,14 @@ describe('hogvm execute', () => { return 'zero' }, } - expect(execSync(['_h', op.INTEGER, 1, op.CALL, 'stringify', 1], { functions })).toBe('one') - expect(execSync(['_h', op.INTEGER, 2, op.CALL, 'stringify', 1], { functions })).toBe('two') - expect(execSync(['_h', op.STRING, '2', op.CALL, 'stringify', 1], { functions })).toBe('zero') + expect(execSync(['_h', op.INTEGER, 1, op.CALL_GLOBAL, 'stringify', 1], { functions })).toBe('one') + expect(execSync(['_h', op.INTEGER, 2, op.CALL_GLOBAL, 'stringify', 1], { functions })).toBe('two') + expect(execSync(['_h', op.STRING, '2', op.CALL_GLOBAL, 'stringify', 1], { functions })).toBe('zero') + }) + + test('version 0 and 1', async () => { + expect(execSync(['_h', op.STRING, '1', op.STRING, '2', op.CALL_GLOBAL, 'concat', 2, op.RETURN])).toBe('21') + expect(execSync(['_H', 1, op.STRING, '1', op.STRING, '2', op.CALL_GLOBAL, 'concat', 2, op.RETURN])).toBe('12') }) test('should execute user-defined stringify async function correctly', async () => { @@ -304,9 +324,9 @@ describe('hogvm execute', () => { return Promise.resolve('zero') }, } - expect(await execAsync(['_h', op.INTEGER, 1, op.CALL, 'stringify', 1], { asyncFunctions })).toBe('one') - expect(await execAsync(['_h', op.INTEGER, 2, op.CALL, 'stringify', 1], { asyncFunctions })).toBe('two') - expect(await execAsync(['_h', op.STRING, '2', op.CALL, 'stringify', 1], { asyncFunctions })).toBe('zero') + expect(await execAsync(['_h', op.INTEGER, 1, op.CALL_GLOBAL, 'stringify', 1], { asyncFunctions })).toBe('one') + expect(await execAsync(['_h', op.INTEGER, 2, op.CALL_GLOBAL, 'stringify', 1], { asyncFunctions })).toBe('two') + expect(await execAsync(['_h', op.STRING, '2', op.CALL_GLOBAL, 'stringify', 1], { asyncFunctions })).toBe('zero') }) test('bytecode variable assignment', async () => { @@ -393,7 +413,7 @@ describe('hogvm execute', () => { 1, op.INTEGER, 2, - op.CALL, + op.CALL_GLOBAL, 'add', 2, op.INTEGER, @@ -402,12 +422,12 @@ describe('hogvm execute', () => { 4, op.INTEGER, 3, - op.CALL, + op.CALL_GLOBAL, 'add', 2, op.PLUS, op.PLUS, - op.CALL, + op.CALL_GLOBAL, 'divide', 2, op.RETURN, @@ -439,7 +459,7 @@ describe('hogvm execute', () => { op.GET_LOCAL, 0, op.MINUS, - op.CALL, + op.CALL_GLOBAL, 'fibonacci', 1, op.INTEGER, @@ -447,14 +467,14 @@ describe('hogvm execute', () => { op.GET_LOCAL, 0, op.MINUS, - op.CALL, + op.CALL_GLOBAL, 'fibonacci', 1, op.PLUS, op.RETURN, op.INTEGER, 6, - op.CALL, + op.CALL_GLOBAL, 'fibonacci', 1, op.RETURN, @@ -623,10 +643,10 @@ describe('hogvm execute', () => { .result ).toEqual(map({ key: map({ otherKey: 'value' }) })) - // return {key: 'value'}; + // // return {key: 'value'}; expect( - exec(['_h', op.STRING, 'key', op.GET_GLOBAL, 1, op.STRING, 'value', op.DICT, 1, op.RETURN]).result - ).toEqual(new Map([[null, 'value']])) + () => exec(['_h', op.STRING, 'key', op.GET_GLOBAL, 1, op.STRING, 'value', op.DICT, 1, op.RETURN]).result + ).toThrow('Global variable not found: key') // var key := 3; return {key: 'value'}; expect( @@ -1729,11 +1749,11 @@ describe('hogvm execute', () => { op.DICT, 2, ] - expect(execSync(['_h', op.STRING, '[1,2,3]', op.CALL, 'jsonParse', 1])).toEqual([1, 2, 3]) - expect(execSync(['_h', ...dict, op.CALL, 'jsonStringify', 1])).toEqual( + expect(execSync(['_h', op.STRING, '[1,2,3]', op.CALL_GLOBAL, 'jsonParse', 1])).toEqual([1, 2, 3]) + expect(execSync(['_h', ...dict, op.CALL_GLOBAL, 'jsonStringify', 1])).toEqual( '{"event":"$pageview","properties":{"$browser":"Chrome","$os":"Windows"}}' ) - expect(execSync(['_h', op.INTEGER, 2, ...dict, op.CALL, 'jsonStringify', 2])).toEqual( + expect(execSync(['_h', op.INTEGER, 2, ...dict, op.CALL_GLOBAL, 'jsonStringify', 2])).toEqual( JSON.stringify({ event: '$pageview', properties: { $browser: 'Chrome', $os: 'Windows' } }, null, 2) ) }) @@ -1783,21 +1803,21 @@ describe('hogvm execute', () => { 7, op.STRING, 'true1', - op.CALL, + op.CALL_GLOBAL, 'noisy_print', 1, op.JUMP, 5, op.STRING, 'false1', - op.CALL, + op.CALL_GLOBAL, 'noisy_print', 1, op.JUMP, 5, op.STRING, 'false2', - op.CALL, + op.CALL_GLOBAL, 'noisy_print', 1, op.RETURN, @@ -1823,13 +1843,13 @@ describe('hogvm execute', () => { op.POP, op.STRING, 'no', - op.CALL, + op.CALL_GLOBAL, 'noisy_print', 1, op.RETURN, op.STRING, 'post', - op.CALL, + op.CALL_GLOBAL, 'noisy_print', 1, op.POP, @@ -1840,7 +1860,7 @@ describe('hogvm execute', () => { test('uncaught exceptions', () => { // throw Error('Not a good day') - const bytecode1 = ['_h', op.NULL, op.NULL, op.STRING, 'Not a good day', op.CALL, 'Error', 3, op.THROW] + const bytecode1 = ['_h', op.NULL, op.NULL, op.STRING, 'Not a good day', op.CALL_GLOBAL, 'Error', 3, op.THROW] expect(() => execSync(bytecode1)).toThrow(new UncaughtHogVMException('Error', 'Not a good day', null)) // throw RetryError('Not a good day', {'key': 'value'}) @@ -1854,7 +1874,7 @@ describe('hogvm execute', () => { 1, op.STRING, 'Not a good day', - op.CALL, + op.CALL_GLOBAL, 'RetryError', 2, op.THROW, @@ -1865,7 +1885,20 @@ describe('hogvm execute', () => { }) test('returns serialized state', () => { - const bytecode = ['_h', op.STRING, 'key', op.STRING, 'value', op.DICT, 1, op.GET_LOCAL, 0, op.CALL, 'fetch', 1] + const bytecode = [ + '_h', + op.STRING, + 'key', + op.STRING, + 'value', + op.DICT, + 1, + op.GET_LOCAL, + 0, + op.CALL_GLOBAL, + 'fetch', + 1, + ] expect(exec(bytecode, { asyncFunctions: { fetch: async () => null } })).toEqual({ asyncFunctionArgs: [{ key: 'value' }], // not a Map asyncFunctionName: 'fetch', diff --git a/hogvm/typescript/src/execute.ts b/hogvm/typescript/src/execute.ts index dd7865b77f0af..9f9bb5af6a422 100644 --- a/hogvm/typescript/src/execute.ts +++ b/hogvm/typescript/src/execute.ts @@ -110,10 +110,10 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { } else { bytecode = code } - - if (!bytecode || bytecode.length === 0 || bytecode[0] !== '_h') { - throw new HogVMException("Invalid HogQL bytecode, must start with '_h'") + if (!bytecode || bytecode.length === 0 || (bytecode[0] !== '_h' && bytecode[0] !== '_H')) { + throw new HogVMException("Invalid HogQL bytecode, must start with '_H'") } + const version = bytecode[0] === '_H' ? bytecode[1] ?? 0 : 0 const startTime = Date.now() let temp: any @@ -131,7 +131,7 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { let memUsed = memStack.reduce((acc, val) => acc + val, 0) let maxMemUsed = Math.max(vmState ? vmState.maxMemUsed : 0, memUsed) const memLimit = options?.memoryLimit ?? DEFAULT_MAX_MEMORY - let ip = vmState ? vmState.ip : 1 + let ip = vmState ? vmState.ip : bytecode[0] === '_H' ? 2 : 1 let ops = vmState ? vmState.ops : 0 const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS const maxAsyncSteps = options?.maxAsyncSteps ?? DEFAULT_MAX_ASYNC_STEPS @@ -158,7 +158,9 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { memUsed -= memStack.splice(start, deleteCount).reduce((acc, val) => acc + val, 0) return stack.splice(start, deleteCount) } - function spliceStack1(start: number): any[] { + + /** Keep start elements, return those removed */ + function stackKeepFirstElements(start: number): any[] { memUsed -= memStack.splice(start).reduce((acc, val) => acc + val, 0) return stack.splice(start) } @@ -310,7 +312,11 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { for (let i = 0; i < count; i++) { chain.push(popStack()) } - pushStack(options?.globals ? convertJSToHog(getNestedValue(options.globals, chain)) : null) + if (options?.globals && chain[0] in options.globals && Object.hasOwn(options.globals, chain[0])) { + pushStack(convertJSToHog(getNestedValue(options.globals, chain))) + } else { + throw new HogVMException(`Global variable not found: ${chain.join('.')}`) + } break } case Operation.POP: @@ -320,7 +326,7 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { if (callStack.length > 0) { const [newIp, stackStart, _] = callStack.pop()! const response = popStack() - spliceStack1(stackStart) + stackKeepFirstElements(stackStart) pushStack(response) ip = newIp break @@ -400,7 +406,7 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { ip += bodyLength break } - case Operation.CALL: { + case Operation.CALL_GLOBAL: { checkTimeout() const name = next() // excluding "toString" only because of JavaScript --> no, it's not declared, it's omnipresent! o_O @@ -416,9 +422,14 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { if (temp > MAX_FUNCTION_ARGS_LENGTH) { throw new HogVMException('Too many arguments') } - const args = Array(temp) - .fill(null) - .map(() => popStack()) + + const args = + version === 0 + ? Array(temp) + .fill(null) + .map(() => popStack()) + : stackKeepFirstElements(stack.length - temp) + if (options?.functions && Object.hasOwn(options.functions, name) && options.functions[name]) { pushStack(convertJSToHog(options.functions[name](...args.map(convertHogToJS)))) } else if ( @@ -475,7 +486,7 @@ export function exec(code: any[] | VMState, options?: ExecOptions): ExecResult { } if (throwStack.length > 0) { const [callStackLen, stackLen, catchIp] = throwStack.pop()! - spliceStack1(stackLen) + stackKeepFirstElements(stackLen) memUsed -= memStack.splice(stackLen).reduce((acc, val) => acc + val, 0) callStack.splice(callStackLen) pushStack(exception) diff --git a/hogvm/typescript/src/operation.ts b/hogvm/typescript/src/operation.ts index 2f8af6d275f56..4d4c5eee71a1a 100644 --- a/hogvm/typescript/src/operation.ts +++ b/hogvm/typescript/src/operation.ts @@ -1,6 +1,6 @@ export const enum Operation { GET_GLOBAL = 1, - CALL = 2, + CALL_GLOBAL = 2, AND = 3, OR = 4, NOT = 5, diff --git a/plugin-server/package.json b/plugin-server/package.json index f7672084d42eb..ea7467fb42993 100644 --- a/plugin-server/package.json +++ b/plugin-server/package.json @@ -52,7 +52,7 @@ "@google-cloud/storage": "^5.8.5", "@maxmind/geoip2-node": "^3.4.0", "@posthog/clickhouse": "^1.7.0", - "@posthog/hogvm": "^1.0.35", + "@posthog/hogvm": "^1.0.36", "@posthog/plugin-scaffold": "1.4.4", "@sentry/node": "^7.49.0", "@sentry/profiling-node": "^0.3.0", diff --git a/plugin-server/pnpm-lock.yaml b/plugin-server/pnpm-lock.yaml index 0bb579b69c438..faedff7194010 100644 --- a/plugin-server/pnpm-lock.yaml +++ b/plugin-server/pnpm-lock.yaml @@ -47,8 +47,8 @@ dependencies: specifier: file:../rust/cyclotron-node version: file:../rust/cyclotron-node '@posthog/hogvm': - specifier: ^1.0.35 - version: 1.0.35(luxon@3.4.4)(re2@1.20.3) + specifier: ^1.0.36 + version: 1.0.36(luxon@3.4.4)(re2@1.20.3) '@posthog/plugin-scaffold': specifier: 1.4.4 version: 1.4.4 @@ -3116,8 +3116,8 @@ packages: engines: {node: '>=12'} dev: false - /@posthog/hogvm@1.0.35(luxon@3.4.4)(re2@1.20.3): - resolution: {integrity: sha512-Uq3Cpg0CGxwScdgGddfzDCs42opILFhgnM6wqly55PAZGBhXWxVHgVpejLiWLtG1z8aIMLC8HCbQx1rZheCGMQ==} + /@posthog/hogvm@1.0.36(luxon@3.4.4)(re2@1.20.3): + resolution: {integrity: sha512-O4mVlTCNYAOg3eh82r5YE0HqMf4b0em7JNHCD6tkUUMxe47rTAVflSW/vVOkV7ogfgyjxQD0bOS6w708FW/cMg==} peerDependencies: luxon: ^3.4.4 re2: ^1.21.3 diff --git a/plugin-server/tests/cdp/cdp-function-callbacks-consumer.test.ts b/plugin-server/tests/cdp/cdp-function-callbacks-consumer.test.ts index 54f052fe439d4..33559d108ace9 100644 --- a/plugin-server/tests/cdp/cdp-function-callbacks-consumer.test.ts +++ b/plugin-server/tests/cdp/cdp-function-callbacks-consumer.test.ts @@ -111,6 +111,14 @@ describe('CDP Processed Events Consuner', () => { $lib_version: '1.0.0', }, }, + groups: {}, + person: { + uuid: 'b3a1fe86-b10c-43cc-acaf-d208977608d0', + distinct_ids: ['b3a1fe86-b10c-43cc-acaf-d208977608d0'], + properties: { + email: 'test@posthog.com', + }, + }, } beforeEach(async () => { @@ -161,7 +169,7 @@ describe('CDP Processed Events Consuner', () => { }, { level: 'debug', - message: "Suspending function due to async function call 'fetch'. Payload: 1140 bytes", + message: "Suspending function due to async function call 'fetch'. Payload: 1331 bytes", }, { level: 'info', @@ -209,7 +217,7 @@ describe('CDP Processed Events Consuner', () => { }, { level: 'debug', - message: "Suspending function due to async function call 'fetch'. Payload: 1140 bytes", + message: "Suspending function due to async function call 'fetch'. Payload: 1331 bytes", }, { level: 'debug', diff --git a/plugin-server/tests/cdp/fixtures.ts b/plugin-server/tests/cdp/fixtures.ts index b70af8efe3c39..8f3fed7da62dc 100644 --- a/plugin-server/tests/cdp/fixtures.ts +++ b/plugin-server/tests/cdp/fixtures.ts @@ -100,6 +100,15 @@ export const createHogExecutionGlobals = ( ): HogFunctionInvocationGlobals => { return { ...data, + person: { + uuid: 'uuid', + name: 'test', + url: 'http://localhost:8000/persons/1', + properties: { + $lib_version: '1.2.3', + }, + ...(data.person ?? {}), + }, project: { id: 1, name: 'test', diff --git a/plugin-server/tests/cdp/hog-executor.test.ts b/plugin-server/tests/cdp/hog-executor.test.ts index ae5203f6415b0..e8ac3d365cd9a 100644 --- a/plugin-server/tests/cdp/hog-executor.test.ts +++ b/plugin-server/tests/cdp/hog-executor.test.ts @@ -58,9 +58,9 @@ describe('Hog Executor', () => { }) it('can execute messages', () => { - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) expect(results).toHaveLength(1) expect(results[0]).toMatchObject({ @@ -74,9 +74,9 @@ describe('Hog Executor', () => { }) it('collects logs from the function', () => { - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) expect(results[0].logs).toMatchObject([ { @@ -87,7 +87,7 @@ describe('Hog Executor', () => { { timestamp: expect.any(DateTime), level: 'debug', - message: "Suspending function due to async function call 'fetch'. Payload: 1299 bytes", + message: "Suspending function due to async function call 'fetch'. Payload: 1456 bytes", }, ]) }) @@ -100,7 +100,10 @@ describe('Hog Executor', () => { mockFunctionManager.getTeamHogFunctions.mockReturnValue([fn]) - const result = executor.executeFunction(createHogExecutionGlobals(), fn) as HogFunctionInvocationResult + const result = executor.executeFunction( + createHogExecutionGlobals({ groups: {} }), + fn + ) as HogFunctionInvocationResult expect(result.logs.map((x) => x.message)).toMatchInlineSnapshot(` Array [ "Executing function", @@ -115,9 +118,9 @@ describe('Hog Executor', () => { }) it('queues up an async function call', () => { - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) expect(results[0]).toMatchObject({ invocation: { @@ -163,9 +166,14 @@ describe('Hog Executor', () => { properties: { $lib_version: '1.2.3' }, timestamp: '2024-06-07T12:00:00.000Z', }, - groups: null, + groups: {}, nested: { foo: 'http://localhost:8000/events/1' }, - person: null, + person: { + uuid: 'uuid', + name: 'test', + url: 'http://localhost:8000/persons/1', + properties: { $lib_version: '1.2.3' }, + }, event_url: 'http://localhost:8000/events/1-test', }, method: 'POST', @@ -177,9 +185,9 @@ describe('Hog Executor', () => { it('executes the full function in a loop', () => { const logs: LogEntry[] = [] - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) const splicedLogs = results[0].logs.splice(0, 100) logs.push(...splicedLogs) @@ -191,18 +199,18 @@ describe('Hog Executor', () => { expect(asyncExecResult.finished).toBe(true) expect(logs.map((log) => log.message)).toEqual([ 'Executing function', - "Suspending function due to async function call 'fetch'. Payload: 1299 bytes", + "Suspending function due to async function call 'fetch'. Payload: 1456 bytes", 'Resuming function', 'Fetch response:, {"status":200,"body":"success"}', - 'Function completed in 100ms. Sync: 0ms. Mem: 589 bytes. Ops: 22.', + 'Function completed in 100ms. Sync: 0ms. Mem: 746 bytes. Ops: 22.', ]) }) it('parses the responses body if a string', () => { const logs: LogEntry[] = [] - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) const splicedLogs = results[0].logs.splice(0, 100) logs.push(...splicedLogs) @@ -219,10 +227,10 @@ describe('Hog Executor', () => { expect(asyncExecResult.finished).toBe(true) expect(logs.map((log) => log.message)).toEqual([ 'Executing function', - "Suspending function due to async function call 'fetch'. Payload: 1299 bytes", + "Suspending function due to async function call 'fetch'. Payload: 1456 bytes", 'Resuming function', 'Fetch response:, {"status":200,"body":{"foo":"bar"}}', // The body is parsed - 'Function completed in 100ms. Sync: 0ms. Mem: 589 bytes. Ops: 22.', + 'Function completed in 100ms. Sync: 0ms. Mem: 746 bytes. Ops: 22.', ]) }) }) @@ -237,12 +245,13 @@ describe('Hog Executor', () => { mockFunctionManager.getTeamHogFunctions.mockReturnValue([fn]) - const resultsShouldntMatch = executor.findMatchingFunctions(createHogExecutionGlobals()) + const resultsShouldntMatch = executor.findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) expect(resultsShouldntMatch.matchingFunctions).toHaveLength(0) expect(resultsShouldntMatch.nonMatchingFunctions).toHaveLength(1) const resultsShouldMatch = executor.findMatchingFunctions( createHogExecutionGlobals({ + groups: {}, event: { name: '$pageview', properties: { @@ -267,9 +276,9 @@ describe('Hog Executor', () => { mockFunctionManager.getTeamHogFunctions.mockReturnValue([fn]) // Simulate the recusive loop - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) expect(results).toHaveLength(1) @@ -305,9 +314,9 @@ describe('Hog Executor', () => { mockFunctionManager.getTeamHogFunctions.mockReturnValue([fn]) - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const results = executor - .findMatchingFunctions(createHogExecutionGlobals()) + .findMatchingFunctions(createHogExecutionGlobals({ groups: {} })) .matchingFunctions.map((x) => executor.executeFunction(globals, x) as HogFunctionInvocationResult) expect(results).toHaveLength(1) expect(results[0].error).toContain('Execution timed out after 0.1 seconds. Performed ') @@ -339,7 +348,7 @@ describe('Hog Executor', () => { ...HOG_FILTERS_EXAMPLES.no_filters, }) - const globals = createHogExecutionGlobals() + const globals = createHogExecutionGlobals({ groups: {} }) const result = executor.executeFunction(globals, fn) expect(result?.capturedPostHogEvents).toEqual([ { @@ -362,6 +371,7 @@ describe('Hog Executor', () => { }) const globals = createHogExecutionGlobals({ + groups: {}, event: { properties: { $hog_function_execution_count: 1, diff --git a/posthog/api/test/__snapshots__/test_action.ambr b/posthog/api/test/__snapshots__/test_action.ambr index 03d20dc4a2b43..44b749ef70294 100644 --- a/posthog/api/test/__snapshots__/test_action.ambr +++ b/posthog/api/test/__snapshots__/test_action.ambr @@ -290,76 +290,6 @@ "posthog_action"."name" ASC ''' # --- -# name: TestActionApi.test_listing_actions_is_not_nplus1.15 - ''' - SELECT COUNT(*) - FROM - (SELECT "posthog_action"."id" AS "col1" - FROM "posthog_action" - LEFT OUTER JOIN "posthog_action_events" ON ("posthog_action"."id" = "posthog_action_events"."action_id") - WHERE (NOT "posthog_action"."deleted" - AND "posthog_action"."team_id" = 2 - AND "posthog_action"."team_id" = 2) - GROUP BY 1) subquery - ''' -# --- -# name: TestActionApi.test_listing_actions_is_not_nplus1.16 - ''' - SELECT "posthog_action"."id", - "posthog_action"."name", - "posthog_action"."team_id", - "posthog_action"."description", - "posthog_action"."created_at", - "posthog_action"."created_by_id", - "posthog_action"."deleted", - "posthog_action"."post_to_slack", - "posthog_action"."slack_message_format", - "posthog_action"."updated_at", - "posthog_action"."bytecode", - "posthog_action"."bytecode_error", - "posthog_action"."steps_json", - "posthog_action"."is_calculating", - "posthog_action"."last_calculated_at", - COUNT("posthog_action_events"."event_id") AS "count", - "posthog_user"."id", - "posthog_user"."password", - "posthog_user"."last_login", - "posthog_user"."first_name", - "posthog_user"."last_name", - "posthog_user"."is_staff", - "posthog_user"."is_active", - "posthog_user"."date_joined", - "posthog_user"."uuid", - "posthog_user"."current_organization_id", - "posthog_user"."current_team_id", - "posthog_user"."email", - "posthog_user"."pending_email", - "posthog_user"."temporary_token", - "posthog_user"."distinct_id", - "posthog_user"."is_email_verified", - "posthog_user"."requested_password_reset_at", - "posthog_user"."has_seen_product_intro_for", - "posthog_user"."strapi_id", - "posthog_user"."theme_mode", - "posthog_user"."partial_notification_settings", - "posthog_user"."anonymize_data", - "posthog_user"."toolbar_mode", - "posthog_user"."hedgehog_config", - "posthog_user"."events_column_config", - "posthog_user"."email_opt_in" - FROM "posthog_action" - LEFT OUTER JOIN "posthog_action_events" ON ("posthog_action"."id" = "posthog_action_events"."action_id") - LEFT OUTER JOIN "posthog_user" ON ("posthog_action"."created_by_id" = "posthog_user"."id") - WHERE (NOT "posthog_action"."deleted" - AND "posthog_action"."team_id" = 2 - AND "posthog_action"."team_id" = 2) - GROUP BY "posthog_action"."id", - "posthog_user"."id" - ORDER BY "posthog_action"."last_calculated_at" DESC, - "posthog_action"."name" ASC - LIMIT 100 - ''' -# --- # name: TestActionApi.test_listing_actions_is_not_nplus1.2 ''' SELECT "posthog_organizationmembership"."id", diff --git a/posthog/api/test/test_action.py b/posthog/api/test/test_action.py index 3e4a0c4f56d87..96ae589fdea35 100644 --- a/posthog/api/test/test_action.py +++ b/posthog/api/test/test_action.py @@ -100,7 +100,7 @@ def test_create_action_generates_bytecode(self): ) assert response.status_code == status.HTTP_201_CREATED, response.json() action = Action.objects.get(pk=response.json()["id"]) - assert action.bytecode == ["_h", 32, "%/signup%", 32, "$current_url", 32, "properties", 1, 2, 17] + assert action.bytecode == ["_H", 1, 32, "%/signup%", 32, "$current_url", 32, "properties", 1, 2, 17] def test_cant_create_action_with_the_same_name(self, *args): original_action = Action.objects.create(name="user signed up", team=self.team) diff --git a/posthog/api/test/test_hog_function.py b/posthog/api/test/test_hog_function.py index 191b3473da27d..f62a96714a311 100644 --- a/posthog/api/test/test_hog_function.py +++ b/posthog/api/test/test_hog_function.py @@ -5,6 +5,7 @@ from inline_snapshot import snapshot from rest_framework import status +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.constants import AvailableFeature from posthog.models.action.action import Action from posthog.models.hog_functions.hog_function import DEFAULT_STATE, HogFunction @@ -140,10 +141,10 @@ def test_create_hog_function(self, *args): "updated_at": ANY, "enabled": False, "hog": "fetch(inputs.url);", - "bytecode": ["_h", 32, "url", 32, "inputs", 1, 2, 2, "fetch", 1, 35], + "bytecode": ["_H", HOGQL_BYTECODE_VERSION, 32, "url", 32, "inputs", 1, 2, 2, "fetch", 1, 35], "inputs_schema": [], "inputs": {}, - "filters": {"bytecode": ["_h", 29]}, + "filters": {"bytecode": ["_H", HOGQL_BYTECODE_VERSION, 29]}, "icon_url": None, "template": None, "masking": None, @@ -325,7 +326,7 @@ def test_generates_hog_bytecode(self, *args): ) # JSON loads for one line comparison assert response.json()["bytecode"] == json.loads( - '["_h", 33, 0, 33, 3, 36, 0, 15, 40, 45, 33, 1, 36, 0, 6, 37, 0, 32, "headers", 32, "x-count", 36, 0, 42, 1, 32, "body", 32, "payload", 32, "inputs", 1, 2, 32, "method", 32, "method", 32, "inputs", 1, 2, 42, 3, 32, "url", 32, "inputs", 1, 2, 2, "fetch", 2, 35, 39, -52, 35]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 33, 0, 33, 3, 36, 0, 15, 40, 45, 33, 1, 36, 0, 6, 37, 0, 32, "url", 32, "inputs", 1, 2, 32, "headers", 32, "x-count", 36, 0, 42, 1, 32, "body", 32, "payload", 32, "inputs", 1, 2, 32, "method", 32, "method", 32, "inputs", 1, 2, 42, 3, 2, "fetch", 2, 35, 39, -52, 35]' ), response.json() def test_generates_inputs_bytecode(self, *args): @@ -334,7 +335,12 @@ def test_generates_inputs_bytecode(self, *args): assert response.json()["inputs"] == { "url": { "value": "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937", - "bytecode": ["_h", 32, "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937"], + "bytecode": [ + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937", + ], }, "payload": { "value": { @@ -345,18 +351,48 @@ def test_generates_inputs_bytecode(self, *args): "event_url": "{f'{event.url}-test'}", }, "bytecode": { - "event": ["_h", 32, "event", 1, 1], - "groups": ["_h", 32, "groups", 1, 1], - "nested": {"foo": ["_h", 32, "url", 32, "event", 1, 2]}, - "person": ["_h", 32, "person", 1, 1], - "event_url": ["_h", 32, "-test", 32, "url", 32, "event", 1, 2, 2, "concat", 2], + "event": ["_H", HOGQL_BYTECODE_VERSION, 32, "event", 1, 1], + "groups": ["_H", HOGQL_BYTECODE_VERSION, 32, "groups", 1, 1], + "nested": {"foo": ["_H", HOGQL_BYTECODE_VERSION, 32, "url", 32, "event", 1, 2]}, + "person": ["_H", HOGQL_BYTECODE_VERSION, 32, "person", 1, 1], + "event_url": [ + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "url", + 32, + "event", + 1, + 2, + 32, + "-test", + 2, + "concat", + 2, + ], }, }, "method": {"value": "POST"}, "headers": { "value": {"version": "v={event.properties.$lib_version}"}, "bytecode": { - "version": ["_h", 32, "$lib_version", 32, "properties", 32, "event", 1, 3, 32, "v=", 2, "concat", 2] + "version": [ + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "v=", + 32, + "$lib_version", + 32, + "properties", + 32, + "event", + 1, + 3, + 2, + "concat", + 2, + ] }, }, } @@ -394,16 +430,19 @@ def test_generates_filters_bytecode(self, *args): "actions": [{"id": f"{action.id}", "name": "Test Action", "type": "actions", "order": 1}], "filter_test_accounts": True, "bytecode": [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, 32, - "%docs%", + "%@posthog.com%", 32, - "$current_url", + "email", 32, "properties", + 32, + "person", 1, - 2, - 17, + 3, + 20, 32, "$pageview", 32, @@ -424,8 +463,6 @@ def test_generates_filters_bytecode(self, *args): 1, 3, 20, - 3, - 2, 32, "$pageview", 32, @@ -434,16 +471,16 @@ def test_generates_filters_bytecode(self, *args): 1, 11, 32, - "%@posthog.com%", + "%docs%", 32, - "email", + "$current_url", 32, "properties", - 32, - "person", 1, + 2, + 17, 3, - 20, + 2, 3, 2, 4, @@ -465,7 +502,7 @@ def test_saves_masking_config(self, *args): "ttl": 60, "threshold": 20, "hash": "{person.properties.email}", - "bytecode": ["_h", 32, "email", 32, "properties", 32, "person", 1, 3], + "bytecode": ["_H", HOGQL_BYTECODE_VERSION, 32, "email", 32, "properties", 32, "person", 1, 3], } ) diff --git a/posthog/cdp/templates/test_cdp_templates.py b/posthog/cdp/templates/test_cdp_templates.py index 21d5d841b3a9d..889f7431e33a1 100644 --- a/posthog/cdp/templates/test_cdp_templates.py +++ b/posthog/cdp/templates/test_cdp_templates.py @@ -10,5 +10,5 @@ def setUp(self): def test_templates_are_valid(self): for template in HOG_FUNCTION_TEMPLATES: bytecode = compile_hog(template.hog) - assert bytecode[0] == "_h" + assert bytecode[0] == "_H" assert validate_inputs_schema(template.inputs_schema) diff --git a/posthog/cdp/test/test_filters.py b/posthog/cdp/test/test_filters.py index 8278dee55783d..b8c79da81290e 100644 --- a/posthog/cdp/test/test_filters.py +++ b/posthog/cdp/test/test_filters.py @@ -1,6 +1,7 @@ import json from inline_snapshot import snapshot +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.cdp.filters import hog_function_filters_to_expr from posthog.hogql.bytecode import create_bytecode from posthog.models.action.action import Action @@ -63,7 +64,7 @@ def filters_to_bytecode(self, filters: dict): return json.loads(json.dumps(create_bytecode(res))) def test_filters_empty(self): - assert self.filters_to_bytecode(filters={}) == snapshot(["_h", 29]) + assert self.filters_to_bytecode(filters={}) == snapshot(["_H", HOGQL_BYTECODE_VERSION, 29]) def test_filters_all_events(self): assert self.filters_to_bytecode( @@ -77,12 +78,21 @@ def test_filters_all_events(self): } ] } - ) == snapshot(["_h", 3, 0, 29, 4, 2]) + ) == snapshot(["_H", HOGQL_BYTECODE_VERSION, 29, 3, 0, 4, 2]) def test_filters_events(self): - assert self.filters_to_bytecode(filters={"events": self.filters["events"]}) == snapshot( + bytecode = self.filters_to_bytecode(filters={"events": self.filters["events"]}) + assert bytecode == snapshot( [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "$pageview", + 32, + "event", + 1, + 1, + 11, 32, "%docs%", 32, @@ -92,13 +102,6 @@ def test_filters_events(self): 1, 2, 18, - 32, - "$pageview", - 32, - "event", - 1, - 1, - 11, 3, 2, 4, @@ -107,9 +110,18 @@ def test_filters_events(self): ) def test_filters_actions(self): - assert self.filters_to_bytecode(filters={"actions": self.filters["actions"]}) == snapshot( + bytecode = self.filters_to_bytecode(filters={"actions": self.filters["actions"]}) + assert bytecode == snapshot( [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "$pageview", + 32, + "event", + 1, + 1, + 11, 32, "%docs%", 32, @@ -119,13 +131,6 @@ def test_filters_actions(self): 1, 2, 17, - 32, - "$pageview", - 32, - "event", - 1, - 1, - 11, 3, 2, 3, @@ -138,67 +143,52 @@ def test_filters_actions(self): def test_filters_properties(self): assert self.filters_to_bytecode(filters={"properties": self.filters["properties"]}) == snapshot( [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, 32, - "ben", + "%@posthog.com%", 32, - "name", + "email", 32, "properties", 32, "person", 1, 3, - 11, + 18, 32, - "%@posthog.com%", + "ben", 32, - "email", + "name", 32, "properties", 32, "person", 1, 3, - 18, + 11, 3, 2, ] ) def test_filters_full(self): - assert self.filters_to_bytecode(filters=self.filters) == snapshot( + bytecode = self.filters_to_bytecode(filters=self.filters) + assert bytecode == snapshot( [ - "_h", - 32, - "%docs%", - 32, - "$current_url", - 32, - "properties", - 1, - 2, - 17, + "_H", + HOGQL_BYTECODE_VERSION, 32, - "$pageview", - 32, - "event", - 1, - 1, - 11, - 3, - 2, - 32, - "ben", + "%@posthog.com%", 32, - "name", + "email", 32, "properties", 32, "person", 1, 3, - 11, + 20, 32, "%@posthog.com%", 32, @@ -211,18 +201,23 @@ def test_filters_full(self): 3, 18, 32, - "%@posthog.com%", + "ben", 32, - "email", + "name", 32, "properties", 32, "person", 1, 3, - 20, - 3, - 4, + 11, + 32, + "$pageview", + 32, + "event", + 1, + 1, + 11, 32, "%docs%", 32, @@ -232,24 +227,19 @@ def test_filters_full(self): 1, 2, 18, + 3, + 5, 32, - "$pageview", - 32, - "event", - 1, - 1, - 11, - 32, - "ben", + "%@posthog.com%", 32, - "name", + "email", 32, "properties", 32, "person", 1, 3, - 11, + 20, 32, "%@posthog.com%", 32, @@ -262,18 +252,36 @@ def test_filters_full(self): 3, 18, 32, - "%@posthog.com%", + "ben", 32, - "email", + "name", 32, "properties", 32, "person", 1, 3, - 20, + 11, + 32, + "$pageview", + 32, + "event", + 1, + 1, + 11, + 32, + "%docs%", + 32, + "$current_url", + 32, + "properties", + 1, + 2, + 17, 3, - 5, + 2, + 3, + 4, 4, 2, ] diff --git a/posthog/cdp/test/test_validation.py b/posthog/cdp/test/test_validation.py index 90428c9dd574a..90a41f8cca653 100644 --- a/posthog/cdp/test/test_validation.py +++ b/posthog/cdp/test/test_validation.py @@ -2,6 +2,7 @@ from inline_snapshot import snapshot +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.cdp.validation import validate_inputs, validate_inputs_schema from posthog.test.base import APIBaseTest, ClickhouseTestMixin, QueryMatchingTest @@ -78,7 +79,12 @@ def test_validate_inputs(self): { "url": { "value": "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937", - "bytecode": ["_h", 32, "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937"], + "bytecode": [ + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "http://localhost:2080/0e02d917-563f-4050-9725-aad881b69937", + ], }, "payload": { "value": { @@ -89,11 +95,25 @@ def test_validate_inputs(self): "event_url": "{f'{event.url}-test'}", }, "bytecode": { - "event": ["_h", 32, "event", 1, 1], - "groups": ["_h", 32, "groups", 1, 1], - "nested": {"foo": ["_h", 32, "url", 32, "event", 1, 2]}, - "person": ["_h", 32, "person", 1, 1], - "event_url": ["_h", 32, "-test", 32, "url", 32, "event", 1, 2, 2, "concat", 2], + "event": ["_H", HOGQL_BYTECODE_VERSION, 32, "event", 1, 1], + "groups": ["_H", HOGQL_BYTECODE_VERSION, 32, "groups", 1, 1], + "nested": {"foo": ["_H", HOGQL_BYTECODE_VERSION, 32, "url", 32, "event", 1, 2]}, + "person": ["_H", HOGQL_BYTECODE_VERSION, 32, "person", 1, 1], + "event_url": [ + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "url", + 32, + "event", + 1, + 2, + 32, + "-test", + 2, + "concat", + 2, + ], }, }, "method": {"value": "POST"}, @@ -101,7 +121,10 @@ def test_validate_inputs(self): "value": {"version": "v={event.properties.$lib_version}"}, "bytecode": { "version": [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, + 32, + "v=", 32, "$lib_version", 32, @@ -110,8 +133,6 @@ def test_validate_inputs(self): "event", 1, 3, - 32, - "v=", 2, "concat", 2, @@ -140,9 +161,10 @@ def test_validate_inputs_creates_bytecode_for_html(self): { "html": { "bytecode": [ - "_h", + "_H", + HOGQL_BYTECODE_VERSION, 32, - "

\n\n", + '\n\n\n\n\n\n

Hi ', 32, "email", 32, @@ -152,7 +174,7 @@ def test_validate_inputs_creates_bytecode_for_html(self): 1, 3, 32, - '\n\n\n\n\n\n

Hi ', + "

\n\n", 2, "concat", 3, diff --git a/posthog/hogql/bytecode.py b/posthog/hogql/bytecode.py index 36d4e47e9eb72..1d71ec173d372 100644 --- a/posthog/hogql/bytecode.py +++ b/posthog/hogql/bytecode.py @@ -14,6 +14,7 @@ from hogvm.python.operation import ( Operation, HOGQL_BYTECODE_IDENTIFIER, + HOGQL_BYTECODE_VERSION, ) from posthog.schema import HogQLNotice @@ -65,7 +66,8 @@ def create_bytecode( bytecode: list[Any] = [] if args is None: bytecode.append(HOGQL_BYTECODE_IDENTIFIER) - bytecode.extend(BytecodeBuilder(supported_functions, args, context).visit(expr)) + bytecode.append(HOGQL_BYTECODE_VERSION) + bytecode.extend(BytecodeCompiler(supported_functions, args, context).visit(expr)) return bytecode @@ -82,7 +84,7 @@ class HogFunction: bytecode: list[Any] -class BytecodeBuilder(Visitor): +class BytecodeCompiler(Visitor): def __init__( self, supported_functions: Optional[set[str]] = None, @@ -97,7 +99,7 @@ def __init__( self.args = args # we're in a function definition if args is not None: - for arg in reversed(args): + for arg in args: self._declare_local(arg) self.context = context or HogQLContext(team_id=None) @@ -126,7 +128,7 @@ def _declare_local(self, name: str) -> int: def visit_and(self, node: ast.And): response = [] - for expr in reversed(node.exprs): + for expr in node.exprs: response.extend(self.visit(expr)) response.append(Operation.AND) response.append(len(node.exprs)) @@ -134,7 +136,7 @@ def visit_and(self, node: ast.And): def visit_or(self, node: ast.Or): response = [] - for expr in reversed(node.exprs): + for expr in node.exprs: response.extend(self.visit(expr)) response.append(Operation.OR) response.append(len(node.exprs)) @@ -224,12 +226,12 @@ def visit_call(self, node: ast.Call): return [*self.visit(node.args[0]), Operation.NOT] if node.name == "and" and len(node.args) > 1: args = [] - for arg in reversed(node.args): + for arg in node.args: args.extend(self.visit(arg)) return [*args, Operation.AND, len(node.args)] if node.name == "or" and len(node.args) > 1: args = [] - for arg in reversed(node.args): + for arg in node.args: args.extend(self.visit(arg)) return [*args, Operation.OR, len(node.args)] if node.name == "if" and len(node.args) >= 2: @@ -278,16 +280,16 @@ def visit_call(self, node: ast.Call): ) response = [] + for expr in node.args: + response.extend(self.visit(expr)) + if node.name in MIN_ARGS_INCLUDING_OPTIONAL and len(node.args) < MIN_ARGS_INCLUDING_OPTIONAL[node.name]: for _ in range(len(node.args), MIN_ARGS_INCLUDING_OPTIONAL[node.name]): response.append(Operation.NULL) - for expr in reversed(node.args): - response.extend(self.visit(expr)) - response.extend( [ - Operation.CALL, + Operation.CALL_GLOBAL, node.name, len(node.args) if node.name not in MIN_ARGS_INCLUDING_OPTIONAL @@ -484,18 +486,18 @@ def visit_for_in_statement(self, node: ast.ForInStatement): if key_var is not None: expr_keys_local = self._declare_local("__H_keys_H__") # keys - response.extend([Operation.GET_LOCAL, expr_local, Operation.CALL, "keys", 1]) + response.extend([Operation.GET_LOCAL, expr_local, Operation.CALL_GLOBAL, "keys", 1]) else: expr_keys_local = None expr_values_local = self._declare_local("__H_values_H__") # values - response.extend([Operation.GET_LOCAL, expr_local, Operation.CALL, "values", 1]) + response.extend([Operation.GET_LOCAL, expr_local, Operation.CALL_GLOBAL, "values", 1]) loop_index_local = self._declare_local("__H_index_H__") # 0 response.extend([Operation.INTEGER, 1]) loop_limit_local = self._declare_local("__H_limit_H__") # length of keys - response.extend([Operation.GET_LOCAL, expr_values_local, Operation.CALL, "length", 1]) + response.extend([Operation.GET_LOCAL, expr_values_local, Operation.CALL_GLOBAL, "length", 1]) if key_var is not None: key_var_local = self._declare_local(key_var) # loop key @@ -668,5 +670,9 @@ def execute_hog( if not source_code.endswith(";"): source_code = f"{source_code};" program = parse_program(source_code) - bytecode = create_bytecode(program) + bytecode = create_bytecode( + program, + supported_functions=set(functions.keys()) if functions is not None else set(), + context=HogQLContext(team_id=team.id if team else None), + ) return execute_bytecode(bytecode, globals=globals, functions=functions, timeout=timeout, team=team) diff --git a/posthog/hogql/test/test_bytecode.py b/posthog/hogql/test/test_bytecode.py index 29b2c4e5620b4..967066b3312f1 100644 --- a/posthog/hogql/test/test_bytecode.py +++ b/posthog/hogql/test/test_bytecode.py @@ -1,30 +1,31 @@ import pytest from posthog.hogql.bytecode import to_bytecode, execute_hog -from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H +from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H, HOGQL_BYTECODE_VERSION from posthog.hogql.errors import NotImplementedError, QueryError from posthog.test.base import BaseTest class TestBytecode(BaseTest): def test_bytecode_create(self): - self.assertEqual(to_bytecode("1 + 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.PLUS]) - self.assertEqual(to_bytecode("1 and 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.AND, 2]) - self.assertEqual(to_bytecode("1 or 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.OR, 2]) + self.assertEqual(to_bytecode("1 + 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.PLUS]) + self.assertEqual(to_bytecode("1 and 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 1, op.INTEGER, 2, op.AND, 2]) + self.assertEqual(to_bytecode("1 or 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 1, op.INTEGER, 2, op.OR, 2]) self.assertEqual( to_bytecode("1 or (2 and 1) or 2"), [ _H, - op.INTEGER, - 2, + HOGQL_BYTECODE_VERSION, op.INTEGER, 1, op.INTEGER, 2, + op.INTEGER, + 1, op.AND, 2, op.INTEGER, - 1, + 2, op.OR, 3, ], @@ -33,39 +34,41 @@ def test_bytecode_create(self): to_bytecode("(1 or 2) and (1 or 2)"), [ _H, - op.INTEGER, - 2, + HOGQL_BYTECODE_VERSION, op.INTEGER, 1, - op.OR, - 2, op.INTEGER, 2, + op.OR, + 2, op.INTEGER, 1, + op.INTEGER, + 2, op.OR, 2, op.AND, 2, ], ) - self.assertEqual(to_bytecode("not true"), [_H, op.TRUE, op.NOT]) - self.assertEqual(to_bytecode("true"), [_H, op.TRUE]) - self.assertEqual(to_bytecode("false"), [_H, op.FALSE]) - self.assertEqual(to_bytecode("null"), [_H, op.NULL]) - self.assertEqual(to_bytecode("3.14"), [_H, op.FLOAT, 3.14]) + self.assertEqual(to_bytecode("not true"), [_H, HOGQL_BYTECODE_VERSION, op.TRUE, op.NOT]) + self.assertEqual(to_bytecode("true"), [_H, HOGQL_BYTECODE_VERSION, op.TRUE]) + self.assertEqual(to_bytecode("false"), [_H, HOGQL_BYTECODE_VERSION, op.FALSE]) + self.assertEqual(to_bytecode("null"), [_H, HOGQL_BYTECODE_VERSION, op.NULL]) + self.assertEqual(to_bytecode("3.14"), [_H, HOGQL_BYTECODE_VERSION, op.FLOAT, 3.14]) self.assertEqual( to_bytecode("properties.bla"), - [_H, op.STRING, "bla", op.STRING, "properties", op.GET_GLOBAL, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "bla", op.STRING, "properties", op.GET_GLOBAL, 2], ) self.assertEqual( to_bytecode("concat('arg', 'another')"), - [_H, op.STRING, "another", op.STRING, "arg", op.CALL, "concat", 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "arg", op.STRING, "another", op.CALL_GLOBAL, "concat", 2], ) self.assertEqual( to_bytecode("ifNull(properties.email, false)"), [ _H, + HOGQL_BYTECODE_VERSION, op.STRING, "email", op.STRING, @@ -78,91 +81,111 @@ def test_bytecode_create(self): op.FALSE, ], ) - self.assertEqual(to_bytecode("1 = 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.EQ]) - self.assertEqual(to_bytecode("1 == 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.EQ]) - self.assertEqual(to_bytecode("1 != 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.NOT_EQ]) - self.assertEqual(to_bytecode("1 < 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.LT]) - self.assertEqual(to_bytecode("1 <= 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.LT_EQ]) - self.assertEqual(to_bytecode("1 > 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.GT]) - self.assertEqual(to_bytecode("1 >= 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.GT_EQ]) - self.assertEqual(to_bytecode("1 like 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.LIKE]) - self.assertEqual(to_bytecode("1 ilike 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.ILIKE]) - self.assertEqual(to_bytecode("1 not like 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.NOT_LIKE]) + self.assertEqual(to_bytecode("1 = 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.EQ]) + self.assertEqual(to_bytecode("1 == 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.EQ]) + self.assertEqual(to_bytecode("1 != 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.NOT_EQ]) + self.assertEqual(to_bytecode("1 < 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.LT]) + self.assertEqual(to_bytecode("1 <= 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.LT_EQ]) + self.assertEqual(to_bytecode("1 > 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.GT]) + self.assertEqual(to_bytecode("1 >= 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.GT_EQ]) + self.assertEqual(to_bytecode("1 like 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.LIKE]) + self.assertEqual(to_bytecode("1 ilike 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.ILIKE]) + self.assertEqual( + to_bytecode("1 not like 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.NOT_LIKE] + ) self.assertEqual( to_bytecode("1 not ilike 2"), - [_H, op.INTEGER, 2, op.INTEGER, 1, op.NOT_ILIKE], + [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.NOT_ILIKE], + ) + self.assertEqual(to_bytecode("1 in 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.IN]) + self.assertEqual( + to_bytecode("1 not in 2"), [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 2, op.INTEGER, 1, op.NOT_IN] ) - self.assertEqual(to_bytecode("1 in 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.IN]) - self.assertEqual(to_bytecode("1 not in 2"), [_H, op.INTEGER, 2, op.INTEGER, 1, op.NOT_IN]) self.assertEqual( to_bytecode("'string' ~ 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.REGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.REGEX], ) self.assertEqual( to_bytecode("'string' =~ 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.REGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.REGEX], ) self.assertEqual( to_bytecode("'string' !~ 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.NOT_REGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.NOT_REGEX], ) self.assertEqual( to_bytecode("'string' ~* 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.IREGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.IREGEX], ) self.assertEqual( to_bytecode("'string' =~* 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.IREGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.IREGEX], ) self.assertEqual( to_bytecode("'string' !~* 'regex'"), - [_H, op.STRING, "regex", op.STRING, "string", op.NOT_IREGEX], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "regex", op.STRING, "string", op.NOT_IREGEX], ) self.assertEqual( to_bytecode("match('test', 'e.*')"), - [_H, op.STRING, "e.*", op.STRING, "test", op.CALL, "match", 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.STRING, "e.*", op.CALL_GLOBAL, "match", 2], ) self.assertEqual( to_bytecode("match('test', '^e.*')"), - [_H, op.STRING, "^e.*", op.STRING, "test", op.CALL, "match", 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.STRING, "^e.*", op.CALL_GLOBAL, "match", 2], ) self.assertEqual( to_bytecode("match('test', 'x.*')"), - [_H, op.STRING, "x.*", op.STRING, "test", op.CALL, "match", 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.STRING, "x.*", op.CALL_GLOBAL, "match", 2], ) - self.assertEqual(to_bytecode("not('test')"), [_H, op.STRING, "test", op.NOT]) - self.assertEqual(to_bytecode("not 'test'"), [_H, op.STRING, "test", op.NOT]) + self.assertEqual(to_bytecode("not('test')"), [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.NOT]) + self.assertEqual(to_bytecode("not 'test'"), [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.NOT]) self.assertEqual( to_bytecode("or('test', 'test2')"), - [_H, op.STRING, "test2", op.STRING, "test", op.OR, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.STRING, "test2", op.OR, 2], ) self.assertEqual( to_bytecode("and('test', 'test2')"), - [_H, op.STRING, "test2", op.STRING, "test", op.AND, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "test", op.STRING, "test2", op.AND, 2], ) @pytest.mark.skip(reason="C++ parsing is not working for these cases yet.") def test_bytecode_objects(self): self.assertEqual( to_bytecode("[1, 2, 3]"), - [_H, op.INTEGER, 1, op.INTEGER, 2, op.INTEGER, 3, op.ARRAY, 3], + [_H, HOGQL_BYTECODE_VERSION, op.INTEGER, 1, op.INTEGER, 2, op.INTEGER, 3, op.ARRAY, 3], ) self.assertEqual( to_bytecode("[1, 2, 3][1]"), - [_H, op.INTEGER, 1, op.INTEGER, 2, op.INTEGER, 3, op.ARRAY, 3, op.INTEGER, 1, op.GET_PROPERTY, 1], + [ + _H, + HOGQL_BYTECODE_VERSION, + op.INTEGER, + 1, + op.INTEGER, + 2, + op.INTEGER, + 3, + op.ARRAY, + 3, + op.INTEGER, + 1, + op.GET_PROPERTY, + 1, + ], ) self.assertEqual( to_bytecode("{'a': 'b'}"), - [_H, op.STRING, "a", op.STRING, "b", op.DICT, 1], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "a", op.STRING, "b", op.DICT, 1], ) self.assertEqual( to_bytecode("{'a': 'b', 'c': 'd'}"), - [_H, op.STRING, "a", op.STRING, "b", op.STRING, "c", op.STRING, "d", op.DICT, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "a", op.STRING, "b", op.STRING, "c", op.STRING, "d", op.DICT, 2], ) self.assertEqual( to_bytecode("{'a': 'b', 'c': {'a': 'b'}}"), [ _H, + HOGQL_BYTECODE_VERSION, op.STRING, "a", op.STRING, @@ -181,17 +204,17 @@ def test_bytecode_objects(self): ) self.assertEqual( to_bytecode("['a', 'b']"), - [_H, op.STRING, "a", op.STRING, "b", op.ARRAY, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "a", op.STRING, "b", op.ARRAY, 2], ) self.assertEqual( to_bytecode("('a', 'b')"), - [_H, op.STRING, "a", op.STRING, "b", op.TUPLE, 2], + [_H, HOGQL_BYTECODE_VERSION, op.STRING, "a", op.STRING, "b", op.TUPLE, 2], ) def test_bytecode_create_not_implemented_error(self): with self.assertRaises(NotImplementedError) as e: to_bytecode("(select 1)") - self.assertEqual(str(e.exception), "BytecodeBuilder has no method visit_select_query") + self.assertEqual(str(e.exception), "BytecodeCompiler has no method visit_select_query") def test_bytecode_create_query_error(self): with self.assertRaises(QueryError) as e: diff --git a/posthog/management/commands/test/test_migrate_action_webhooks.py b/posthog/management/commands/test/test_migrate_action_webhooks.py index 52e625a0610d5..45f6a393b2809 100644 --- a/posthog/management/commands/test/test_migrate_action_webhooks.py +++ b/posthog/management/commands/test/test_migrate_action_webhooks.py @@ -1,4 +1,6 @@ from inline_snapshot import snapshot + +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.cdp.templates.webhook.template_webhook import template as template_webhook from posthog.management.commands.migrate_action_webhooks import migrate_action_webhooks from posthog.models.action.action import Action @@ -54,7 +56,7 @@ def test_migrates_base_action_config_correctly(self): assert hog_function.name == f"Webhook for action {self.action.id} (Test Action)" assert hog_function.filters == { "actions": [{"id": f"{self.action.id}", "name": "Test Action", "type": "actions", "order": 0}], - "bytecode": ["_h", 29, 3, 1, 4, 1], + "bytecode": ["_H", HOGQL_BYTECODE_VERSION, 29, 3, 1, 4, 1], } assert hog_function.hog == template_webhook.hog assert hog_function.inputs_schema == template_webhook.inputs_schema diff --git a/posthog/management/commands/test/test_migrate_hooks.py b/posthog/management/commands/test/test_migrate_hooks.py index 90910c915c469..62841f0785c2f 100644 --- a/posthog/management/commands/test/test_migrate_hooks.py +++ b/posthog/management/commands/test/test_migrate_hooks.py @@ -1,4 +1,5 @@ from ee.models.hook import Hook +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.cdp.templates.zapier.template_zapier import template as template_zapier from posthog.management.commands.migrate_hooks import migrate_hooks from posthog.models.action.action import Action @@ -54,7 +55,7 @@ def test_migrates_hook_correctly(self): assert hog_function.name == f"Zapier webhook for action {self.action.id}" assert hog_function.filters == { "actions": [{"id": f"{self.action.id}", "name": "", "type": "actions", "order": 0}], - "bytecode": ["_h", 29, 3, 1, 4, 1], + "bytecode": ["_H", HOGQL_BYTECODE_VERSION, 29, 3, 1, 4, 1], } assert hog_function.hog == template_zapier.hog assert hog_function.inputs_schema == template_zapier.inputs_schema diff --git a/posthog/models/test/test_hog_function.py b/posthog/models/test/test_hog_function.py index 02bc9fb62e3c6..95d53f916a6c9 100644 --- a/posthog/models/test/test_hog_function.py +++ b/posthog/models/test/test_hog_function.py @@ -2,6 +2,7 @@ from django.test import TestCase from inline_snapshot import snapshot +from hogvm.python.operation import HOGQL_BYTECODE_VERSION from posthog.models.action.action import Action from posthog.models.hog_functions.hog_function import HogFunction from posthog.models.user import User @@ -30,7 +31,7 @@ def test_hog_function_team_no_filters_compilation(self): # Some json serialization is needed to compare the bytecode more easily in tests json_filters = to_dict(item.filters) - assert json_filters["bytecode"] == ["_h", 29] # TRUE + assert json_filters["bytecode"] == ["_H", HOGQL_BYTECODE_VERSION, 29] # TRUE def test_hog_function_filters_compilation(self): item = HogFunction.objects.create( @@ -45,20 +46,13 @@ def test_hog_function_filters_compilation(self): # Some json serialization is needed to compare the bytecode more easily in tests json_filters = to_dict(item.filters) - assert json_filters == { "events": [{"id": "$pageview", "name": "$pageview", "type": "events", "order": 0}], "actions": [{"id": "9", "name": "Test Action", "type": "actions", "order": 1}], "filter_test_accounts": True, "bytecode": [ - "_h", - 33, - 2, - 33, - 1, - 11, - 32, - "^(localhost|127\\.0\\.0\\.1)($|:)", + "_H", + HOGQL_BYTECODE_VERSION, 32, "$host", 32, @@ -68,6 +62,8 @@ def test_hog_function_filters_compilation(self): 2, "toString", 1, + 32, + "^(localhost|127\\.0\\.0\\.1)($|:)", 2, "match", 2, @@ -77,8 +73,6 @@ def test_hog_function_filters_compilation(self): 35, 33, 1, - 3, - 2, 32, "$pageview", 32, @@ -86,8 +80,8 @@ def test_hog_function_filters_compilation(self): 1, 1, 11, - 32, - "^(localhost|127\\.0\\.0\\.1)($|:)", + 3, + 2, 32, "$host", 32, @@ -97,6 +91,8 @@ def test_hog_function_filters_compilation(self): 2, "toString", 1, + 32, + "^(localhost|127\\.0\\.0\\.1)($|:)", 2, "match", 2, @@ -106,6 +102,11 @@ def test_hog_function_filters_compilation(self): 35, 33, 1, + 33, + 2, + 33, + 1, + 11, 3, 2, 4, @@ -126,7 +127,7 @@ def test_hog_function_team_filters_only_compilation(self): json_filters = to_dict(item.filters) assert json.dumps(json_filters["bytecode"]) == snapshot( - '["_h", 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 2, "match", 2, 5, 47, 3, 35, 33, 1, 3, 1]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 2, "match", 2, 5, 47, 3, 35, 33, 1, 3, 1]' ) @@ -197,11 +198,11 @@ def test_hog_functions_reload_on_action_saved(self): # Check that the bytecode is correct assert json.dumps(hog_function_1.filters["bytecode"]) == snapshot( - '["_h", 32, "old-value-2", 32, "prop-2", 32, "properties", 1, 2, 11, 3, 1, 32, "old-value-1", 32, "prop-1", 32, "properties", 1, 2, 11, 32, "test-event", 32, "event", 1, 1, 11, 3, 2, 3, 1, 4, 2]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "test-event", 32, "event", 1, 1, 11, 32, "old-value-1", 32, "prop-1", 32, "properties", 1, 2, 11, 3, 2, 3, 1, 32, "old-value-2", 32, "prop-2", 32, "properties", 1, 2, 11, 3, 1, 4, 2]' ) assert json.dumps(hog_function_2.filters["bytecode"]) == snapshot( - '["_h", 32, "old-value-1", 32, "prop-1", 32, "properties", 1, 2, 11, 32, "test-event", 32, "event", 1, 1, 11, 3, 2, 3, 1, 4, 1]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "test-event", 32, "event", 1, 1, 11, 32, "old-value-1", 32, "prop-1", 32, "properties", 1, 2, 11, 3, 2, 3, 1, 4, 1]' ) # Modify the action and check that the bytecode is updated @@ -225,10 +226,10 @@ def test_hog_functions_reload_on_action_saved(self): hog_function_2.refresh_from_db() assert json.dumps(hog_function_1.filters["bytecode"]) == snapshot( - '["_h", 32, "old-value-2", 32, "prop-2", 32, "properties", 1, 2, 11, 3, 1, 32, "change-value", 32, "prop-1", 32, "properties", 1, 2, 11, 32, "test-event", 32, "event", 1, 1, 11, 3, 2, 3, 1, 4, 2]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "test-event", 32, "event", 1, 1, 11, 32, "change-value", 32, "prop-1", 32, "properties", 1, 2, 11, 3, 2, 3, 1, 32, "old-value-2", 32, "prop-2", 32, "properties", 1, 2, 11, 3, 1, 4, 2]' ) assert json.dumps(hog_function_2.filters["bytecode"]) == snapshot( - '["_h", 32, "change-value", 32, "prop-1", 32, "properties", 1, 2, 11, 32, "test-event", 32, "event", 1, 1, 11, 3, 2, 3, 1, 4, 1]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "test-event", 32, "event", 1, 1, 11, 32, "change-value", 32, "prop-1", 32, "properties", 1, 2, 11, 3, 2, 3, 1, 4, 1]' ) def test_hog_functions_reload_on_team_saved(self): @@ -258,11 +259,11 @@ def test_hog_functions_reload_on_team_saved(self): ) # Check that the bytecode is correct - assert json.dumps(hog_function_1.filters["bytecode"]) == snapshot('["_h", 29]') + assert json.dumps(hog_function_1.filters["bytecode"]) == snapshot(f'["_H", {HOGQL_BYTECODE_VERSION}, 29]') assert json.dumps(hog_function_2.filters["bytecode"]) == snapshot( - '["_h", 32, "$pageview", 32, "event", 1, 1, 11, 3, 1, 4, 1]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "$pageview", 32, "event", 1, 1, 11, 3, 1, 4, 1]' ) - assert json.dumps(hog_function_3.filters["bytecode"]) == snapshot('["_h", 29]') + assert json.dumps(hog_function_3.filters["bytecode"]) == snapshot(f'["_H", {HOGQL_BYTECODE_VERSION}, 29]') # Modify the action and check that the bytecode is updated self.team.test_account_filters = [ @@ -277,9 +278,9 @@ def test_hog_functions_reload_on_team_saved(self): hog_function_3.refresh_from_db() assert json.dumps(hog_function_1.filters["bytecode"]) == snapshot( - '["_h", 32, "test", 32, "$pageview", 32, "properties", 1, 2, 2, "toString", 1, 2, "match", 2, 47, 3, 35, 33, 0, 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 2, "match", 2, 47, 3, 35, 33, 0, 3, 2]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 2, "match", 2, 47, 3, 35, 33, 0, 32, "$pageview", 32, "properties", 1, 2, 2, "toString", 1, 32, "test", 2, "match", 2, 47, 3, 35, 33, 0, 3, 2]' ) assert json.dumps(hog_function_2.filters["bytecode"]) == snapshot( - '["_h", 32, "$pageview", 32, "event", 1, 1, 11, 32, "test", 32, "$pageview", 32, "properties", 1, 2, 2, "toString", 1, 2, "match", 2, 47, 3, 35, 33, 0, 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 2, "match", 2, 47, 3, 35, 33, 0, 3, 3, 4, 1]' + f'["_H", {HOGQL_BYTECODE_VERSION}, 32, "$host", 32, "properties", 1, 2, 2, "toString", 1, 32, "^(localhost|127\\\\.0\\\\.0\\\\.1)($|:)", 2, "match", 2, 47, 3, 35, 33, 0, 32, "$pageview", 32, "properties", 1, 2, 2, "toString", 1, 32, "test", 2, "match", 2, 47, 3, 35, 33, 0, 32, "$pageview", 32, "event", 1, 1, 11, 3, 3, 4, 1]' ) - assert json.dumps(hog_function_3.filters["bytecode"]) == snapshot('["_h", 29]') + assert json.dumps(hog_function_3.filters["bytecode"]) == snapshot(f'["_H", {HOGQL_BYTECODE_VERSION}, 29]')