0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-24 00:47:50 +01:00

feat(cdp): hog to JS transpiler (#26143)

This commit is contained in:
Marius Andra 2024-11-18 14:38:25 +01:00 committed by GitHub
parent fc6038118f
commit 0fd91f4743
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
69 changed files with 5450 additions and 114 deletions

2
.vscode/launch.json vendored
View File

@ -97,7 +97,7 @@
"console": "integratedTerminal", "console": "integratedTerminal",
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
"env": { "env": {
"SKIP_ASYNC_MIGRATIONS_SETUP": "0", "SKIP_ASYNC_MIGRATIONS_SETUP": "1",
"DEBUG": "1", "DEBUG": "1",
"BILLING_SERVICE_URL": "https://billing.dev.posthog.dev", "BILLING_SERVICE_URL": "https://billing.dev.posthog.dev",
"SKIP_SERVICE_VERSION_REQUIREMENTS": "1" "SKIP_SERVICE_VERSION_REQUIREMENTS": "1"

View File

@ -3,10 +3,13 @@ set -e
if [[ "$@" == *".hog"* ]]; then if [[ "$@" == *".hog"* ]]; then
exec python3 -m posthog.hogql.cli --compile "$@" exec python3 -m posthog.hogql.cli --compile "$@"
elif [[ "$@" == *".js"* ]]; then
exec python3 -m posthog.hogql.cli --compile "$@"
else else
echo "$0 - the Hog compilër! 🦔+🕶️= Hoge" echo "$0 - the Hog compilër! 🦔+🕶️= Hoge"
echo "" echo ""
echo "Usage: bin/hoge <file.hog> [output.hoge] compile .hog into .hoge" echo "Usage: bin/hoge <file.hog> [output.hoge] compile .hog into .hoge"
echo " bin/hoge <file.hog> <output.js> compile .hog into .js"
echo " bin/hog <file.hog> run .hog source code" echo " bin/hog <file.hog> run .hog source code"
echo " bin/hog <file.hoge> run compiled .hoge bytecode" echo " bin/hog <file.hoge> run compiled .hoge bytecode"
exit 1 exit 1

View File

@ -1,7 +1,7 @@
import dataclasses import dataclasses
from posthog.client import sync_execute from posthog.client import sync_execute
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.hogql import HogQLContext from posthog.hogql.hogql import HogQLContext
from posthog.hogql.property import action_to_expr from posthog.hogql.property import action_to_expr
from posthog.models.action import Action from posthog.models.action import Action

View File

@ -29,4 +29,6 @@
33, 2, 33, 3, 43, 3, 33, 4, 2, "indexOf", 2, 2, "print", 1, 35, 52, "lambda", 1, 0, 6, 33, 2, 36, 0, 13, 38, 53, 0, 33, 33, 2, 33, 3, 43, 3, 33, 4, 2, "indexOf", 2, 2, "print", 1, 35, 52, "lambda", 1, 0, 6, 33, 2, 36, 0, 13, 38, 53, 0, 33,
1, 33, 2, 33, 3, 33, 4, 33, 5, 43, 5, 2, "arrayCount", 2, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, 33, 1, 33, 1, 33, 2, 33, 3, 33, 4, 33, 5, 43, 5, 2, "arrayCount", 2, 2, "print", 1, 35, 32, "------", 2, "print", 1, 35, 33, 1, 33,
2, 33, 3, 43, 3, 36, 3, 33, 1, 45, 36, 3, 33, 2, 45, 36, 3, 33, 3, 45, 36, 3, 33, 4, 45, 2, "print", 4, 35, 36, 3, 33, 2, 33, 3, 43, 3, 36, 3, 33, 1, 45, 36, 3, 33, 2, 45, 36, 3, 33, 3, 45, 36, 3, 33, 4, 45, 2, "print", 4, 35, 36, 3, 33,
-1, 45, 36, 3, 33, -2, 45, 36, 3, 33, -3, 45, 36, 3, 33, -4, 45, 2, "print", 4, 35, 35, 35, 35, 35] -1, 45, 36, 3, 33, -2, 45, 36, 3, 33, -3, 45, 36, 3, 33, -4, 45, 2, "print", 4, 35, 32, "------", 2, "print", 1, 35, 32,
"a", 32, "b", 32, "c", 43, 3, 32, "a", 21, 2, "print", 1, 35, 32, "a", 32, "b", 32, "c", 43, 3, 32, "d", 21, 2, "print",
1, 35, 43, 0, 32, "a", 21, 2, "print", 1, 35, 35, 35, 35, 35]

View File

@ -0,0 +1,144 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function indexOf (arrOrString, elem) { if (Array.isArray(arrOrString)) { return arrOrString.indexOf(elem) + 1 } else { return 0 } }
function has (arr, elem) { if (!Array.isArray(arr) || arr.length === 0) { return false } return arr.includes(elem) }
function arrayStringConcat (arr, separator = '') { if (!Array.isArray(arr)) { return '' } return arr.join(separator) }
function arraySort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort() }
function arrayReverseSort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort().reverse() }
function arrayReverse (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].reverse() }
function arrayPushFront (arr, item) { if (!Array.isArray(arr)) { return [item] } return [item, ...arr] }
function arrayPushBack (arr, item) { if (!Array.isArray(arr)) { return [item] } return [...arr, item] }
function arrayPopFront (arr) { if (!Array.isArray(arr)) { return [] } return arr.slice(1) }
function arrayPopBack (arr) { if (!Array.isArray(arr)) { return [] } return arr.slice(0, arr.length - 1) }
function arrayCount (func, arr) { let count = 0; for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { count = count + 1 } } return count }
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print([]);
print([1, 2, 3]);
print([1, "2", 3]);
print([1, [2, 3], 4]);
print([1, [2, [3, 4]], 5]);
let a = [1, 2, 3];
print(__getProperty(a, 2, false));
print(__getProperty(a, 2, true));
print(__getProperty(a, 2, true));
print(__getProperty(a, 7, true));
print(__getProperty(a, 7, true));
print(__getProperty([1, 2, 3], 2, false));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 2, false), 2, false), 2, false));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 2, true), 2, true), 2, true));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 2, true), 2, true), 2, true));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 7, true), 4, true), 2, true));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 7, true), 4, true), 2, true));
print((__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 2, false), 2, false), 2, false) + 1));
print(__getProperty(__getProperty(__getProperty([1, [2, [3, 4]], 5], 2, false), 2, false), 2, false));
print("------");
let b = [1, 2, [1, 2, 3]];
__setProperty(b, 2, 4);
print(__getProperty(b, 1, false));
print(__getProperty(b, 2, false));
print(__getProperty(b, 3, false));
__setProperty(__getProperty(b, 3, false), 3, 8);
print(b);
print("------");
print(arrayPushBack([1, 2, 3], 4));
print(arrayPushFront([1, 2, 3], 0));
print(arrayPopBack([1, 2, 3]));
print(arrayPopFront([1, 2, 3]));
print(arraySort([3, 2, 1]));
print(arrayReverse([1, 2, 3]));
print(arrayReverseSort([3, 2, 1]));
print(arrayStringConcat([1, 2, 3], ","));
print("-----");
let arr = [1, 2, 3, 4];
print(arr);
arrayPushBack(arr, 5);
print(arr);
arrayPushFront(arr, 0);
print(arr);
arrayPopBack(arr);
print(arr);
arrayPopFront(arr);
print(arr);
arraySort(arr);
print(arr);
arrayReverse(arr);
print(arr);
arrayReverseSort(arr);
print(arr);
print("------");
print(has(arr, 0));
print(has(arr, 2));
print(has(arr, "banana"));
print(has("banananas", "banana"));
print(has("banananas", "foo"));
print(has(["1", "2"], "1"));
print(indexOf([1, 2, 3], 1));
print(indexOf([1, 2, 3], 2));
print(indexOf([1, 2, 3], 3));
print(indexOf([1, 2, 3], 4));
print(arrayCount(__lambda((x) => (x > 2)), [1, 2, 3, 4, 5]));
print("------");
let c = [1, 2, 3];
print(__getProperty(c, 1, false), __getProperty(c, 2, false), __getProperty(c, 3, false), __getProperty(c, 4, false));
print(__getProperty(c, -1, false), __getProperty(c, -2, false), __getProperty(c, -3, false), __getProperty(c, -4, false));
print("------");
print((["a", "b", "c"].includes("a")));
print((["a", "b", "c"].includes("d")));
print(([].includes("a")));

View File

@ -54,3 +54,7 @@ true
------ ------
1 2 3 null 1 2 3 null
3 2 1 null 3 2 1 null
------
true
false
false

View File

@ -0,0 +1,66 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function like (str, pattern) { return __like(str, pattern, false) }
function arrayMap (func, arr) { let result = []; for (let i = 0; i < arr.length; i++) { result = arrayPushBack(result, func(arr[i])) } return result }
function arrayFilter (func, arr) { let result = []; for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { result = arrayPushBack(result, arr[i]) } } return result}
function arrayPushBack (arr, item) { if (!Array.isArray(arr)) { return [item] } return [...arr, item] }
function arrayExists (func, arr) { for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { return true } } return false }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __like(str, pattern, caseInsensitive = false) {
if (caseInsensitive) {
str = str.toLowerCase()
pattern = pattern.toLowerCase()
}
pattern = String(pattern)
.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
.replaceAll('%', '.*')
.replaceAll('_', '.')
return new RegExp(pattern).test(str)
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("--- arrayMap ----");
print(arrayMap(__lambda((x) => (x * 2)), [1, 2, 3]));
print("--- arrayExists ----");
print(arrayExists(__lambda((x) => like(x, "%nana%")), ["apple", "banana", "cherry"]));
print(arrayExists(__lambda((x) => like(x, "%boom%")), ["apple", "banana", "cherry"]));
print(arrayExists(__lambda((x) => like(x, "%boom%")), []));
print("--- arrayFilter ----");
print(arrayFilter(__lambda((x) => like(x, "%nana%")), ["apple", "banana", "cherry"]));
print(arrayFilter(__lambda((x) => like(x, "%e%")), ["apple", "banana", "cherry"]));
print(arrayFilter(__lambda((x) => like(x, "%boom%")), []));

View File

@ -0,0 +1,134 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
function NotImplementedError (message, payload) { return __newHogError('NotImplementedError', message, payload) }
function HogError (type, message, payload) { return __newHogError(type, message, payload) }
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function FishError(message) {
return HogError("FishError", message);
}
function FoodError(message) {
return HogError("FoodError", message);
}
try {
throw FishError("You forgot to feed your fish");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else if (__error.type === "FishError") { let e = __error;
print(concat("Problem with your fish: ", __getProperty(e, "message", true)));
}
else { throw __error; }}
try {
throw FoodError("Your fish are hungry");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else if (__error.type === "FishError") { let e = __error;
print(concat("Problem with your fish: ", __getProperty(e, "message", true)));
}
else { throw __error; }}
try {
throw NotImplementedError("Your fish are hungry");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else if (true) { let e = __error;
print(concat("Unknown problem: ", e));
}
}

View File

@ -6,10 +6,10 @@
"You forgot to feed your fish", 2, "HogError", 2, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, "You forgot to feed your fish", 2, "HogError", 2, 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, 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, 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, "concat", 2, 2, "print", 1, 35, 39, 25, 32, "Error of type ", 36, 0, 32, "type", 45, 32, ": ", 36, 0, 32, "message", 45,
2, "concat", 4, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 49, 50, 12, 32, "FishError", 32, 2, "concat", 4, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35, 50, 49, 50, 12, 32, "FishError", 32,
"You forgot to feed your fish", 2, "HogError", 2, 49, 51, 39, 32, 36, 0, 32, "type", 45, 32, "FoodError", 36, 1, 11, 40, "You forgot to feed your fish", 2, "HogError", 2, 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, 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, 51, 39, 55, 36, 0, 32, "type", 45, 32, "Error of type ", 36, 0, 32, "type", 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, "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] 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35]

View File

@ -0,0 +1,142 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
function HogError (type, message, payload) { return __newHogError(type, message, payload) }
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
try {
try {
throw HogError("FishError", "You forgot to feed your fish");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else { throw __error; }}
} catch (__error) { if (__error.type === "FishError") { let e = __error;
print(concat("FishError: ", __getProperty(e, "message", true)));
}
else if (true) { let e = __error;
print(concat("Error: ", __getProperty(e, "message", true)));
}
}
try {
try {
throw HogError("FunkyError", "You forgot to feed your fish");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else { throw __error; }}
} catch (__error) { if (__error.type === "FishError") { let e = __error;
print(concat("FishError: ", __getProperty(e, "message", true)));
}
else if (true) { let e = __error;
print(concat("Error of type ", __getProperty(e, "type", true), ": ", __getProperty(e, "message", true)));
}
}
try {
try {
throw HogError("FishError", "You forgot to feed your fish");
} catch (__error) { if (__error.type === "FoodError") { let e = __error;
print(concat("Problem with your food: ", __getProperty(e, "message", true)));
}
else { throw __error; }}
} catch (__error) { if (true) { let e = __error;
print(concat("Error of type ", __getProperty(e, "type", true), ": ", __getProperty(e, "message", true)));
}
else if (__error.type === "FishError") { let e = __error;
print(concat("FishError: ", __getProperty(e, "message", true)));
}
}

View File

@ -1,3 +1,3 @@
FishError: You forgot to feed your fish FishError: You forgot to feed your fish
Error of type : You forgot to feed your fish Error of type FunkyError: You forgot to feed your fish
Error of type : You forgot to feed your fish Error of type FishError: You forgot to feed your fish

View File

@ -0,0 +1,49 @@
function sha256HmacChainHex (data, options) { return 'sha256HmacChainHex not implemented' }
function sha256Hex (str, options) { return 'SHA256 is not implemented' }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function md5Hex(string) { return 'MD5 is not implemented' }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let string = "this is a secure string";
print("string:", string);
print("md5Hex(string):", md5Hex(string));
print("sha256Hex(string):", sha256Hex(string));
let data = ["1", "string", "more", "keys"];
print("data:", data);
print("sha256HmacChainHex(data):", sha256HmacChainHex(data));

View File

@ -0,0 +1,171 @@
function toUnixTimestampMilli (input, zone) { return __toUnixTimestampMilli(input, zone) }
function toUnixTimestamp (input, zone) { return __toUnixTimestamp(input, zone) }
function toTimeZone (input, zone) { return __toTimeZone(input, zone) }
function toString (value) { return __STLToString(value) }
function toInt(value) {
if (__isHogDateTime(value)) { return Math.floor(value.dt); }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = Math.floor((date - epoch) / (1000 * 60 * 60 * 24)); return diffInDays; }
return !isNaN(parseInt(value)) ? parseInt(value) : null; }
function toFloat(value) {
if (__isHogDateTime(value)) { return value.dt; }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = (date - epoch) / (1000 * 60 * 60 * 24); return diffInDays; }
return !isNaN(parseFloat(value)) ? parseFloat(value) : null; }
function toDateTime (input, zone) { return __toDateTime(input, zone) }
function toDate (input) { return __toDate(input) }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function fromUnixTimestampMilli (input) { return __fromUnixTimestampMilli(input) }
function fromUnixTimestamp (input) { return __fromUnixTimestamp(input) }
function __toUnixTimestampMilli(input, zone) { return __toUnixTimestamp(input, zone) * 1000 }
function __toUnixTimestamp(input, zone) {
if (__isHogDateTime(input)) { return input.dt; }
if (__isHogDate(input)) { return __toHogDateTime(input).dt; }
const date = new Date(input);
if (isNaN(date.getTime())) { throw new Error('Invalid date input'); }
return Math.floor(date.getTime() / 1000);}
function __toTimeZone(input, zone) { if (!__isHogDateTime(input)) { throw new Error('Expected a DateTime') }; return { ...input, zone }}
function __toDateTime(input, zone) { let dt;
if (typeof input === 'number') { dt = input; }
else { const date = new Date(input); if (isNaN(date.getTime())) { throw new Error('Invalid date input'); } dt = date.getTime() / 1000; }
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' }; }
function __toDate(input) { let date;
if (typeof input === 'number') { date = new Date(input * 1000); } else { date = new Date(input); }
if (isNaN(date.getTime())) { throw new Error('Invalid date input'); }
return { __hogDate__: true, year: date.getUTCFullYear(), month: date.getUTCMonth() + 1, day: date.getUTCDate() }; }
function __fromUnixTimestampMilli(input) { return __toHogDateTime(input / 1000) }
function __fromUnixTimestamp(input) { return __toHogDateTime(input) }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
let dt = fromUnixTimestamp(1234334543);
print(dt);
print(toString(dt));
print(toInt(toUnixTimestamp(dt)));
print("-");
let dt2 = toDate("2024-05-03");
print(dt2);
print(toString(dt2));
print(toInt(toUnixTimestamp(dt2)));
print("-");
let dt3 = toDateTime("2024-05-03T12:34:56Z");
print(dt3);
print(toString(dt3));
print(toInt(toUnixTimestamp(dt3)));
print("------");
print(toTimeZone(dt3, "Europe/Brussels"));
print(toString(toTimeZone(dt3, "Europe/Brussels")));
print("-");
print(toTimeZone(dt3, "Europe/Tallinn"));
print(toString(toTimeZone(dt3, "Europe/Tallinn")));
print("-");
print(toTimeZone(dt3, "America/New_York"));
print(toString(toTimeZone(dt3, "America/New_York")));
print("------");
let timestamp = fromUnixTimestamp(1234334543.123);
print("timestamp: ", timestamp);
print("toString(timestamp): ", toString(timestamp));
print("toInt(timestamp): ", toInt(timestamp));
print("toDateTime(toInt(timestamp)): ", toDateTime(toInt(timestamp)));
print("toInt(toDateTime(toInt(timestamp))): ", toInt(toDateTime(toInt(timestamp))));
print("toString(toDateTime(toInt(timestamp))): ", toString(toDateTime(toInt(timestamp))));
print("toFloat(timestamp): ", toFloat(timestamp));
print("toDateTime(toFloat(timestamp)): ", toDateTime(toFloat(timestamp)));
print("toFloat(toDateTime(toFloat(timestamp))): ", toFloat(toDateTime(toFloat(timestamp))));
print("toString(toDateTime(toFloat(timestamp))): ", toString(toDateTime(toFloat(timestamp))));
print("------");
let millisTs = fromUnixTimestampMilli(1234334543123);
print("millisTs: ", toString(millisTs));
print("toString(millisTs): ", toString(millisTs));
print("toInt(millisTs): ", toInt(millisTs));
print("toFloat(millisTs): ", toFloat(millisTs));
print("toUnixTimestampMilli(millisTs): ", toUnixTimestampMilli(millisTs));
print("------");
let date = toDate("2024-05-03");
print(date);
print(toString(date));
print(toInt(date));

View File

@ -0,0 +1,287 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function fromUnixTimestamp (input) { return __fromUnixTimestamp(input) }
function formatDateTime (input, format, zone) { return __formatDateTime(input, format, zone) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __fromUnixTimestamp(input) { return __toHogDateTime(input) }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __formatDateTime(input, format, zone) {
if (!__isHogDateTime(input)) { throw new Error('Expected a DateTime'); }
if (!format) { throw new Error('formatDateTime requires at least 2 arguments'); }
const timestamp = input.dt * 1000;
let date = new Date(timestamp);
if (!zone) { zone = 'UTC'; }
const padZero = (num, len = 2) => String(num).padStart(len, '0');
const padSpace = (num, len = 2) => String(num).padStart(len, ' ');
const getDateComponent = (type, options = {}) => {
const formatter = new Intl.DateTimeFormat('en-US', { ...options, timeZone: zone });
const parts = formatter.formatToParts(date);
const part = parts.find(p => p.type === type);
return part ? part.value : '';
};
const getNumericComponent = (type, options = {}) => {
const value = getDateComponent(type, options);
return parseInt(value, 10);
};
const getWeekNumber = (d) => {
const dateInZone = new Date(d.toLocaleString('en-US', { timeZone: zone }));
const target = new Date(Date.UTC(dateInZone.getFullYear(), dateInZone.getMonth(), dateInZone.getDate()));
const dayNr = (target.getUTCDay() + 6) % 7;
target.setUTCDate(target.getUTCDate() - dayNr + 3);
const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
const weekNumber = 1 + Math.round(((target - firstThursday) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7);
return weekNumber;
};
const getDayOfYear = (d) => {
const startOfYear = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const dateInZone = new Date(d.toLocaleString('en-US', { timeZone: zone }));
const diff = dateInZone - startOfYear;
return Math.floor(diff / 86400000) + 1;
};
// Token mapping with corrections
const tokens = {
'%a': () => getDateComponent('weekday', { weekday: 'short' }),
'%b': () => getDateComponent('month', { month: 'short' }),
'%c': () => padZero(getNumericComponent('month', { month: '2-digit' })),
'%C': () => getDateComponent('year', { year: '2-digit' }),
'%d': () => padZero(getNumericComponent('day', { day: '2-digit' })),
'%D': () => {
const month = padZero(getNumericComponent('month', { month: '2-digit' }));
const day = padZero(getNumericComponent('day', { day: '2-digit' }));
const year = getDateComponent('year', { year: '2-digit' });
return `${month}/${day}/${year}`;
},
'%e': () => padSpace(getNumericComponent('day', { day: 'numeric' })),
'%F': () => {
const year = getNumericComponent('year', { year: 'numeric' });
const month = padZero(getNumericComponent('month', { month: '2-digit' }));
const day = padZero(getNumericComponent('day', { day: '2-digit' }));
return `${year}-${month}-${day}`;
},
'%g': () => getDateComponent('year', { year: '2-digit' }),
'%G': () => getNumericComponent('year', { year: 'numeric' }),
'%h': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%H': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false })),
'%i': () => padZero(getNumericComponent('minute', { minute: '2-digit' })),
'%I': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%j': () => padZero(getDayOfYear(date), 3),
'%k': () => padSpace(getNumericComponent('hour', { hour: 'numeric', hour12: false })),
'%l': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%m': () => padZero(getNumericComponent('month', { month: '2-digit' })),
'%M': () => getDateComponent('month', { month: 'long' }),
'%n': () => '\n',
'%p': () => getDateComponent('dayPeriod', { hour: 'numeric', hour12: true }),
'%r': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
const second = padZero(getNumericComponent('second', { second: '2-digit' }));
const period = getDateComponent('dayPeriod', { hour: 'numeric', hour12: true });
return `${hour}:${minute} ${period}`;
},
'%R': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
return `${hour}:${minute}`;
},
'%s': () => padZero(getNumericComponent('second', { second: '2-digit' })),
'%S': () => padZero(getNumericComponent('second', { second: '2-digit' })),
'%t': () => '\t',
'%T': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
const second = padZero(getNumericComponent('second', { second: '2-digit' }));
return `${hour}:${minute}:${second}`;
},
'%u': () => {
let day = getDateComponent('weekday', { weekday: 'short' });
const dayMap = { 'Mon': '1', 'Tue': '2', 'Wed': '3', 'Thu': '4', 'Fri': '5', 'Sat': '6', 'Sun': '7' };
return dayMap[day];
},
'%V': () => padZero(getWeekNumber(date)),
'%w': () => {
let day = getDateComponent('weekday', { weekday: 'short' });
const dayMap = { 'Sun': '0', 'Mon': '1', 'Tue': '2', 'Wed': '3', 'Thu': '4', 'Fri': '5', 'Sat': '6' };
return dayMap[day];
},
'%W': () => getDateComponent('weekday', { weekday: 'long' }),
'%y': () => getDateComponent('year', { year: '2-digit' }),
'%Y': () => getNumericComponent('year', { year: 'numeric' }),
'%z': () => {
if (zone === 'UTC') {
return '+0000';
} else {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
timeZoneName: 'shortOffset',
});
const parts = formatter.formatToParts(date);
const offsetPart = parts.find(part => part.type === 'timeZoneName');
if (offsetPart && offsetPart.value) {
const offsetValue = offsetPart.value;
const match = offsetValue.match(/GMT([+-]\d{1,2})(?::(\d{2}))?/);
if (match) {
const sign = match[1][0];
const hours = padZero(Math.abs(parseInt(match[1], 10)));
const minutes = padZero(match[2] ? parseInt(match[2], 10) : 0);
return `${sign}${hours}${minutes}`;
}
}
return '';
}
},
'%%': () => '%',
};
// Replace tokens in the format string
let result = '';
let i = 0;
while (i < format.length) {
if (format[i] === '%') {
const token = format.substring(i, i + 2);
if (tokens[token]) {
result += tokens[token]();
i += 2;
} else {
// If token not found, include '%' and move to next character
result += format[i];
i += 1;
}
} else {
result += format[i];
i += 1;
}
}
return result;
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
let dt = fromUnixTimestamp(1234377543.123456);
print(formatDateTime(dt, "%Y-%m-%d %H:%i:%S"));
print(formatDateTime(dt, "%Y-%m-%d %H:%i:%S", "Europe/Brussels"));
print(formatDateTime(dt, "%Y-%m-%d %H:%i:%S", "America/New_York"));
print(formatDateTime(dt, "%Y%m%dT%H%i%sZ"));
print("-----");
print(concat("%a: ", formatDateTime(dt, "%a")));
print(concat("%b: ", formatDateTime(dt, "%b")));
print(concat("%c: ", formatDateTime(dt, "%c")));
print(concat("%C: ", formatDateTime(dt, "%C")));
print(concat("%d: ", formatDateTime(dt, "%d")));
print(concat("%D: ", formatDateTime(dt, "%D")));
print(concat("%e: ", formatDateTime(dt, "%e")));
print(concat("%F: ", formatDateTime(dt, "%F")));
print(concat("%g: ", formatDateTime(dt, "%g")));
print(concat("%G: ", formatDateTime(dt, "%G")));
print(concat("%h: ", formatDateTime(dt, "%h")));
print(concat("%H: ", formatDateTime(dt, "%H")));
print(concat("%i: ", formatDateTime(dt, "%i")));
print(concat("%I: ", formatDateTime(dt, "%I")));
print(concat("%j: ", formatDateTime(dt, "%j")));
print(concat("%k: ", formatDateTime(dt, "%k")));
print(concat("%l: ", formatDateTime(dt, "%l")));
print(concat("%m: ", formatDateTime(dt, "%m")));
print(concat("%M: ", formatDateTime(dt, "%M")));
print(concat("%n: ", formatDateTime(dt, "%n")));
print(concat("%p: ", formatDateTime(dt, "%p")));
print(concat("%r: ", formatDateTime(dt, "%r")));
print(concat("%R: ", formatDateTime(dt, "%R")));
print(concat("%s: ", formatDateTime(dt, "%s")));
print(concat("%S: ", formatDateTime(dt, "%S")));
print(concat("%t: ", formatDateTime(dt, "%t")));
print(concat("%T: ", formatDateTime(dt, "%T")));
print(concat("%u: ", formatDateTime(dt, "%u")));
print(concat("%V: ", formatDateTime(dt, "%V")));
print(concat("%w: ", formatDateTime(dt, "%w")));
print(concat("%W: ", formatDateTime(dt, "%W")));
print(concat("%y: ", formatDateTime(dt, "%y")));
print(concat("%Y: ", formatDateTime(dt, "%Y")));
print(concat("%z: ", formatDateTime(dt, "%z")));
print(concat("%%: ", formatDateTime(dt, "%%")));
print("-----");
print(formatDateTime(dt, "one banana"));
print(formatDateTime(dt, "%Y no way %m is this %d a %H real %i time %S"));

View File

@ -1,6 +1,6 @@
["_H", 1, 42, 0, 2, "print", 1, 35, 32, "key", 32, "value", 42, 1, 2, "print", 1, 35, 32, "key", 32, "value", 32, ["_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, "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, "kk", 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", 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", 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] 45, 32, "otherKey", 45, 2, "print", 1, 35, 35]

View File

@ -0,0 +1,57 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print({});
print({"key": "value"});
print({"key": "value", "other": "thing"});
print({"key": {"otherKey": "value"}});
let key = "kk";
print({[key]: "value"});
print(__getProperty({"key": "value"}, "key", false));
print(__getProperty({"key": "value"}, "key", false));
print(__getProperty(__getProperty({"key": {"otherKey": "value"}}, "key", false), "otherKey", false));
print(__getProperty(__getProperty({"key": {"otherKey": "value"}}, "key", false), "otherKey", false));

View File

@ -2,7 +2,7 @@
{'key': 'value'} {'key': 'value'}
{'key': 'value', 'other': 'thing'} {'key': 'value', 'other': 'thing'}
{'key': {'otherKey': 'value'}} {'key': {'otherKey': 'value'}}
{3: 'value'} {'kk': 'value'}
value value
value value
value value

View File

@ -0,0 +1,152 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
function __x_Error (message, payload) { return __newHogError('Error', message, payload) }
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
print("start");
try {
print("try");
} catch (__error) { if (true) { let e = __error;
print(concat(e, " was the exception"));
}
}
print("------------------");
print("start");
try {
print("try");
} catch (__error) { if (true) { let e = __error;
print("No var for error, but no error");
}
}
print("------------------");
try {
print("try again");
throw __x_Error();
} catch (__error) { if (true) { let e = __error;
print(concat(e, " was the exception"));
}
}
print("------------------");
try {
print("try again");
throw __x_Error();
} catch (__error) { if (true) { let e = __error;
print("No var for error");
}
}
print("------------------");
function third() {
print("Throwing in third");
throw __x_Error("Threw in third");
}
function second() {
print("second");
third();
}
function first() {
print("first");
second();
}
function base() {
print("base");
try {
first();
} catch (__error) { if (true) { let e = __error;
print(concat("Caught in base: ", e)); throw e;
}
}
}
try {
base();
} catch (__error) { if (true) { let e = __error;
print(concat("Caught in root: ", e));
}
}
print("The end");

View File

@ -0,0 +1,72 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function base64Encode (str) { return Buffer.from(str).toString('base64') }
function base64Decode (str) { return Buffer.from(str, 'base64').toString() }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function execFunction() {
print("execFunction");
}
function execFunctionNested() {
function execFunction() {
print("execFunctionNew");
}
print("execFunctionNested");
execFunction();
}
execFunction();
execFunctionNested();
execFunction();
print("--------");
function secondExecFunction() {
print("secondExecFunction");
}
function secondExecFunctionNested() {
print("secondExecFunctionNested");
secondExecFunction();
}
secondExecFunction();
secondExecFunctionNested();
secondExecFunction();
print("--------");
let decode = __lambda(() => base64Decode);
let sixtyFour = base64Encode;
print(sixtyFour("http://www.google.com"));
print(decode()(sixtyFour("http://www.google.com")));
print(decode()(sixtyFour("http://www.google.com")));

View File

@ -0,0 +1,124 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function empty (value) {
if (typeof value === 'object') {
if (Array.isArray(value)) { return value.length === 0 } else if (value === null) { return true } else if (value instanceof Map) { return value.size === 0 }
return Object.keys(value).length === 0
} else if (typeof value === 'number' || typeof value === 'boolean') { return false }
return !value }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("-- test functions --");
function add(a, b) {
return (a + b);
}
print(add);
function add2(a, b) {
let c = (a + b);
return c;
}
print(add2);
function mult(a, b) {
return (a * b);
}
print(mult);
function noArgs() {
let url = "basdfasdf";
let second = (2 + 3);
return second;
}
print(noArgs);
function empty() {
}
function empty2() {
}
function empty3() {
}
function noReturn() {
let a = 1;
let b = 2;
let c = (a + b);
}
function emptyReturn() {
return null;
}
function emptyReturnBeforeOtherStuff() {
return null;
(2 + 2);
}
function emptyReturnBeforeOtherStuffNoSemicolon() {
return (2 + 2);
}
function ifThenReturn() {
if (false) {
return null;
}
return 4;
}
print(add(3, 4));
print(((add(3, 4) + 100) + add(1, 1)));
print((noArgs() ?? -1));
print((empty() ?? -1));
print((empty2() ?? -1));
print((empty3() ?? -1));
print((noReturn() ?? -1));
print((emptyReturn() ?? -1));
print((emptyReturnBeforeOtherStuff() ?? -1));
print((emptyReturnBeforeOtherStuffNoSemicolon() ?? -1));
print((ifThenReturn() ?? -1));
print(mult(((add(3, 4) + 100) + add(2, 1)), 2));
print(mult(((add2(3, 4) + 100) + add2(2, 1)), 10));
function printArgs(arg1, arg2, arg3, arg4, arg5, arg6, arg7) {
print(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
}
let printArgs2 = __lambda((arg1, arg2, arg3, arg4, arg5, arg6, arg7) => {
print(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
return null;
});
printArgs(1, 2, 3, 4, 5, 6, 7);
printArgs2(1, 2, 3, 4, 5, 6, 7);
printArgs(1, 2, 3, 4, 5, 6);
printArgs2(1, 2, 3, 4, 5, 6);
printArgs(1, 2, 3, 4, 5);
printArgs2(1, 2, 3, 4, 5);
printArgs();
printArgs2();

View File

@ -0,0 +1,69 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("-- test if else --");
{
if (true) {
print(1);
} else {
print(2);
}
if (true) {
print(1);
} else {
print(2);
}
if (false) {
print(1);
} else {
print(2);
}
if (true) {
print(1);
} else {
print(2);
}
let a = true;
if (a) {
let a = 3;
print((a + 2));
} else {
print(2);
}
}

View File

@ -0,0 +1,61 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let props = {};
let email = __getProperty(props, "email", true);
if ((email == "")) {
print("ERROR - Email not found!");
print("3");
}
print("1");
if ((email == "")) {
print("ERROR - Email not found!");
print("3");
} else {
print("else");
}
print("1");

View File

@ -0,0 +1,197 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}
function jsonParse (str) {
function convert(x) {
if (Array.isArray(x)) { return x.map(convert) }
else if (typeof x === 'object' && x !== null) {
if (x.__hogDateTime__) { return __toHogDateTime(x.dt, x.zone)
} else if (x.__hogDate__) { return __toHogDate(x.year, x.month, x.day)
} else if (x.__hogError__) { return __newHogError(x.type, x.message, x.payload) }
const obj = {}; for (const key in x) { obj[key] = convert(x[key]) }; return obj }
return x }
return convert(JSON.parse(str)) }
function isValidJSON (str) { try { JSON.parse(str); return true } catch (e) { return false } }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function JSONLength (obj, ...path) {
try { if (typeof obj === 'string') { obj = JSON.parse(obj) } } catch (e) { return 0 }
if (typeof obj === 'object' && obj !== null) {
const value = __getNestedValue(obj, path, true)
if (Array.isArray(value)) {
return value.length
} else if (value instanceof Map) {
return value.size
} else if (typeof value === 'object' && value !== null) {
return Object.keys(value).length
}
}
return 0 }
function JSONHas (obj, ...path) {
let current = obj
for (const key of path) {
let currentParsed = current
if (typeof current === 'string') { try { currentParsed = JSON.parse(current) } catch (e) { return false } }
if (currentParsed instanceof Map) { if (!currentParsed.has(key)) { return false }; current = currentParsed.get(key) }
else if (typeof currentParsed === 'object' && currentParsed !== null) {
if (typeof key === 'number') {
if (Array.isArray(currentParsed)) {
if (key < 0) { if (key < -currentParsed.length) { return false }; current = currentParsed[currentParsed.length + key] }
else if (key === 0) { return false }
else { if (key > currentParsed.length) { return false }; current = currentParsed[key - 1] }
} else { return false }
} else {
if (!(key in currentParsed)) { return false }
current = currentParsed[key]
}
} else { return false }
}
return true }
function JSONExtractBool (obj, ...path) {
try {
if (typeof obj === 'string') {
obj = JSON.parse(obj)
}
} catch (e) {
return false
}
if (path.length > 0) {
obj = __getNestedValue(obj, path, true)
}
if (typeof obj === 'boolean') {
return obj
}
return false
}
function __getNestedValue(obj, path, allowNull = false) {
let current = obj
for (const key of path) {
if (current == null) {
return null
}
if (current instanceof Map) {
current = current.get(key)
} else if (typeof current === 'object' && current !== null) {
current = current[key]
} else {
return null
}
}
if (current === null && !allowNull) {
return null
}
return current
}
print(jsonParse("[1,2,3]"));
let event = {"event": "$pageview", "properties": {"$browser": "Chrome", "$os": "Windows"}};
let json = jsonStringify(event);
print(jsonParse(json));
print("-- JSONHas --");
print(JSONHas("{\"a\": \"hello\", \"b\": [-100, 200.0, 300]}", "b"));
print(JSONHas("{\"a\": \"hello\", \"b\": [-100, 200.0, 300]}", "b", 4));
print(JSONHas({"a": "hello", "b": [-100, 200.0, 300]}, "b"));
print(JSONHas({"a": "hello", "b": [-100, 200.0, 300]}, "b", 4));
print(JSONHas({"a": "hello", "b": [-100, 200.0, 300]}, "b", -2));
print(JSONHas({"a": "hello", "b": [-100, 200.0, 300]}, "b", -4));
print(JSONHas("[1,2,3]", 0));
print(JSONHas("[1,2,[1,2]]", -1, 1));
print(JSONHas("[1,2,[1,2]]", -1, -3));
print(JSONHas("[1,2,[1,2]]", 1, 1));
print("-- isValidJSON --");
print(isValidJSON("{\"a\": \"hello\", \"b\": [-100, 200.0, 300]}"));
print(isValidJSON("not a json"));
print("-- JSONLength --");
print(JSONLength("{\"a\": \"hello\", \"b\": [-100, 200.0, 300]}", "b"));
print(JSONLength("{\"a\": \"hello\", \"b\": [-100, 200.0, 300]}"));
print(JSONLength({"a": "hello", "b": [-100, 200.0, 300]}, "b"));
print(JSONLength({"a": "hello", "b": [-100, 200.0, 300]}));
print("-- JSONExtractBool --");
print(JSONExtractBool("{\"a\": \"hello\", \"b\": true}", "b"));
print(JSONExtractBool("{\"a\": \"hello\", \"b\": false}", "b"));
print(JSONExtractBool("{\"a\": \"hello\", \"b\": 1}", "b"));
print(JSONExtractBool("{\"a\": \"hello\", \"b\": 0}", "b"));
print(JSONExtractBool("{\"a\": \"hello\", \"b\": \"true\"}", "b"));
print(JSONExtractBool("{\"a\": \"hello\", \"b\": \"false\"}", "b"));
print(JSONExtractBool(true));
print(JSONExtractBool(false));
print(JSONExtractBool(1));
print(JSONExtractBool(0));
print(JSONExtractBool("true"));
print(JSONExtractBool("false"));

View File

@ -0,0 +1,54 @@
function values (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return [...obj] } else if (obj instanceof Map) { return Array.from(obj.values()) } return Object.values(obj) } return [] }
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function keys (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return Array.from(obj.keys()) } else if (obj instanceof Map) { return Array.from(obj.keys()) } return Object.keys(obj) } return [] }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let a = [3, 4, 5];
let b = tuple(3, 4, 5);
let c = {"key": "value", "other": "val"};
print(">> A");
print(keys(a));
print(values(a));
print(">> B");
print(keys(b));
print(values(b));
print(">> C");
print(keys(c));
print(values(c));

View File

@ -0,0 +1,129 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}
function jsonParse (str) {
function convert(x) {
if (Array.isArray(x)) { return x.map(convert) }
else if (typeof x === 'object' && x !== null) {
if (x.__hogDateTime__) { return __toHogDateTime(x.dt, x.zone)
} else if (x.__hogDate__) { return __toHogDate(x.year, x.month, x.day)
} else if (x.__hogError__) { return __newHogError(x.type, x.message, x.payload) }
const obj = {}; for (const key in x) { obj[key] = convert(x[key]) }; return obj }
return x }
return convert(JSON.parse(str)) }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let b = __lambda((x) => (x * 2));
print(b);
print(b(2));
print(b(8));
print("--------");
let func = __lambda((x) => (x * 2));
let arr = [func];
print(func(2));
print(__getProperty(arr, 1, false)(2));
print(__lambda((x) => (x * 2))(2));
print("--------");
let withArg = __lambda((x) => {
print(x);
print("moo");
print("cow");
});
withArg(2);
print("--------");
let noArg = __lambda(() => {
print("moo");
print("cow");
});
noArg();
print("-------- lambdas do not survive json --------");
print(b);
print(jsonStringify(b));
let c = jsonParse(jsonStringify(b));
print(c);

View File

@ -0,0 +1,108 @@
function values (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return [...obj] } else if (obj instanceof Map) { return Array.from(obj.values()) } return Object.values(obj) } return [] }
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function keys (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return Array.from(obj.keys()) } else if (obj instanceof Map) { return Array.from(obj.keys()) } return Object.keys(obj) } return [] }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("-- test while loop --");
{
let i = 0;
while ((i < 3)) {
i = (i + 1)
print(i);
}
print(i);
}
print("-- test for loop --");
{
for (let i = 0; (i < 3); i = (i + 1)) {
print(i);
}
}
print("-- test emptier for loop --");
{
let i = 0;
for (; (i < 3); ) {
print("woo");
i = (i + 1)
}
print("hoo");
}
print("-- for in loop with arrays --");
{
let arr = [1, 2, 3];
for (let i of values(arr)) {
print(i);
}
}
print("-- for in loop with arrays and keys --");
{
let arr = [1, 2, 3];
for (let k of keys(arr)) { let v = arr[k]; {
print(k, v);
} }
}
print("-- for in loop with tuples --");
{
let tup = tuple(1, 2, 3);
for (let i of values(tup)) {
print(i);
}
}
print("-- for in loop with tuples and keys --");
{
let tup = tuple(1, 2, 3);
for (let k of keys(tup)) { let v = tup[k]; {
print(k, v);
} }
}
print("-- for in loop with dicts --");
{
let obj = {"first": "v1", "second": "v2", "third": "v3"};
for (let i of values(obj)) {
print(i);
}
}
print("-- for in loop with dicts and keys --");
{
let obj = {"first": "v1", "second": "v2", "third": "v3"};
for (let k of keys(obj)) { let v = obj[k]; {
print(k, v);
} }
}

View File

@ -0,0 +1,125 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
function mandelbrot(re, im, max_iter) {
let z_re = 0.0;
let z_im = 0.0;
let n = 0;
while (!!((((z_re * z_re) + (z_im * z_im)) <= 4) && (n < max_iter))) {
let temp_re = (((z_re * z_re) - (z_im * z_im)) + re);
let temp_im = (((2 * z_re) * z_im) + im);
z_re = temp_re
z_im = temp_im
n = (n + 1)
}
if ((n == max_iter)) {
return " ";
} else {
return "#";
}
}
function main() {
let width = 80;
let height = 24;
let xmin = -2.0;
let xmax = 1.0;
let ymin = -1.0;
let ymax = 1.0;
let max_iter = 30;
let y = 0;
while ((y < height)) {
let row = "";
let x = 0;
while ((x < width)) {
let re = (((x / width) * (xmax - xmin)) + xmin);
let im = (((y / height) * (ymax - ymin)) + ymin);
let letter = mandelbrot(re, im, max_iter);
row = concat(row, letter)
x = (x + 1)
}
print(row);
y = (y + 1)
}
}
main();

View File

@ -10,20 +10,22 @@
54, 1, 35, 32, "%x%", 32, "baa", 17, 36, 0, 54, 1, 35, 32, "%A%", 32, "baa", 18, 36, 0, 54, 1, 35, 32, "%C%", 32, "baa", 54, 1, 35, 32, "%x%", 32, "baa", 17, 36, 0, 54, 1, 35, 32, "%A%", 32, "baa", 18, 36, 0, 54, 1, 35, 32, "%C%", 32, "baa",
18, 36, 0, 54, 1, 35, 32, "b", 32, "a", 18, 36, 0, 54, 1, 35, 32, "b", 32, "a", 19, 36, 0, 54, 1, 35, 32, "b", 32, "a", 18, 36, 0, 54, 1, 35, 32, "b", 32, "a", 18, 36, 0, 54, 1, 35, 32, "b", 32, "a", 19, 36, 0, 54, 1, 35, 32, "b", 32, "a",
20, 36, 0, 54, 1, 35, 32, "car", 32, "a", 21, 36, 0, 54, 1, 35, 32, "foo", 32, "a", 21, 36, 0, 54, 1, 35, 32, "car", 32, 20, 36, 0, 54, 1, 35, 32, "car", 32, "a", 21, 36, 0, 54, 1, 35, 32, "foo", 32, "a", 21, 36, 0, 54, 1, 35, 32, "car", 32,
"a", 22, 36, 0, 54, 1, 35, 32, "arg", 32, "another", 2, "concat", 2, 36, 0, 54, 1, 35, 33, 1, 31, 2, "concat", 2, 36, 0, "a", 22, 36, 0, 54, 1, 35, 32, "b_x", 32, "bax", 17, 36, 0, 54, 1, 35, 32, "b_x", 32, "baax", 19, 36, 0, 54, 1, 35, 32,
54, 1, 35, 29, 30, 2, "concat", 2, 36, 0, 54, 1, 35, 32, "test", 32, "e.*", 2, "match", 2, 36, 0, 54, 1, 35, 32, "test", "b%x", 32, "baax", 17, 36, 0, 54, 1, 35, 32, "arg", 32, "another", 2, "concat", 2, 36, 0, 54, 1, 35, 33, 1, 31, 2,
32, "^e.*", 2, "match", 2, 36, 0, 54, 1, 35, 32, "test", 32, "x.*", 2, "match", 2, 36, 0, 54, 1, 35, 32, "e.*", 32, "concat", 2, 36, 0, 54, 1, 35, 29, 30, 2, "concat", 2, 36, 0, 54, 1, 35, 32, "test", 32, "e.*", 2, "match", 2, 36, 0,
"test", 23, 36, 0, 54, 1, 35, 32, "e.*", 32, "test", 24, 36, 0, 54, 1, 35, 32, "^e.*", 32, "test", 23, 36, 0, 54, 1, 35, 54, 1, 35, 32, "test", 32, "^e.*", 2, "match", 2, 36, 0, 54, 1, 35, 32, "test", 32, "x.*", 2, "match", 2, 36, 0, 54, 1,
32, "^e.*", 32, "test", 24, 36, 0, 54, 1, 35, 32, "x.*", 32, "test", 23, 36, 0, 54, 1, 35, 32, "x.*", 32, "test", 24, 35, 32, "e.*", 32, "test", 23, 36, 0, 54, 1, 35, 32, "e.*", 32, "test", 24, 36, 0, 54, 1, 35, 32, "^e.*", 32, "test",
36, 0, 54, 1, 35, 32, "EST", 32, "test", 25, 36, 0, 54, 1, 35, 32, "EST", 32, "test", 25, 36, 0, 54, 1, 35, 32, "EST", 23, 36, 0, 54, 1, 35, 32, "^e.*", 32, "test", 24, 36, 0, 54, 1, 35, 32, "x.*", 32, "test", 23, 36, 0, 54, 1, 35, 32,
32, "test", 26, 36, 0, 54, 1, 35, 33, 1, 2, "toString", 1, 36, 0, 54, 1, 35, 34, 1.5, 2, "toString", 1, 36, 0, 54, 1, "x.*", 32, "test", 24, 36, 0, 54, 1, 35, 32, "EST", 32, "test", 25, 36, 0, 54, 1, 35, 32, "EST", 32, "test", 25, 36, 0,
35, 29, 2, "toString", 1, 36, 0, 54, 1, 35, 31, 2, "toString", 1, 36, 0, 54, 1, 35, 32, "string", 2, "toString", 1, 36, 54, 1, 35, 32, "EST", 32, "test", 26, 36, 0, 54, 1, 35, 33, 1, 2, "toString", 1, 36, 0, 54, 1, 35, 34, 1.5, 2,
0, 54, 1, 35, 32, "1", 2, "toInt", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toInt", 1, 36, 0, 54, 1, 35, 32, "1.2", 2, "toString", 1, 36, 0, 54, 1, 35, 29, 2, "toString", 1, 36, 0, 54, 1, 35, 31, 2, "toString", 1, 36, 0, 54, 1, 35, 32,
"toFloat", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toFloat", 1, 36, 0, 54, 1, 35, 32, "asd", 2, "toUUID", 1, 36, 0, 54, 1, "string", 2, "toString", 1, 36, 0, 54, 1, 35, 32, "1", 2, "toInt", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toInt", 1, 36, 0,
35, 31, 33, 1, 11, 36, 0, 54, 1, 35, 31, 33, 1, 12, 36, 0, 54, 1, 35, 33, 1, 32, "1", 11, 36, 0, 54, 1, 35, 32, "1", 33, 54, 1, 35, 32, "1.2", 2, "toFloat", 1, 36, 0, 54, 1, 35, 32, "bla", 2, "toFloat", 1, 36, 0, 54, 1, 35, 32, "asd", 2,
1, 11, 36, 0, 54, 1, 35, 29, 33, 1, 11, 36, 0, 54, 1, 35, 29, 33, 0, 11, 36, 0, 54, 1, 35, 29, 33, 2, 11, 36, 0, 54, 1, "toUUID", 1, 36, 0, 54, 1, 35, 31, 33, 1, 11, 36, 0, 54, 1, 35, 31, 33, 1, 12, 36, 0, 54, 1, 35, 33, 1, 32, "1", 11, 36,
35, 30, 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 11, 36, 0, 54, 1, 35, 32, "2", 33, 1, 11, 36, 0, 54, 1, 35, 32, 0, 54, 1, 35, 32, "1", 33, 1, 11, 36, 0, 54, 1, 35, 29, 33, 1, 11, 36, 0, 54, 1, 35, 29, 33, 0, 11, 36, 0, 54, 1, 35,
"2", 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 15, 36, 0, 54, 1, 35, 32, "2", 33, 1, 16, 36, 0, 54, 1, 35, 32, "2", 29, 33, 2, 11, 36, 0, 54, 1, 35, 30, 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 11, 36, 0, 54, 1, 35, 32, "2", 33, 1,
33, 1, 13, 36, 0, 54, 1, 35, 32, "2", 33, 1, 14, 36, 0, 54, 1, 35, 33, 2, 32, "1", 11, 36, 0, 54, 1, 35, 33, 2, 32, "1", 11, 36, 0, 54, 1, 35, 32, "2", 33, 1, 12, 36, 0, 54, 1, 35, 32, "2", 33, 1, 15, 36, 0, 54, 1, 35, 32, "2", 33, 1, 16,
11, 36, 0, 54, 1, 35, 33, 2, 32, "1", 12, 36, 0, 54, 1, 35, 33, 2, 32, "1", 15, 36, 0, 54, 1, 35, 33, 2, 32, "1", 16, 36, 0, 54, 1, 35, 32, "2", 33, 1, 13, 36, 0, 54, 1, 35, 32, "2", 33, 1, 14, 36, 0, 54, 1, 35, 33, 2, 32, "1", 11, 36, 0,
36, 0, 54, 1, 35, 33, 2, 32, "1", 13, 36, 0, 54, 1, 35, 33, 2, 32, "1", 14, 36, 0, 54, 1, 35, 35] 54, 1, 35, 33, 2, 32, "1", 11, 36, 0, 54, 1, 35, 33, 2, 32, "1", 12, 36, 0, 54, 1, 35, 33, 2, 32, "1", 15, 36, 0, 54, 1,
35, 33, 2, 32, "1", 16, 36, 0, 54, 1, 35, 33, 2, 32, "1", 13, 36, 0, 54, 1, 35, 33, 2, 32, "1", 14, 36, 0, 54, 1, 35,
35]

View File

@ -0,0 +1,224 @@
function toUUID (value) { return __STLToString(value) }
function toString (value) { return __STLToString(value) }
function toInt(value) {
if (__isHogDateTime(value)) { return Math.floor(value.dt); }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = Math.floor((date - epoch) / (1000 * 60 * 60 * 24)); return diffInDays; }
return !isNaN(parseInt(value)) ? parseInt(value) : null; }
function toFloat(value) {
if (__isHogDateTime(value)) { return value.dt; }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = (date - epoch) / (1000 * 60 * 60 * 24); return diffInDays; }
return !isNaN(parseFloat(value)) ? parseFloat(value) : null; }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function match (str, pattern) { return new RegExp(pattern).test(str) }
function like (str, pattern) { return __like(str, pattern, false) }
function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}
function ilike (str, pattern) { return __like(str, pattern, true) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __like(str, pattern, caseInsensitive = false) {
if (caseInsensitive) {
str = str.toLowerCase()
pattern = pattern.toLowerCase()
}
pattern = String(pattern)
.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
.replaceAll('%', '.*')
.replaceAll('_', '.')
return new RegExp(pattern).test(str)
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
function test(val) {
print(jsonStringify(val));
}
print("-- test the most common expressions --");
test((1 + 2));
test((1 - 2));
test((3 * 2));
test((3 / 2));
test((3 % 2));
test(!!(1 && 2));
test(!!(1 || 0));
test(!!(1 && 0));
test(!!(1 || !!(0 && 1) || 2));
test(!!(1 && 0 && 1));
test(!!(!!(1 || 2) && !!(1 || 2)));
test(true);
test((!true));
test(false);
test(null);
test(3.14);
test((1 == 2));
test((1 == 2));
test((1 != 2));
test((1 < 2));
test((1 <= 2));
test((1 > 2));
test((1 >= 2));
test(like("a", "b"));
test(like("baa", "%a%"));
test(like("baa", "%x%"));
test(ilike("baa", "%A%"));
test(ilike("baa", "%C%"));
test(ilike("a", "b"));
test(!like("a", "b"));
test(!ilike("a", "b"));
test(("car".includes("a")));
test(("foo".includes("a")));
test((!"car".includes("a")));
test(like("bax", "b_x"));
test(!like("baax", "b_x"));
test(like("baax", "b%x"));
test(concat("arg", "another"));
test(concat(1, null));
test(concat(true, false));
test(match("test", "e.*"));
test(match("test", "^e.*"));
test(match("test", "x.*"));
test(new RegExp("e.*").test("test"));
test(!(new RegExp("e.*").test("test")));
test(new RegExp("^e.*").test("test"));
test(!(new RegExp("^e.*").test("test")));
test(new RegExp("x.*").test("test"));
test(!(new RegExp("x.*").test("test")));
test(new RegExp("EST", "i").test("test"));
test(new RegExp("EST", "i").test("test"));
test(!(new RegExp("EST", "i").test("test")));
test(toString(1));
test(toString(1.5));
test(toString(true));
test(toString(null));
test(toString("string"));
test(toInt("1"));
test(toInt("bla"));
test(toFloat("1.2"));
test(toFloat("bla"));
test(toUUID("asd"));
test((1 == null));
test((1 != null));
test(("1" == 1));
test((1 == "1"));
test((1 == true));
test((0 == true));
test((2 == true));
test((1 != false));
test((1 == "2"));
test((1 == "2"));
test((1 != "2"));
test((1 < "2"));
test((1 <= "2"));
test((1 > "2"));
test((1 >= "2"));
test(("1" == 2));
test(("1" == 2));
test(("1" != 2));
test(("1" < 2));
test(("1" <= 2));
test(("1" > 2));
test(("1" >= 2));

View File

@ -33,6 +33,9 @@ true
true true
false false
false false
true
true
true
"arganother" "arganother"
"1" "1"
"truefalse" "truefalse"

View File

@ -0,0 +1,158 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}
function jsonParse (str) {
function convert(x) {
if (Array.isArray(x)) { return x.map(convert) }
else if (typeof x === 'object' && x !== null) {
if (x.__hogDateTime__) { return __toHogDateTime(x.dt, x.zone)
} else if (x.__hogDate__) { return __toHogDate(x.year, x.month, x.day)
} else if (x.__hogError__) { return __newHogError(x.type, x.message, x.payload) }
const obj = {}; for (const key in x) { obj[key] = convert(x[key]) }; return obj }
return x }
return convert(JSON.parse(str)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } }
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
let obj = {"key": "value", "key2": "value2"};
let str = "na";
for (let i = 0; (i < 100); i = (i + 1)) {
str = concat(str, "na")
__setProperty(obj, concat("key_", i), {"wasted": concat("memory: ", str, " batman!"), "something": obj});
}
print(obj);
let json = jsonStringify(obj);
print(jsonParse(json));

View File

@ -0,0 +1,156 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}
function jsonParse (str) {
function convert(x) {
if (Array.isArray(x)) { return x.map(convert) }
else if (typeof x === 'object' && x !== null) {
if (x.__hogDateTime__) { return __toHogDateTime(x.dt, x.zone)
} else if (x.__hogDate__) { return __toHogDate(x.year, x.month, x.day)
} else if (x.__hogError__) { return __newHogError(x.type, x.message, x.payload) }
const obj = {}; for (const key in x) { obj[key] = convert(x[key]) }; return obj }
return x }
return convert(JSON.parse(str)) }
function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }
function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }
function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } }
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
let root = {"key": "value", "key2": "value2"};
let leaf = {"key": "value", "key2": "value2"};
for (let i = 0; (i < 30); i = (i + 1)) {
__setProperty(root, concat("key_", i), {"something": leaf});
}
print(root);
print(jsonParse(jsonStringify(root)));

View File

@ -0,0 +1,122 @@
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
{
let r = [1, 2, {"d": tuple(1, 3, 42, 6)}];
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 2, false));
}
{
let r = [1, 2, {"d": tuple(1, 3, 42, 6)}];
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, false));
}
{
let r = [1, 2, {"d": tuple(1, 3, 42, 6)}];
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 4, false));
}
{
let r = {"d": tuple(1, 3, 42, 6)};
print(__getProperty(__getProperty(r, "d", true), 2, false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
__setProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, 3);
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
__setProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, 3);
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
__setProperty(__getProperty(r, 3, false), "c", [666]);
print(__getProperty(r, 3, false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
__setProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, 3);
print(__getProperty(__getProperty(r, 3, false), "d", false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
__setProperty(__getProperty(r, 3, false), "d", ["a", "b", "c", "d"]);
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, false));
}
{
let r = [1, 2, {"d": [1, 3, 42, 3]}];
let g = "d";
__setProperty(__getProperty(r, 3, false), g, ["a", "b", "c", "d"]);
print(__getProperty(__getProperty(__getProperty(r, 3, false), "d", false), 3, false));
}
{
let event = {"event": "$pageview", "properties": {"$browser": "Chrome", "$os": "Windows"}};
__setProperty(__getProperty(event, "properties", false), "$browser", "Firefox");
print(event);
}
{
let event = {"event": "$pageview", "properties": {"$browser": "Chrome", "$os": "Windows"}};
__setProperty(__getProperty(event, "properties", true), "$browser", "Firefox")
print(event);
}
{
let event = {"event": "$pageview", "properties": {"$browser": "Chrome", "$os": "Windows"}};
let config = {};
print(event);
}

View File

@ -0,0 +1,56 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let fibonacci = __lambda((number) => {
if ((number < 2)) {
return number;
} else {
return (fibonacci((number - 1)) + fibonacci((number - 2)));
}
});
print(fibonacci(6));
function hogonacci(number) {
if ((number < 2)) {
return number;
} else {
return (hogonacci((number - 1)) + hogonacci((number - 2)));
}
}
print(hogonacci(6));

View File

@ -0,0 +1,122 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
let dbl = __lambda((x) => (x * 2));
print(dbl);
print(dbl(2));
print(dbl(8));
print("--------");
let __x_var = 5;
let varify = __lambda((x) => (x * __x_var));
print(varify(2));
__x_var = 10
print(varify(2));
print(varify(8));
print("--------");
function bigVar() {
let __x_var = 5;
let varify = __lambda((x) => (x * __x_var));
return varify;
}
let bigVarify = bigVar();
print(bigVarify(2));
print(bigVarify(8));
print("--------");
let a = 3;
function outerA() {
print(a);
a = 4
print(a);
}
function innerA() {
print(a);
outerA();
print(a);
}
print(a);
innerA();
print(a);
print("--------");
let b = {"key": 3};
function outerB() {
print(b);
__setProperty(b, "key", 4)
print(b);
}
function innerB() {
print(b);
outerB();
print(b);
}
print(b);
innerB();
print(b);
print("--------");
function outerC() {
let x = "outside";
function innerC() {
print(x);
}
innerC();
}
outerC();
print("--------");
function myFunctionOuter() {
let b = 3;
function myFunction(a) {
return (a + b);
}
print(myFunction(2));
print(myFunction(3));
}
myFunctionOuter();
print("--------");

View File

@ -0,0 +1,121 @@
function upper (value) { return value.toUpperCase() }
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function reverse (value) { return value.split('').reverse().join('') }
function replaceOne (str, searchValue, replaceValue) { return str.replace(searchValue, replaceValue) }
function replaceAll (str, searchValue, replaceValue) { return str.replaceAll(searchValue, replaceValue) }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function notEmpty (value) { return !empty(value) }
function lower (value) { return value.toLowerCase() }
function length (value) { return value.length }
function generateUUIDv4 () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16) })}
function encodeURLComponent (str) { return encodeURIComponent(str) }
function empty (value) {
if (typeof value === 'object') {
if (Array.isArray(value)) { return value.length === 0 } else if (value === null) { return true } else if (value instanceof Map) { return value.size === 0 }
return Object.keys(value).length === 0
} else if (typeof value === 'number' || typeof value === 'boolean') { return false }
return !value }
function decodeURLComponent (str) { return decodeURIComponent(str) }
function base64Encode (str) { return Buffer.from(str).toString('base64') }
function base64Decode (str) { return Buffer.from(str, 'base64').toString() }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("-- empty, notEmpty, length, lower, upper, reverse --");
if (!!(empty("") && notEmpty("234"))) {
print(length("123"));
}
if ((lower("Tdd4gh") == "tdd4gh")) {
print(upper("test"));
}
print(reverse("spinner"));
print("");
print("-- encodeURLComponent, decodeURLComponent --");
print(encodeURLComponent("http://www.google.com"));
print(encodeURLComponent("tom & jerry"));
print(decodeURLComponent(encodeURLComponent("http://www.google.com")));
print(decodeURLComponent(encodeURLComponent("tom & jerry")));
print("");
print("-- base64Encode, base64Decode --");
print(base64Encode("http://www.google.com"));
print(base64Encode("tom & jerry"));
print(base64Decode(base64Encode("http://www.google.com")));
print(base64Decode(base64Encode("tom & jerry")));
print("");
print("-- empty --");
print(empty(null));
print(empty(0));
print(empty(1));
print(empty(-1));
print(empty(0.0));
print(empty(0.01));
print(empty(""));
print(empty("string"));
print(empty("0"));
print(empty([]));
print(empty({}));
print(empty(tuple()));
print(empty(tuple(0)));
print(empty(tuple(1, 2)));
print(empty(true));
print(empty(false));
print("");
print("-- notEmpty --");
print(notEmpty(null));
print(notEmpty(0));
print(notEmpty(1));
print(notEmpty(-1));
print(notEmpty(0.0));
print(notEmpty(0.01));
print(notEmpty(""));
print(notEmpty("string"));
print(notEmpty("0"));
print(notEmpty([]));
print(notEmpty({}));
print(notEmpty(tuple()));
print(notEmpty(tuple(0)));
print(notEmpty(tuple(1, 2)));
print(notEmpty(true));
print(notEmpty(false));
print("");
print("-- replaceAll, replaceOne --");
print(replaceAll("hello world", "l", "L"));
print(replaceOne("hello world", "l", "L"));
print("");
print("-- generateUUIDv4 --");
print(length(generateUUIDv4()));

View File

@ -0,0 +1,129 @@
function trimRight (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let end = str.length
while (str[end - 1] === char) {
end--
}
return str.slice(0, end)
}
function trimLeft (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let start = 0
while (str[start] === char) {
start++
}
return str.slice(start)
}
function trim (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let start = 0
while (str[start] === char) {
start++
}
let end = str.length
while (str[end - 1] === char) {
end--
}
if (start >= end) {
return ''
}
return str.slice(start, end)
}
function splitByString (separator, str, maxSplits) { if (maxSplits === undefined || maxSplits === null) { return str.split(separator) } return str.split(separator, maxSplits) }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function positionCaseInsensitive (str, elem) { if (typeof str === 'string') { return str.toLowerCase().indexOf(String(elem).toLowerCase()) + 1 } else { return 0 } }
function position (str, elem) { if (typeof str === 'string') { return str.indexOf(String(elem)) + 1 } else { return 0 } }
function notLike (str, pattern) { return !__like(str, pattern, false) }
function notILike (str, pattern) { return !__like(str, pattern, true) }
function like (str, pattern) { return __like(str, pattern, false) }
function ilike (str, pattern) { return __like(str, pattern, true) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __like(str, pattern, caseInsensitive = false) {
if (caseInsensitive) {
str = str.toLowerCase()
pattern = pattern.toLowerCase()
}
pattern = String(pattern)
.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
.replaceAll('%', '.*')
.replaceAll('_', '.')
return new RegExp(pattern).test(str)
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print(trim(" hello world "));
print(trimLeft(" hello world "));
print(trimRight(" hello world "));
print(trim("xxxx hello world xx", "x"));
print(trimLeft("xxxx hello world xx", "x"));
print(trimRight("xxxx hello world xx", "x"));
print(splitByString(" ", "hello world and more"));
print(splitByString(" ", "hello world and more", 1));
print(splitByString(" ", "hello world and more", 2));
print(splitByString(" ", "hello world and more", 10));
print(like("banana", "N"));
print(like("banana", "n"));
print(like("banana", "naan"));
print(ilike("banana", "N"));
print(ilike("banana", "n"));
print(ilike("banana", "naan"));
print(notLike("banana", "N"));
print(notILike("banana", "NO"));
print(position("abc", "a"));
print(position("abc", "b"));
print(position("abc", "c"));
print(position("abc", "d"));
print(positionCaseInsensitive("AbC", "a"));
print(positionCaseInsensitive("AbC", "b"));
print(positionCaseInsensitive("AbC", "c"));
print(positionCaseInsensitive("AbC", "d"));

View File

@ -0,0 +1,67 @@
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print(tuple());
print(tuple(1));
print(tuple(1, 2));
print(tuple(1, 2));
print(tuple(1, 2, 3));
print(tuple(1, "2", 3));
print(tuple(1, tuple(2, 3), 4));
print(tuple(1, tuple(2, tuple(3, 4)), 5));
let a = tuple(1, 2, 3);
print(__getProperty(a, 2, false));
print(__getProperty(a, 2, true));
print(__getProperty(a, 8, true));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 2, false), 2, false), 2, false));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 2, true), 2, true), 2, true));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 2, true), 2, true), 2, true));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 4, true), 7, true), 2, true));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 4, true), 7, true), 2, true));
print(__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 2, false), 2, false), 2, false));
print((__getProperty(__getProperty(__getProperty(tuple(1, tuple(2, tuple(3, 4)), 5), 2, false), 2, false), 2, false) + 1));

View File

@ -0,0 +1,88 @@
function __x_typeof (value) {
if (value === null || value === undefined) { return 'null'
} else if (__isHogDateTime(value)) { return 'datetime'
} else if (__isHogDate(value)) { return 'date'
} else if (__isHogError(value)) { return 'error'
} else if (typeof value === 'function') { return 'function'
} else if (Array.isArray(value)) { if (value.__isHogTuple) { return 'tuple' } return 'array'
} else if (typeof value === 'object') { return 'object'
} else if (typeof value === 'number') { return Number.isInteger(value) ? 'integer' : 'float'
} else if (typeof value === 'string') { return 'string'
} else if (typeof value === 'boolean') { return 'boolean' }
return 'unknown'
}
function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }
function toDateTime (input, zone) { return __toDateTime(input, zone) }
function toDate (input) { return __toDate(input) }
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __toDateTime(input, zone) { let dt;
if (typeof input === 'number') { dt = input; }
else { const date = new Date(input); if (isNaN(date.getTime())) { throw new Error('Invalid date input'); } dt = date.getTime() / 1000; }
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' }; }
function __toDate(input) { let date;
if (typeof input === 'number') { date = new Date(input * 1000); } else { date = new Date(input); }
if (isNaN(date.getTime())) { throw new Error('Invalid date input'); }
return { __hogDate__: true, year: date.getUTCFullYear(), month: date.getUTCMonth() + 1, day: date.getUTCDate() }; }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function __x_Error (message, payload) { return __newHogError('Error', message, payload) }
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
function test(obj) {
print(__x_typeof(obj));
}
test("hello world");
test(123);
test(1.23);
test(true);
test(false);
test(null);
test({});
test([]);
test(tuple(1, 2, 3));
test(__lambda(() => (1 + 2)));
test(toDateTime("2021-01-01T00:00:00Z"));
test(toDate("2021-01-01"));
test(__x_Error("BigError", "message"));

View File

@ -0,0 +1,57 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __lambda (fn) { return fn }
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
function returnCallable(a) {
return __lambda((x) => (x * a));
}
let double = returnCallable(2);
let triple = returnCallable(3);
print(double(2));
print(triple(2));
print("----------");
function outer() {
let x = "outside";
function inner() {
print(x);
}
return inner;
}
let closure = outer();
closure();

View File

@ -0,0 +1,53 @@
function print (...args) { console.log(...args.map(__printHogStringOutput)) }
function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) }
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
function __isHogError(obj) {return obj && obj.__hogError__ === true}
function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }
function __isHogDate(obj) { return obj && obj.__hogDate__ === true }
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', "'": "\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\b': '\\b', '\f': '\\f', '\r': '\\r', '\n': '\\n', '\t': '\\t', '\0': '\\0', '\v': '\\v', '\\': '\\\\', '`': '\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\``;
}
print("-- test variables --");
{
let a = (1 + 2);
print(a);
let b = (a + 4);
print(b);
}
print("-- test variable reassignment --");
{
let a = 1;
a = (a + 3)
a = (a * 2)
print(a);
}

View File

@ -76,3 +76,8 @@ print('------')
let c := [1,2,3] let c := [1,2,3]
print(c[1], c[2], c[3], c[4]) print(c[1], c[2], c[3], c[4])
print(c[-1], c[-2], c[-3], c[-4]) print(c[-1], c[-2], c[-3], c[-4])
print('------')
print('a' in ['a', 'b', 'c'])
print('d' in ['a', 'b', 'c'])
print('a' in [])

View File

@ -19,7 +19,7 @@ try {
} catch (e: FishError) { } catch (e: FishError) {
print(f'FishError: {e.message}') print(f'FishError: {e.message}')
} catch (e: Error) { } catch (e: Error) {
print(f'Error of type {e.name}: {e.message}') print(f'Error of type {e.type}: {e.message}')
} }
try { try {
@ -29,7 +29,7 @@ try {
print(f'Problem with your food: {e.message}') print(f'Problem with your food: {e.message}')
} }
} catch (e: Error) { } catch (e: Error) {
print(f'Error of type {e.name}: {e.message}') print(f'Error of type {e.type}: {e.message}')
} catch (e: FishError) { } catch (e: FishError) {
print(f'FishError: {e.message}') print(f'FishError: {e.message}')
} }

View File

@ -3,7 +3,10 @@ print({'key': 'value'})
print({'key': 'value', 'other': 'thing'}) print({'key': 'value', 'other': 'thing'})
print({'key': {'otherKey': 'value'}}) print({'key': {'otherKey': 'value'}})
let key := 3 // We support non-string keys... in the HogVM.
// Keys are always converted to a string in the transpiled JS version.
// TODO: this might be worth revisiting
let key := 'kk'
print({key: 'value'}) print({key: 'value'})
print({'key': 'value', }.key) print({'key': 'value', }.key)

View File

@ -37,6 +37,9 @@ test('a' not ilike 'b') // true
test('a' in 'car') // true test('a' in 'car') // true
test('a' in 'foo') // false test('a' in 'foo') // false
test('a' not in 'car') // false test('a' not in 'car') // false
test('bax' like 'b_x')
test('baax' not like 'b_x')
test('baax' like 'b%x')
test(concat('arg', 'another')) // 'arganother' test(concat('arg', 'another')) // 'arganother'
test(concat(1, NULL)) // '1' test(concat(1, NULL)) // '1'
test(concat(true, false)) // 'truefalse' test(concat(true, false)) // 'truefalse'

View File

@ -10,7 +10,7 @@ from hogvm.python.operation import (
HOGQL_BYTECODE_VERSION as VERSION, HOGQL_BYTECODE_VERSION as VERSION,
) )
from hogvm.python.utils import UncaughtHogVMException from hogvm.python.utils import UncaughtHogVMException
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.parser import parse_expr, parse_program from posthog.hogql.parser import parse_expr, parse_program

View File

@ -26,7 +26,7 @@ class UncaughtHogVMException(HogVMException):
def like(string, pattern, flags=0): def like(string, pattern, flags=0):
pattern = re.escape(pattern).replace("%", ".*") pattern = re.escape(pattern).replace("%", ".*").replace("_", ".")
re_pattern = re.compile(pattern, flags) re_pattern = re.compile(pattern, flags)
return re_pattern.search(string) is not None return re_pattern.search(string) is not None

View File

@ -5,7 +5,7 @@ import glob
import json import json
from posthog.hogql import ast from posthog.hogql import ast
from posthog.hogql.bytecode import create_bytecode, parse_program from posthog.hogql.compiler.bytecode import create_bytecode, parse_program
source = "hogvm/stl/src/*.hog" source = "hogvm/stl/src/*.hog"
target_ts = "hogvm/typescript/src/stl/bytecode.ts" target_ts = "hogvm/typescript/src/stl/bytecode.ts"

View File

@ -1,32 +1,119 @@
#!/bin/bash #!/bin/bash
set -e set -e
# List of test files to skip the compiledjs tests
SKIP_COMPILEDJS_FILES=("crypto.hog")
# Navigate to the script's directory
cd "$(dirname "$0")"
# Build the project
cd typescript cd typescript
pnpm run build pnpm run build
cd .. cd ..
# Navigate to the project root (parent directory of 'hogvm')
cd .. cd ..
rm -f hogvm/__tests__/__snapshots__/*.stdout.nodejs # Function to compute the basename for a given file
rm -f hogvm/__tests__/__snapshots__/*.stdout.python get_basename() {
local file="$1"
local base="${file%.hog}"
base="${base##*/}"
echo "hogvm/__tests__/__snapshots__/$base"
}
for file in hogvm/__tests__/*.hog; do # Function to check if a value is in an array
is_in_array() {
local val="$1"
shift
local arr=("$@")
for item in "${arr[@]}"; do
if [ "$item" == "$val" ]; then
return 0
fi
done
return 1
}
# Check if an argument is provided
if [ "$#" -eq 1 ]; then
test_file="$1"
# Adjust the test file path if it doesn't start with 'hogvm/'
if [[ ! "$test_file" == hogvm/* ]]; then
test_file="hogvm/__tests__/$test_file"
fi
# Check if the test file exists
if [ ! -f "$test_file" ]; then
echo "Test file $test_file does not exist."
exit 1
fi
test_files=("$test_file")
# Remove previous outputs for this test file only
basename=$(get_basename "$test_file")
rm -f "$basename.stdout.nodejs" "$basename.stdout.python" "$basename.stdout.compiledjs"
else
shopt -s nullglob
test_files=(hogvm/__tests__/*.hog)
shopt -u nullglob
if [ ${#test_files[@]} -eq 0 ]; then
echo "No test files found in hogvm/__tests__/"
exit 1
fi
# Remove all previous outputs
rm -f hogvm/__tests__/__snapshots__/*.stdout.nodejs
rm -f hogvm/__tests__/__snapshots__/*.stdout.python
rm -f hogvm/__tests__/__snapshots__/*.stdout.compiledjs
fi
for file in "${test_files[@]}"; do
echo "Testing $file" echo "Testing $file"
# from hogvm/__tests__/*.hog get hogvm/__tests__/__snapshots__/* basename=$(get_basename "$file")
basename="${file%.hog}" filename=$(basename "$file")
basename="${basename##*/}"
basename="hogvm/__tests__/__snapshots__/$basename"
./bin/hoge $file $basename.hoge ./bin/hoge "$file" "$basename.hoge"
./bin/hog --nodejs $basename.hoge > $basename.stdout.nodejs ./bin/hog --nodejs "$basename.hoge" > "$basename.stdout.nodejs"
./bin/hog --python $basename.hoge > $basename.stdout.python ./bin/hog --python "$basename.hoge" > "$basename.stdout.python"
set +e
diff $basename.stdout.nodejs $basename.stdout.python # Check if the current file should skip the compiledjs tests
if [ $? -eq 0 ]; then if is_in_array "$filename" "${SKIP_COMPILEDJS_FILES[@]}"; then
mv $basename.stdout.nodejs $basename.stdout # Skip compiledjs steps for this file
rm $basename.stdout.python echo "Skipping compiledjs tests for $filename"
set +e
diff "$basename.stdout.nodejs" "$basename.stdout.python"
if [ $? -eq 0 ]; then
mv "$basename.stdout.nodejs" "$basename.stdout"
rm "$basename.stdout.python"
echo "Test passed"
else
echo "Test failed: Output differs between Node.js and Python interpreters."
fi
set -e
else else
echo "Test failed" # Proceed with compiledjs tests
set +e
./bin/hoge "$file" "$basename.js"
node "$basename.js" > "$basename.stdout.compiledjs" 2>&1
set -e
set +e
diff "$basename.stdout.nodejs" "$basename.stdout.compiledjs"
if [ $? -eq 0 ]; then
diff "$basename.stdout.nodejs" "$basename.stdout.python"
if [ $? -eq 0 ]; then
mv "$basename.stdout.nodejs" "$basename.stdout"
rm "$basename.stdout.python"
rm "$basename.stdout.compiledjs"
echo "Test passed"
else
echo "Test failed: Output differs between Node.js and Python interpreters."
fi
else
echo "Test failed: Output differs between Node.js interpreter and compiled JavaScript."
fi
set -e
fi fi
set -e
done done

View File

@ -1,6 +1,6 @@
{ {
"name": "@posthog/hogvm", "name": "@posthog/hogvm",
"version": "1.0.58", "version": "1.0.59",
"description": "PostHog Hog Virtual Machine", "description": "PostHog Hog Virtual Machine",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"source": "src/index.ts", "source": "src/index.ts",

View File

@ -19,6 +19,8 @@ import {
} from './date' } from './date'
import { printHogStringOutput } from './print' import { printHogStringOutput } from './print'
// TODO: this file should be generated from or mergred with posthog/hogql/compiler/javascript_stl.py
function STLToString(args: any[]): string { function STLToString(args: any[]): string {
if (isHogDate(args[0])) { if (isHogDate(args[0])) {
const month = args[0].month const month = args[0].month
@ -71,9 +73,7 @@ export const STL: Record<string, STLFunction> = {
}, },
toString: { fn: STLToString, minArgs: 1, maxArgs: 1 }, toString: { fn: STLToString, minArgs: 1, maxArgs: 1 },
toUUID: { toUUID: {
fn: (args) => { fn: STLToString,
return String(args[0])
},
minArgs: 1, minArgs: 1,
maxArgs: 1, maxArgs: 1,
}, },
@ -148,8 +148,8 @@ export const STL: Record<string, STLFunction> = {
}, },
tuple: { tuple: {
fn: (args) => { fn: (args) => {
const tuple = args.slice() const tuple = args.slice();
;(tuple as any).__isHogTuple = true (tuple as any).__isHogTuple = true
return tuple return tuple
}, },
minArgs: 0, minArgs: 0,

View File

@ -36,6 +36,7 @@ export function like(
pattern = String(pattern) pattern = String(pattern)
.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') .replaceAll(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
.replaceAll('%', '.*') .replaceAll('%', '.*')
.replaceAll('_', '.')
if (match) { if (match) {
return match((caseInsensitive ? '(?i)' : '') + pattern, string) return match((caseInsensitive ? '(?i)' : '') + pattern, string)
} }

View File

@ -76,7 +76,7 @@
"@medv/finder": "^3.1.0", "@medv/finder": "^3.1.0",
"@microlink/react-json-view": "^1.21.3", "@microlink/react-json-view": "^1.21.3",
"@monaco-editor/react": "4.6.0", "@monaco-editor/react": "4.6.0",
"@posthog/hogvm": "^1.0.58", "@posthog/hogvm": "^1.0.59",
"@posthog/icons": "0.9.1", "@posthog/icons": "0.9.1",
"@posthog/plugin-scaffold": "^1.4.4", "@posthog/plugin-scaffold": "^1.4.4",
"@react-hook/size": "^2.1.2", "@react-hook/size": "^2.1.2",

View File

@ -13,7 +13,7 @@
"start:dev": "NODE_ENV=dev BASE_DIR=.. nodemon --watch src/ --exec node -r @swc-node/register src/index.ts", "start:dev": "NODE_ENV=dev BASE_DIR=.. nodemon --watch src/ --exec node -r @swc-node/register src/index.ts",
"start:devNoWatch": "NODE_ENV=dev BASE_DIR=.. node -r @swc-node/register src/index.ts", "start:devNoWatch": "NODE_ENV=dev BASE_DIR=.. node -r @swc-node/register src/index.ts",
"build": "pnpm clean && pnpm compile", "build": "pnpm clean && pnpm compile",
"clean": "rm -rf dist/*", "clean": "rm -rf dist/* && rm -rf ../rust/cyclotron-node/index.node",
"typescript:compile": "tsc -b", "typescript:compile": "tsc -b",
"typescript:check": "tsc --noEmit -p .", "typescript:check": "tsc --noEmit -p .",
"compile": "pnpm typescript:compile", "compile": "pnpm typescript:compile",
@ -54,7 +54,7 @@
"@maxmind/geoip2-node": "^3.4.0", "@maxmind/geoip2-node": "^3.4.0",
"@posthog/clickhouse": "^1.7.0", "@posthog/clickhouse": "^1.7.0",
"@posthog/cyclotron": "file:../rust/cyclotron-node", "@posthog/cyclotron": "file:../rust/cyclotron-node",
"@posthog/hogvm": "^1.0.58", "@posthog/hogvm": "^1.0.59",
"@posthog/plugin-scaffold": "1.4.4", "@posthog/plugin-scaffold": "1.4.4",
"@sentry/node": "^7.49.0", "@sentry/node": "^7.49.0",
"@sentry/profiling-node": "^0.3.0", "@sentry/profiling-node": "^0.3.0",

View File

@ -47,8 +47,8 @@ dependencies:
specifier: file:../rust/cyclotron-node specifier: file:../rust/cyclotron-node
version: file:../rust/cyclotron-node version: file:../rust/cyclotron-node
'@posthog/hogvm': '@posthog/hogvm':
specifier: ^1.0.58 specifier: ^1.0.59
version: 1.0.58(luxon@3.4.4) version: 1.0.59(luxon@3.4.4)
'@posthog/plugin-scaffold': '@posthog/plugin-scaffold':
specifier: 1.4.4 specifier: 1.4.4
version: 1.4.4 version: 1.4.4
@ -3119,8 +3119,8 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: false dev: false
/@posthog/hogvm@1.0.58(luxon@3.4.4): /@posthog/hogvm@1.0.59(luxon@3.4.4):
resolution: {integrity: sha512-n7NlJWth9WymJWd3w2YOKfq+soxAcycdfjNIVxxniL1bmEL+aI+Nff+MCPKrsv7YLj9qAnyLWBVAw9SZMksB1Q==} resolution: {integrity: sha512-4KJfCXUhK7x5Wm3pheKWDmrbQ0y1lWlLWdVEjocdjSy3wOS8hQQqaFAVEKZs7hfk9pZqvNFh2UPgD4ccpwUQjA==}
peerDependencies: peerDependencies:
luxon: ^3.4.4 luxon: ^3.4.4
dependencies: dependencies:

View File

@ -50,8 +50,8 @@ dependencies:
specifier: 4.6.0 specifier: 4.6.0
version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.2.0)(react@18.2.0) version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.2.0)(react@18.2.0)
'@posthog/hogvm': '@posthog/hogvm':
specifier: ^1.0.58 specifier: ^1.0.59
version: 1.0.58(luxon@3.5.0) version: 1.0.59(luxon@3.5.0)
'@posthog/icons': '@posthog/icons':
specifier: 0.9.1 specifier: 0.9.1
version: 0.9.1(react-dom@18.2.0)(react@18.2.0) version: 0.9.1(react-dom@18.2.0)(react@18.2.0)
@ -392,7 +392,7 @@ dependencies:
optionalDependencies: optionalDependencies:
fsevents: fsevents:
specifier: ^2.3.2 specifier: ^2.3.2
version: 2.3.3 version: 2.3.2
devDependencies: devDependencies:
'@babel/core': '@babel/core':
@ -5418,8 +5418,8 @@ packages:
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
dev: false dev: false
/@posthog/hogvm@1.0.58(luxon@3.5.0): /@posthog/hogvm@1.0.59(luxon@3.5.0):
resolution: {integrity: sha512-n7NlJWth9WymJWd3w2YOKfq+soxAcycdfjNIVxxniL1bmEL+aI+Nff+MCPKrsv7YLj9qAnyLWBVAw9SZMksB1Q==} resolution: {integrity: sha512-4KJfCXUhK7x5Wm3pheKWDmrbQ0y1lWlLWdVEjocdjSy3wOS8hQQqaFAVEKZs7hfk9pZqvNFh2UPgD4ccpwUQjA==}
peerDependencies: peerDependencies:
luxon: ^3.4.4 luxon: ^3.4.4
dependencies: dependencies:
@ -13142,7 +13142,6 @@ packages:
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin] os: [darwin]
requiresBuild: true requiresBuild: true
dev: true
optional: true optional: true
/fsevents@2.3.3: /fsevents@2.3.3:

View File

@ -6,7 +6,7 @@ from rest_framework.response import Response
from hogql_parser import parse_program from hogql_parser import parse_program
from posthog.api.mixins import PydanticModelMixin from posthog.api.mixins import PydanticModelMixin
from posthog.api.routing import TeamAndOrgViewSetMixin from posthog.api.routing import TeamAndOrgViewSetMixin
from posthog.hogql.bytecode import create_bytecode, Local from posthog.hogql.compiler.bytecode import create_bytecode, Local
from posthog.hogql.errors import ExposedHogQLError from posthog.hogql.errors import ExposedHogQLError
from posthog.schema import HogCompileResponse from posthog.schema import HogCompileResponse

View File

@ -7,7 +7,7 @@ from rest_framework.exceptions import ValidationError
from hogvm.python.debugger import color_bytecode from hogvm.python.debugger import color_bytecode
from posthog.clickhouse.query_tagging import tag_queries from posthog.clickhouse.query_tagging import tag_queries
from posthog.cloud_utils import is_cloud from posthog.cloud_utils import is_cloud
from posthog.hogql.bytecode import execute_hog from posthog.hogql.compiler.bytecode import execute_hog
from posthog.hogql.constants import LimitContext from posthog.hogql.constants import LimitContext
from posthog.hogql.context import HogQLContext from posthog.hogql.context import HogQLContext
from posthog.hogql.database.database import create_hogql_database, serialize_database from posthog.hogql.database.database import create_hogql_database, serialize_database

View File

@ -1,6 +1,6 @@
from typing import Optional from typing import Optional
from posthog.models.action.action import Action from posthog.models.action.action import Action
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.parser import parse_expr from posthog.hogql.parser import parse_expr
from posthog.hogql.property import action_to_expr, property_to_expr, ast from posthog.hogql.property import action_to_expr, property_to_expr, ast
from posthog.models.team.team import Team from posthog.models.team.team import Team

View File

@ -3,7 +3,7 @@ from inline_snapshot import snapshot
from hogvm.python.operation import HOGQL_BYTECODE_VERSION from hogvm.python.operation import HOGQL_BYTECODE_VERSION
from posthog.cdp.filters import hog_function_filters_to_expr from posthog.cdp.filters import hog_function_filters_to_expr
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.models.action.action import Action from posthog.models.action.action import Action
from posthog.test.base import APIBaseTest, ClickhouseTestMixin, QueryMatchingTest from posthog.test.base import APIBaseTest, ClickhouseTestMixin, QueryMatchingTest

View File

@ -2,7 +2,7 @@ import logging
from typing import Any, Optional from typing import Any, Optional
from rest_framework import serializers from rest_framework import serializers
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.parser import parse_program, parse_string_template from posthog.hogql.parser import parse_program, parse_string_template
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -2,7 +2,8 @@ import sys
import json import json
from hogvm.python.execute import execute_bytecode from hogvm.python.execute import execute_bytecode
from .bytecode import create_bytecode, parse_program from posthog.hogql.compiler.bytecode import create_bytecode, parse_program
from posthog.hogql.compiler.javascript import to_js_program
modifiers = [arg for arg in sys.argv if arg.startswith("-")] modifiers = [arg for arg in sys.argv if arg.startswith("-")]
args = [arg for arg in sys.argv if arg != "" and not arg.startswith("-")] args = [arg for arg in sys.argv if arg != "" and not arg.startswith("-")]
@ -14,46 +15,53 @@ if not filename.endswith(".hog") and not filename.endswith(".hoge"):
with open(filename) as file: with open(filename) as file:
code = file.read() code = file.read()
if filename.endswith(".hog"): if "--compile" in modifiers and len(args) == 3 and args[2].endswith(".js"):
bytecode = create_bytecode(parse_program(code)).bytecode target = args[2]
js_program = to_js_program(code)
with open(target, "w") as file:
file.write(js_program + "\n")
else: else:
bytecode = json.loads(code) if filename.endswith(".hog"):
bytecode = create_bytecode(parse_program(code)).bytecode
if "--run" in modifiers:
if len(args) != 2:
raise ValueError("Must specify exactly one filename")
response = execute_bytecode(bytecode, globals=None, timeout=5, team=None, debug="--debug" in modifiers)
for line in response.stdout:
print(line) # noqa: T201
elif "--out" in modifiers:
if len(args) != 2:
raise ValueError("Must specify exactly one filename")
print(json.dumps(bytecode)) # noqa: T201
elif "--compile" in modifiers:
if len(args) == 3:
target = args[2]
else: else:
target = filename[:-4] + ".hoge" bytecode = json.loads(code)
if "--run" in modifiers:
if len(args) != 2: if len(args) != 2:
raise ValueError("Must specify exactly one filename") raise ValueError("Must specify exactly one filename")
# write bytecode to file response = execute_bytecode(bytecode, globals=None, timeout=5, team=None, debug="--debug" in modifiers)
with open(target, "w") as file: for line in response.stdout:
max_length = 120 print(line) # noqa: T201
line = "["
for index, op in enumerate(bytecode):
encoded = json.dumps(op)
if len(line) + len(encoded) > max_length - 2:
file.write(line + "\n")
line = ""
line += (" " if len(line) > 1 else "") + encoded + ("]" if index == len(bytecode) - 1 else ",")
if line == "[":
file.write(line + "]\n")
elif line != "":
file.write(line + "\n")
else: elif "--out" in modifiers:
raise ValueError("Must specify either --run or --compile") if len(args) != 2:
raise ValueError("Must specify exactly one filename")
print(json.dumps(bytecode)) # noqa: T201
elif "--compile" in modifiers:
if len(args) == 3:
target = args[2]
else:
target = filename[:-4] + ".hoge"
if len(args) != 2:
raise ValueError("Must specify exactly one filename")
# write bytecode to file
with open(target, "w") as file:
max_length = 120
line = "["
for index, op in enumerate(bytecode):
encoded = json.dumps(op)
if len(line) + len(encoded) > max_length - 2:
file.write(line + "\n")
line = ""
line += (" " if len(line) > 1 else "") + encoded + ("]" if index == len(bytecode) - 1 else ",")
if line == "[":
file.write(line + "]\n")
elif line != "":
file.write(line + "\n")
else:
raise ValueError("Must specify either --run or --compile")

View File

@ -0,0 +1,572 @@
import dataclasses
import json
import re
from enum import StrEnum
from typing import Any, Optional
from posthog.hogql import ast
from posthog.hogql.base import AST
from posthog.hogql.compiler.javascript_stl import STL_FUNCTIONS, import_stl_functions
from posthog.hogql.errors import QueryError, NotImplementedError
from posthog.hogql.parser import parse_expr, parse_program
from posthog.hogql.visitor import Visitor
_JS_GET_GLOBAL = "__getGlobal"
_JS_KEYWORDS = {
"await",
"break",
"case",
"catch",
"class",
"const",
"continue",
"debugger",
"default",
"delete",
"do",
"else",
"enum",
"export",
"extends",
"false",
"finally",
"for",
"function",
"if",
"import",
"in",
"instanceof",
"new",
"null",
"return",
"super",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"var",
"void",
"while",
"with",
"yield",
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"arguments",
"eval",
"Error",
_JS_GET_GLOBAL, # don't let this get overridden
}
@dataclasses.dataclass
class Local:
name: str
depth: int
def to_js_program(code: str) -> str:
compiler = JavaScriptCompiler()
code = compiler.visit(parse_program(code))
imports = compiler.get_inlined_stl()
return imports + ("\n\n" if imports else "") + code
def to_js_expr(expr: str) -> str:
return JavaScriptCompiler().visit(parse_expr(expr))
def _as_block(node: ast.Statement) -> ast.Block:
if isinstance(node, ast.Block):
return node
return ast.Block(declarations=[node])
def _sanitize_identifier(name: str | int) -> str:
name = str(name)
if re.match(r"^[a-zA-Z_][a-zA-Z0-9_]*$", name):
if name in _JS_KEYWORDS:
return f"__x_{name}"
if name.startswith("__x_"):
# add a second __x_ to avoid conflicts with our internal variables
return f"__x_{name}"
return name
else:
return f"[{json.dumps(name)}]"
class JavaScriptCompiler(Visitor):
def __init__(
self,
args: Optional[list[str]] = None,
locals: Optional[list[Local]] = None,
):
super().__init__()
self.locals: list[Local] = locals or []
self.scope_depth = 0
self.args = args or []
self.indent_level = 0
self.inlined_stl: set[str] = set()
# Initialize locals with function arguments
for arg in self.args:
self._declare_local(arg)
def get_inlined_stl(self) -> str:
return import_stl_functions(self.inlined_stl)
def _start_scope(self):
self.scope_depth += 1
def _end_scope(self):
self.locals = [local for local in self.locals if local.depth < self.scope_depth]
self.scope_depth -= 1
def _declare_local(self, name: str):
for local in reversed(self.locals):
if local.depth == self.scope_depth and local.name == name:
raise QueryError(f"Variable `{name}` already declared in this scope")
self.locals.append(Local(name=name, depth=self.scope_depth))
def _indent(self, code: str) -> str:
indentation = " " * self.indent_level
return "\n".join(indentation + line if line else "" for line in code.split("\n"))
def visit_and(self, node: ast.And):
code = " && ".join([self.visit(expr) for expr in node.exprs])
return f"!!({code})"
def visit_or(self, node: ast.Or):
code = " || ".join([self.visit(expr) for expr in node.exprs])
return f"!!({code})"
def visit_not(self, node: ast.Not):
expr_code = self.visit(node.expr)
return f"(!{expr_code})"
def visit_compare_operation(self, node: ast.CompareOperation):
left_code = self.visit(node.left)
right_code = self.visit(node.right)
op = node.op
op_map = {
ast.CompareOperationOp.Eq: "==",
ast.CompareOperationOp.NotEq: "!=",
ast.CompareOperationOp.Gt: ">",
ast.CompareOperationOp.GtEq: ">=",
ast.CompareOperationOp.Lt: "<",
ast.CompareOperationOp.LtEq: "<=",
}
if op in op_map:
return f"({left_code} {op_map[op]} {right_code})"
elif op == ast.CompareOperationOp.In:
return f"({right_code}.includes({left_code}))"
elif op == ast.CompareOperationOp.NotIn:
return f"(!{right_code}.includes({left_code}))"
elif op == ast.CompareOperationOp.Like:
self.inlined_stl.add("like")
return f"like({left_code}, {right_code})"
elif op == ast.CompareOperationOp.ILike:
self.inlined_stl.add("ilike")
return f"ilike({left_code}, {right_code})"
elif op == ast.CompareOperationOp.NotLike:
self.inlined_stl.add("like")
return f"!like({left_code}, {right_code})"
elif op == ast.CompareOperationOp.NotILike:
self.inlined_stl.add("ilike")
return f"!ilike({left_code}, {right_code})"
elif op == ast.CompareOperationOp.Regex:
# TODO: re2?
return f"new RegExp({right_code}).test({left_code})"
elif op == ast.CompareOperationOp.IRegex:
return f'new RegExp({right_code}, "i").test({left_code})'
elif op == ast.CompareOperationOp.NotRegex:
return f"!(new RegExp({right_code}).test({left_code}))"
elif op == ast.CompareOperationOp.NotIRegex:
return f'!(new RegExp({right_code}, "i").test({left_code}))'
elif op == ast.CompareOperationOp.InCohort or op == ast.CompareOperationOp.NotInCohort:
cohort_name = ""
if isinstance(node.right, ast.Constant):
if isinstance(node.right.value, int):
cohort_name = f" (cohort id={node.right.value})"
else:
cohort_name = f" (cohort: {str(node.right.value)})"
raise QueryError(
f"Can't use cohorts in real-time filters. Please inline the relevant expressions{cohort_name}."
)
else:
raise QueryError(f"Unsupported comparison operator: {op}")
def visit_arithmetic_operation(self, node: ast.ArithmeticOperation):
left_code = self.visit(node.left)
right_code = self.visit(node.right)
op_map = {
ast.ArithmeticOperationOp.Add: "+",
ast.ArithmeticOperationOp.Sub: "-",
ast.ArithmeticOperationOp.Mult: "*",
ast.ArithmeticOperationOp.Div: "/",
ast.ArithmeticOperationOp.Mod: "%",
}
op_str = op_map[node.op]
return f"({left_code} {op_str} {right_code})"
def visit_field(self, node: ast.Field):
found_local = any(local.name == str(node.chain[0]) for local in self.locals)
array_code = ""
for index, element in enumerate(node.chain):
if index == 0:
if found_local:
array_code = _sanitize_identifier(element)
elif element in STL_FUNCTIONS:
self.inlined_stl.add(str(element))
array_code = f"{_sanitize_identifier(element)}"
else:
array_code = f"{_JS_GET_GLOBAL}({json.dumps(element)})"
continue
if (isinstance(element, int) and not isinstance(element, bool)) or isinstance(element, str):
self.inlined_stl.add("__getProperty")
array_code = f"__getProperty({array_code}, {json.dumps(element)}, true)"
else:
raise QueryError(f"Unsupported element: {element} ({type(element)})")
return array_code
def visit_tuple_access(self, node: ast.TupleAccess):
tuple_code = self.visit(node.tuple)
index_code = str(node.index)
self.inlined_stl.add("__getProperty")
return f"__getProperty({tuple_code}, {index_code}, {json.dumps(node.nullish)})"
def visit_array_access(self, node: ast.ArrayAccess):
array_code = self.visit(node.array)
property_code = self.visit(node.property)
self.inlined_stl.add("__getProperty")
return f"__getProperty({array_code}, {property_code}, {json.dumps(node.nullish)})"
def visit_constant(self, node: ast.Constant):
value = node.value
if value is True:
return "true"
elif value is False:
return "false"
elif value is None:
return "null"
elif isinstance(value, int | float | str):
return json.dumps(value)
else:
raise QueryError(f"Unsupported constant type: {type(value)}")
def visit_call(self, node: ast.Call):
if node.params is not None:
return self.visit(ast.ExprCall(expr=ast.Call(name=node.name, args=node.params), args=node.args or []))
# Handle special functions
if node.name == "not" and len(node.args) == 1:
expr_code = self.visit(node.args[0])
return f"(!{expr_code})"
if node.name == "and" and len(node.args) > 1:
exprs_code = " && ".join([self.visit(arg) for arg in node.args])
return f"({exprs_code})"
if node.name == "or" and len(node.args) > 1:
exprs_code = " || ".join([self.visit(arg) for arg in node.args])
return f"({exprs_code})"
if node.name == "if" and len(node.args) >= 2:
condition_code = self.visit(node.args[0])
then_code = self.visit(node.args[1])
else_code = self.visit(node.args[2]) if len(node.args) == 3 else "null"
return f"({condition_code} ? {then_code} : {else_code})"
if node.name == "multiIf" and len(node.args) >= 2:
def build_nested_if(args):
condition_code = self.visit(args[0])
then_code = self.visit(args[1])
if len(args) == 2:
return f"({condition_code} ? {then_code} : null)"
elif len(args) == 3:
else_code = self.visit(args[2])
return f"({condition_code} ? {then_code} : {else_code})"
else:
else_code = build_nested_if(args[2:])
return f"({condition_code} ? {then_code} : {else_code})"
return build_nested_if(node.args)
if node.name == "ifNull" and len(node.args) == 2:
expr_code = self.visit(node.args[0])
if_null_code = self.visit(node.args[1])
return f"({expr_code} ?? {if_null_code})"
if node.name in STL_FUNCTIONS:
self.inlined_stl.add(node.name)
name = _sanitize_identifier(node.name)
args_code = ", ".join(self.visit(arg) for arg in node.args)
return f"{name}({args_code})"
else:
# Regular function calls
name = _sanitize_identifier(node.name)
args_code = ", ".join([self.visit(arg) for arg in node.args or []])
return f"{name}({args_code})"
def visit_expr_call(self, node: ast.ExprCall):
func_code = self.visit(node.expr)
args_code = ", ".join([self.visit(arg) for arg in node.args])
return f"{func_code}({args_code})"
def visit_program(self, node: ast.Program):
code_lines = []
self._start_scope()
for declaration in node.declarations:
code = self.visit(declaration)
code_lines.append(self._indent(code))
self._end_scope()
return "\n".join(code_lines)
def visit_block(self, node: ast.Block):
code_lines = []
self._start_scope()
self.indent_level += 1
for declaration in node.declarations:
code = self.visit(declaration)
code_lines.append(self._indent(code))
self.indent_level -= 1
self._end_scope()
return "{\n" + "\n".join(code_lines) + "\n" + (" " * self.indent_level) + "}"
def visit_expr_statement(self, node: ast.ExprStatement):
if node.expr is None:
return ""
expr_code = self.visit(node.expr)
return expr_code + ";"
def visit_return_statement(self, node: ast.ReturnStatement):
if node.expr:
return f"return {self.visit(node.expr)};"
else:
return "return null;"
def visit_throw_statement(self, node: ast.ThrowStatement):
return f"throw {self.visit(node.expr)};"
def visit_try_catch_statement(self, node: ast.TryCatchStatement):
try_code = self.visit(_as_block(node.try_stmt))
code = "try " + try_code + " catch (__error) { "
has_catch_all = False
for index, catch in enumerate(node.catches):
catch_var = catch[0] or "e"
self._start_scope()
self._declare_local(catch_var)
catch_type = str(catch[1]) if catch[1] is not None else None
catch_declarations = _as_block(catch[2])
catch_code = "".join(self._indent(self.visit(d)) for d in catch_declarations.declarations)
if index > 0:
code += " else "
if catch_type is not None and catch_type != "Error":
code += (
f"if (__error.type === {json.dumps(catch_type)}) {{ let {_sanitize_identifier(catch_var)} = __error;\n"
f"{catch_code}\n"
f"}}\n"
)
else:
has_catch_all = True
code += f"if (true) {{ let {_sanitize_identifier(catch_var)} = __error;\n" f"{catch_code}\n" f"}}\n"
self._end_scope()
if not has_catch_all:
code += " else { throw __error; }"
code += "}"
if node.finally_stmt:
finally_code = self.visit(_as_block(node.finally_stmt))
code += " finally " + finally_code
return code
def visit_if_statement(self, node: ast.IfStatement):
expr_code = self.visit(node.expr)
then_code = self.visit(_as_block(node.then))
code = f"if ({expr_code}) {then_code}"
if node.else_:
else_code = self.visit(_as_block(node.else_))
code += f" else {else_code}"
return code
def visit_while_statement(self, node: ast.WhileStatement):
expr_code = self.visit(node.expr)
body_code = self.visit(_as_block(node.body))
return f"while ({expr_code}) {body_code}"
def visit_for_statement(self, node: ast.ForStatement):
self._start_scope()
init_code = self.visit(node.initializer) if node.initializer else ""
init_code = init_code[:-1] if init_code.endswith(";") else init_code
condition_code = self.visit(node.condition) if node.condition else ""
condition_code = condition_code[:-1] if condition_code.endswith(";") else condition_code
increment_code = self.visit(node.increment) if node.increment else ""
increment_code = increment_code[:-1] if increment_code.endswith(";") else increment_code
body_code = self.visit(_as_block(node.body))
self._end_scope()
return f"for ({init_code}; {condition_code}; {increment_code}) {body_code}"
def visit_for_in_statement(self, node: ast.ForInStatement):
expr_code = self.visit(node.expr)
if node.keyVar and node.valueVar:
self._start_scope()
self._declare_local(node.keyVar)
self._declare_local(node.valueVar)
body_code = self.visit(_as_block(node.body))
self.inlined_stl.add("keys")
resp = f"for (let {_sanitize_identifier(node.keyVar)} of keys({expr_code})) {{ let {_sanitize_identifier(node.valueVar)} = {expr_code}[{_sanitize_identifier(node.keyVar)}]; {body_code} }}"
self._end_scope()
return resp
elif node.valueVar:
self._start_scope()
self._declare_local(node.valueVar)
body_code = self.visit(_as_block(node.body))
self.inlined_stl.add("values")
resp = f"for (let {_sanitize_identifier(node.valueVar)} of values({expr_code})) {body_code}"
self._end_scope()
return resp
else:
raise QueryError("ForInStatement requires at least a valueVar")
def visit_variable_declaration(self, node: ast.VariableDeclaration):
self._declare_local(node.name)
if node.expr:
expr_code = self.visit(node.expr)
return f"let {_sanitize_identifier(node.name)} = {expr_code};"
else:
return f"let {_sanitize_identifier(node.name)};"
def visit_variable_assignment(self, node: ast.VariableAssignment):
if isinstance(node.left, ast.TupleAccess):
tuple_code = self.visit(node.left.tuple)
index = node.left.index
right_code = self.visit(node.right)
self.inlined_stl.add("__setProperty")
return f"__setProperty({tuple_code}, {index}, {right_code});"
elif isinstance(node.left, ast.ArrayAccess):
array_code = self.visit(node.left.array)
property_code = self.visit(node.left.property)
right_code = self.visit(node.right)
self.inlined_stl.add("__setProperty")
return f"__setProperty({array_code}, {property_code}, {right_code});"
elif isinstance(node.left, ast.Field):
chain = node.left.chain
name = chain[0]
is_local = any(local.name == name for local in self.locals)
if is_local:
array_code = ""
for index, element in enumerate(chain):
if index == 0:
array_code = _sanitize_identifier(element)
if len(chain) == 1:
array_code = f"{array_code} = {self.visit(node.right)}"
elif (isinstance(element, int) and not isinstance(element, bool)) or isinstance(element, str):
if index == len(chain) - 1:
right_code = self.visit(node.right)
self.inlined_stl.add("__setProperty")
array_code = f"__setProperty({array_code}, {json.dumps(element)}, {right_code})"
else:
self.inlined_stl.add("__getProperty")
array_code = f"__getProperty({array_code}, {json.dumps(element)}, true)"
else:
raise QueryError(f"Unsupported element: {element} ({type(element)})")
return array_code
else:
# Cannot assign to undeclared variables or globals
raise QueryError(f'Variable "{name}" not declared in this scope. Cannot assign to globals.')
else:
left_code = self.visit(node.left)
right_code = self.visit(node.right)
return f"{left_code} = {right_code};"
def visit_function(self, node: ast.Function):
self._declare_local(_sanitize_identifier(node.name))
params_code = ", ".join(_sanitize_identifier(p) for p in node.params)
self._start_scope()
for arg in node.params:
self._declare_local(arg)
if isinstance(node.body, ast.Placeholder):
body_code = ast.Block(declarations=[ast.ExprStatement(expr=node.body.expr), ast.ReturnStatement(expr=None)])
else:
body_code = self.visit(_as_block(node.body))
self._end_scope()
return f"function {_sanitize_identifier(node.name)}({params_code}) {body_code}"
def visit_lambda(self, node: ast.Lambda):
params_code = ", ".join(_sanitize_identifier(p) for p in node.args)
self._start_scope()
for arg in node.args:
self._declare_local(arg)
if isinstance(node.expr, ast.Placeholder):
expr_code = self.visit(
ast.Block(declarations=[ast.ExprStatement(expr=node.expr.expr), ast.ReturnStatement(expr=None)])
)
else:
expr_code = self.visit(node.expr)
self._end_scope()
self.inlined_stl.add("__lambda")
# we wrap it in __lambda() to make the function anonymous (a true lambda without a name)
return f"__lambda(({params_code}) => {expr_code})"
def visit_dict(self, node: ast.Dict):
items = []
for key, value in node.items:
key_code = self.visit(key)
if not isinstance(key, ast.Constant) or not isinstance(key.value, str):
key_code = f"[{key_code}]"
value_code = self.visit(value)
items.append(f"{key_code}: {value_code}")
items_code = ", ".join(items)
return f"{{{items_code}}}"
def visit_array(self, node: ast.Array):
items_code = ", ".join([self.visit(expr) for expr in node.exprs])
return f"[{items_code}]"
def visit_tuple(self, node: ast.Tuple):
items_code = ", ".join([self.visit(expr) for expr in node.exprs])
self.inlined_stl.add("tuple")
return f"tuple({items_code})"
def visit_hogqlx_tag(self, node: ast.HogQLXTag):
attrs = [f'"__hx_tag": {json.dumps(node.kind)}']
for attr in node.attributes:
attrs.append(f'"{attr.name}": {self._visit_hogqlx_value(attr.value)}')
return f'{{{", ".join(attrs)}}}'
def _visit_hogqlx_value(self, value: Any) -> str:
if isinstance(value, AST):
return self.visit(value)
if isinstance(value, list):
elems = ", ".join([self._visit_hogqlx_value(v) for v in value])
return f"[{elems}]"
if isinstance(value, dict):
items = ", ".join(
[f"{self._visit_hogqlx_value(k)}: {self._visit_hogqlx_value(v)}" for k, v in value.items()]
)
return f"{{{items}}}"
if isinstance(value, StrEnum):
return '"' + str(value.value) + '"'
if value is True:
return "true"
if value is False:
return "false"
if isinstance(value, int | float):
return str(value)
if isinstance(value, str):
return json.dumps(value)
return "null"
def visit_select_query(self, node: ast.SelectQuery):
raise NotImplementedError("JavaScriptCompiler does not support SelectQuery")

View File

@ -0,0 +1,923 @@
# TODO: this should be autogenerated from hogvm/typescript/src/stl/*
STL_FUNCTIONS: dict[str, list[str | list[str]]] = {
"concat": [
"function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') }",
["__STLToString"],
],
"match": [
"function match (str, pattern) { return new RegExp(pattern).test(str) }",
[],
],
"like": [
"function like (str, pattern) { return __like(str, pattern, false) }",
["__like"],
],
"ilike": [
"function ilike (str, pattern) { return __like(str, pattern, true) }",
["__like"],
],
"notLike": [
"function notLike (str, pattern) { return !__like(str, pattern, false) }",
["__like"],
],
"notILike": [
"function notILike (str, pattern) { return !__like(str, pattern, true) }",
["__like"],
],
"toString": [
"function toString (value) { return __STLToString(value) }",
["__STLToString"],
],
"toUUID": [
"function toUUID (value) { return __STLToString(value) }",
["__STLToString"],
],
"toInt": [
"""function toInt(value) {
if (__isHogDateTime(value)) { return Math.floor(value.dt); }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = Math.floor((date - epoch) / (1000 * 60 * 60 * 24)); return diffInDays; }
return !isNaN(parseInt(value)) ? parseInt(value) : null; }""",
["__isHogDateTime", "__isHogDate"],
],
"toFloat": [
"""function toFloat(value) {
if (__isHogDateTime(value)) { return value.dt; }
else if (__isHogDate(value)) { const date = new Date(Date.UTC(value.year, value.month - 1, value.day)); const epoch = new Date(Date.UTC(1970, 0, 1)); const diffInDays = (date - epoch) / (1000 * 60 * 60 * 24); return diffInDays; }
return !isNaN(parseFloat(value)) ? parseFloat(value) : null; }""",
["__isHogDateTime", "__isHogDate"],
],
"ifNull": [
"function ifNull (value, defaultValue) { return value !== null ? value : defaultValue } ",
[],
],
"length": [
"function length (value) { return value.length }",
[],
],
"empty": [
"""function empty (value) {
if (typeof value === 'object') {
if (Array.isArray(value)) { return value.length === 0 } else if (value === null) { return true } else if (value instanceof Map) { return value.size === 0 }
return Object.keys(value).length === 0
} else if (typeof value === 'number' || typeof value === 'boolean') { return false }
return !value }""",
[],
],
"notEmpty": [
"function notEmpty (value) { return !empty(value) }",
["empty"],
],
"tuple": [
"function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; }",
[],
],
"lower": [
"function lower (value) { return value.toLowerCase() }",
[],
],
"upper": [
"function upper (value) { return value.toUpperCase() }",
[],
],
"reverse": [
"function reverse (value) { return value.split('').reverse().join('') }",
[],
],
"print": [
"function print (...args) { console.log(...args.map(__printHogStringOutput)) }",
["__printHogStringOutput"],
],
"jsonParse": [
"""function jsonParse (str) {
function convert(x) {
if (Array.isArray(x)) { return x.map(convert) }
else if (typeof x === 'object' && x !== null) {
if (x.__hogDateTime__) { return __toHogDateTime(x.dt, x.zone)
} else if (x.__hogDate__) { return __toHogDate(x.year, x.month, x.day)
} else if (x.__hogError__) { return __newHogError(x.type, x.message, x.payload) }
const obj = {}; for (const key in x) { obj[key] = convert(x[key]) }; return obj }
return x }
return convert(JSON.parse(str)) }""",
["__toHogDateTime", "__toHogDate", "__newHogError"],
],
"jsonStringify": [
"""function jsonStringify (value, spacing) {
function convert(x, marked) {
if (!marked) { marked = new Set() }
if (typeof x === 'object' && x !== null) {
if (marked.has(x)) { return null }
marked.add(x)
try {
if (x instanceof Map) {
const obj = {}
x.forEach((value, key) => { obj[convert(key, marked)] = convert(value, marked) })
return obj
}
if (Array.isArray(x)) { return x.map((v) => convert(v, marked)) }
if (__isHogDateTime(x) || __isHogDate(x) || __isHogError(x)) { return x }
if (typeof x === 'function') { return `fn<${x.name || 'lambda'}(${x.length})>` }
const obj = {}; for (const key in x) { obj[key] = convert(x[key], marked) }
return obj
} finally {
marked.delete(x)
}
}
return x
}
if (spacing && typeof spacing === 'number' && spacing > 0) {
return JSON.stringify(convert(value), null, spacing)
}
return JSON.stringify(convert(value), (key, val) => typeof val === 'function' ? `fn<${val.name || 'lambda'}(${val.length})>` : val)
}""",
["__isHogDateTime", "__isHogDate", "__isHogError"],
],
"JSONHas": [
"""function JSONHas (obj, ...path) {
let current = obj
for (const key of path) {
let currentParsed = current
if (typeof current === 'string') { try { currentParsed = JSON.parse(current) } catch (e) { return false } }
if (currentParsed instanceof Map) { if (!currentParsed.has(key)) { return false }; current = currentParsed.get(key) }
else if (typeof currentParsed === 'object' && currentParsed !== null) {
if (typeof key === 'number') {
if (Array.isArray(currentParsed)) {
if (key < 0) { if (key < -currentParsed.length) { return false }; current = currentParsed[currentParsed.length + key] }
else if (key === 0) { return false }
else { if (key > currentParsed.length) { return false }; current = currentParsed[key - 1] }
} else { return false }
} else {
if (!(key in currentParsed)) { return false }
current = currentParsed[key]
}
} else { return false }
}
return true }""",
[],
],
"isValidJSON": [
"function isValidJSON (str) { try { JSON.parse(str); return true } catch (e) { return false } }",
[],
],
"JSONLength": [
"""function JSONLength (obj, ...path) {
try { if (typeof obj === 'string') { obj = JSON.parse(obj) } } catch (e) { return 0 }
if (typeof obj === 'object' && obj !== null) {
const value = __getNestedValue(obj, path, true)
if (Array.isArray(value)) {
return value.length
} else if (value instanceof Map) {
return value.size
} else if (typeof value === 'object' && value !== null) {
return Object.keys(value).length
}
}
return 0 }""",
["__getNestedValue"],
],
"JSONExtractBool": [
"""function JSONExtractBool (obj, ...path) {
try {
if (typeof obj === 'string') {
obj = JSON.parse(obj)
}
} catch (e) {
return false
}
if (path.length > 0) {
obj = __getNestedValue(obj, path, true)
}
if (typeof obj === 'boolean') {
return obj
}
return false
}""",
["__getNestedValue"],
],
"base64Encode": [
"function base64Encode (str) { return Buffer.from(str).toString('base64') }",
[],
],
"base64Decode": [
"function base64Decode (str) { return Buffer.from(str, 'base64').toString() } ",
[],
],
"tryBase64Decode": [
"function tryBase64Decode (str) { try { return Buffer.from(str, 'base64').toString() } catch (e) { return '' } }",
[],
],
"encodeURLComponent": [
"function encodeURLComponent (str) { return encodeURIComponent(str) }",
[],
],
"decodeURLComponent": [
"function decodeURLComponent (str) { return decodeURIComponent(str) }",
[],
],
"replaceOne": [
"function replaceOne (str, searchValue, replaceValue) { return str.replace(searchValue, replaceValue) }",
[],
],
"replaceAll": [
"function replaceAll (str, searchValue, replaceValue) { return str.replaceAll(searchValue, replaceValue) }",
[],
],
"position": [
"function position (str, elem) { if (typeof str === 'string') { return str.indexOf(String(elem)) + 1 } else { return 0 } }",
[],
],
"positionCaseInsensitive": [
"function positionCaseInsensitive (str, elem) { if (typeof str === 'string') { return str.toLowerCase().indexOf(String(elem).toLowerCase()) + 1 } else { return 0 } }",
[],
],
"trim": [
"""function trim (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let start = 0
while (str[start] === char) {
start++
}
let end = str.length
while (str[end - 1] === char) {
end--
}
if (start >= end) {
return ''
}
return str.slice(start, end)
}""",
[],
],
"trimLeft": [
"""function trimLeft (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let start = 0
while (str[start] === char) {
start++
}
return str.slice(start)
}""",
[],
],
"trimRight": [
"""function trimRight (str, char) {
if (char === null || char === undefined) {
char = ' '
}
if (char.length !== 1) {
return ''
}
let end = str.length
while (str[end - 1] === char) {
end--
}
return str.slice(0, end)
}""",
[],
],
"splitByString": [
"function splitByString (separator, str, maxSplits) { if (maxSplits === undefined || maxSplits === null) { return str.split(separator) } return str.split(separator, maxSplits) }",
[],
],
"generateUUIDv4": [
"function generateUUIDv4 () { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16) })}",
[],
],
"sha256Hex": [
"function sha256Hex (str, options) { return 'SHA256 is not implemented' }",
[],
],
"md5Hex": [
"""function md5Hex(string) { return 'MD5 is not implemented' }""",
[],
],
"sha256HmacChainHex": [
"function sha256HmacChainHex (data, options) { return 'sha256HmacChainHex not implemented' }",
[],
],
"keys": [
"""function keys (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return Array.from(obj.keys()) } else if (obj instanceof Map) { return Array.from(obj.keys()) } return Object.keys(obj) } return [] }""",
[],
],
"values": [
"""function values (obj) { if (typeof obj === 'object' && obj !== null) { if (Array.isArray(obj)) { return [...obj] } else if (obj instanceof Map) { return Array.from(obj.values()) } return Object.values(obj) } return [] }""",
[],
],
"indexOf": [
"function indexOf (arrOrString, elem) { if (Array.isArray(arrOrString)) { return arrOrString.indexOf(elem) + 1 } else { return 0 } }",
[],
],
"arrayPushBack": [
"function arrayPushBack (arr, item) { if (!Array.isArray(arr)) { return [item] } return [...arr, item] }",
[],
],
"arrayPushFront": [
"function arrayPushFront (arr, item) { if (!Array.isArray(arr)) { return [item] } return [item, ...arr] }",
[],
],
"arrayPopBack": [
"function arrayPopBack (arr) { if (!Array.isArray(arr)) { return [] } return arr.slice(0, arr.length - 1) }",
[],
],
"arrayPopFront": [
"function arrayPopFront (arr) { if (!Array.isArray(arr)) { return [] } return arr.slice(1) }",
[],
],
"arraySort": [
"function arraySort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort() }",
[],
],
"arrayReverse": [
"function arrayReverse (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].reverse() }",
[],
],
"arrayReverseSort": [
"function arrayReverseSort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort().reverse() }",
[],
],
"arrayStringConcat": [
"function arrayStringConcat (arr, separator = '') { if (!Array.isArray(arr)) { return '' } return arr.join(separator) }",
[],
],
"arrayCount": [
"function arrayCount (func, arr) { let count = 0; for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { count = count + 1 } } return count }",
[],
],
"arrayExists": [
"""function arrayExists (func, arr) { for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { return true } } return false }""",
[],
],
"arrayFilter": [
"""function arrayFilter (func, arr) { let result = []; for (let i = 0; i < arr.length; i++) { if (func(arr[i])) { result = arrayPushBack(result, arr[i]) } } return result}""",
["arrayPushBack"],
],
"arrayMap": [
"""function arrayMap (func, arr) { let result = []; for (let i = 0; i < arr.length; i++) { result = arrayPushBack(result, func(arr[i])) } return result }""",
["arrayPushBack"],
],
"has": [
"""function has (arr, elem) { if (!Array.isArray(arr) || arr.length === 0) { return false } return arr.includes(elem) }""",
[],
],
"now": [
"""function now () { return __now() }""",
["__now"],
],
"toUnixTimestamp": [
"""function toUnixTimestamp (input, zone) { return __toUnixTimestamp(input, zone) }""",
["__toUnixTimestamp"],
],
"fromUnixTimestamp": [
"""function fromUnixTimestamp (input) { return __fromUnixTimestamp(input) }""",
["__fromUnixTimestamp"],
],
"toUnixTimestampMilli": [
"""function toUnixTimestampMilli (input, zone) { return __toUnixTimestampMilli(input, zone) }""",
["__toUnixTimestampMilli"],
],
"fromUnixTimestampMilli": [
"""function fromUnixTimestampMilli (input) { return __fromUnixTimestampMilli(input) }""",
["__fromUnixTimestampMilli"],
],
"toTimeZone": [
"""function toTimeZone (input, zone) { return __toTimeZone(input, zone) }""",
["__toTimeZone"],
],
"toDate": [
"""function toDate (input) { return __toDate(input) }""",
["__toDate"],
],
"toDateTime": [
"""function toDateTime (input, zone) { return __toDateTime(input, zone) }""",
["__toDateTime"],
],
"formatDateTime": [
"""function formatDateTime (input, format, zone) { return __formatDateTime(input, format, zone) }""",
["__formatDateTime"],
],
"HogError": [
"""function HogError (type, message, payload) { return __newHogError(type, message, payload) }""",
["__newHogError"],
],
"Error": [
"""function __x_Error (message, payload) { return __newHogError('Error', message, payload) }""",
["__newHogError"],
],
"RetryError": [
"""function RetryError (message, payload) { return __newHogError('RetryError', message, payload) }""",
["__newHogError"],
],
"NotImplementedError": [
"""function NotImplementedError (message, payload) { return __newHogError('NotImplementedError', message, payload) }""",
["__newHogError"],
],
"typeof": [
"""function __x_typeof (value) {
if (value === null || value === undefined) { return 'null'
} else if (__isHogDateTime(value)) { return 'datetime'
} else if (__isHogDate(value)) { return 'date'
} else if (__isHogError(value)) { return 'error'
} else if (typeof value === 'function') { return 'function'
} else if (Array.isArray(value)) { if (value.__isHogTuple) { return 'tuple' } return 'array'
} else if (typeof value === 'object') { return 'object'
} else if (typeof value === 'number') { return Number.isInteger(value) ? 'integer' : 'float'
} else if (typeof value === 'string') { return 'string'
} else if (typeof value === 'boolean') { return 'boolean' }
return 'unknown'
}
""",
["__isHogDateTime", "__isHogDate", "__isHogError"],
],
"__DateTimeToString": [
r"""function __DateTimeToString(dt) {
if (__isHogDateTime(dt)) {
const date = new Date(dt.dt * 1000);
const timeZone = dt.zone || 'UTC';
const milliseconds = Math.floor(dt.dt * 1000 % 1000);
const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false };
const formatter = new Intl.DateTimeFormat('en-US', options);
const parts = formatter.formatToParts(date);
let year, month, day, hour, minute, second;
for (const part of parts) {
switch (part.type) {
case 'year': year = part.value; break;
case 'month': month = part.value; break;
case 'day': day = part.value; break;
case 'hour': hour = part.value; break;
case 'minute': minute = part.value; break;
case 'second': second = part.value; break;
default: break;
}
}
const getOffset = (date, timeZone) => {
const tzDate = new Date(date.toLocaleString('en-US', { timeZone }));
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const offset = (tzDate - utcDate) / 60000; // in minutes
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = Math.floor(absOffset / 60);
const minutes = absOffset % 60;
return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
};
let offset = 'Z';
if (timeZone !== 'UTC') {
offset = getOffset(date, timeZone);
}
let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
isoString += `.${milliseconds.toString().padStart(3, '0')}`;
isoString += offset;
return isoString;
}
}
""",
[],
],
"__STLToString": [
r"""function __STLToString(arg) {
if (arg && __isHogDate(arg)) { return `${arg.year}-${arg.month.toString().padStart(2, '0')}-${arg.day.toString().padStart(2, '0')}`; }
else if (arg && __isHogDateTime(arg)) { return __DateTimeToString(arg); }
return __printHogStringOutput(arg); }""",
["__isHogDate", "__isHogDateTime", "__printHogStringOutput", "__DateTimeToString"],
],
"__isHogDate": [
"""function __isHogDate(obj) { return obj && obj.__hogDate__ === true }""",
[],
],
"__isHogDateTime": [
"""function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true }""",
[],
],
"__toHogDate": [
"""function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } }""",
[],
],
"__toHogDateTime": [
"""function __toHogDateTime(timestamp, zone) {
if (__isHogDate(timestamp)) {
const date = new Date(Date.UTC(timestamp.year, timestamp.month - 1, timestamp.day));
const dt = date.getTime() / 1000;
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' };
}
return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }""",
["__isHogDate"],
],
"__now": [
"""function __now(zone) { return __toHogDateTime(Date.now() / 1000, zone) }""",
["__toHogDateTime"],
],
"__toUnixTimestamp": [
"""function __toUnixTimestamp(input, zone) {
if (__isHogDateTime(input)) { return input.dt; }
if (__isHogDate(input)) { return __toHogDateTime(input).dt; }
const date = new Date(input);
if (isNaN(date.getTime())) { throw new Error('Invalid date input'); }
return Math.floor(date.getTime() / 1000);}""",
["__isHogDateTime", "__isHogDate", "__toHogDateTime"],
],
"__fromUnixTimestamp": [
"""function __fromUnixTimestamp(input) { return __toHogDateTime(input) }""",
["__toHogDateTime"],
],
"__toUnixTimestampMilli": [
"""function __toUnixTimestampMilli(input, zone) { return __toUnixTimestamp(input, zone) * 1000 }""",
["__toUnixTimestamp"],
],
"__fromUnixTimestampMilli": [
"""function __fromUnixTimestampMilli(input) { return __toHogDateTime(input / 1000) }""",
["__toHogDateTime"],
],
"__toTimeZone": [
"""function __toTimeZone(input, zone) { if (!__isHogDateTime(input)) { throw new Error('Expected a DateTime') }; return { ...input, zone }}""",
["__isHogDateTime"],
],
"__toDate": [
"""function __toDate(input) { let date;
if (typeof input === 'number') { date = new Date(input * 1000); } else { date = new Date(input); }
if (isNaN(date.getTime())) { throw new Error('Invalid date input'); }
return { __hogDate__: true, year: date.getUTCFullYear(), month: date.getUTCMonth() + 1, day: date.getUTCDate() }; }""",
[],
],
"__toDateTime": [
"""function __toDateTime(input, zone) { let dt;
if (typeof input === 'number') { dt = input; }
else { const date = new Date(input); if (isNaN(date.getTime())) { throw new Error('Invalid date input'); } dt = date.getTime() / 1000; }
return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' }; }""",
[],
],
"__formatDateTime": [
"""function __formatDateTime(input, format, zone) {
if (!__isHogDateTime(input)) { throw new Error('Expected a DateTime'); }
if (!format) { throw new Error('formatDateTime requires at least 2 arguments'); }
const timestamp = input.dt * 1000;
let date = new Date(timestamp);
if (!zone) { zone = 'UTC'; }
const padZero = (num, len = 2) => String(num).padStart(len, '0');
const padSpace = (num, len = 2) => String(num).padStart(len, ' ');
const getDateComponent = (type, options = {}) => {
const formatter = new Intl.DateTimeFormat('en-US', { ...options, timeZone: zone });
const parts = formatter.formatToParts(date);
const part = parts.find(p => p.type === type);
return part ? part.value : '';
};
const getNumericComponent = (type, options = {}) => {
const value = getDateComponent(type, options);
return parseInt(value, 10);
};
const getWeekNumber = (d) => {
const dateInZone = new Date(d.toLocaleString('en-US', { timeZone: zone }));
const target = new Date(Date.UTC(dateInZone.getFullYear(), dateInZone.getMonth(), dateInZone.getDate()));
const dayNr = (target.getUTCDay() + 6) % 7;
target.setUTCDate(target.getUTCDate() - dayNr + 3);
const firstThursday = new Date(Date.UTC(target.getUTCFullYear(), 0, 4));
const weekNumber = 1 + Math.round(((target - firstThursday) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7);
return weekNumber;
};
const getDayOfYear = (d) => {
const startOfYear = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
const dateInZone = new Date(d.toLocaleString('en-US', { timeZone: zone }));
const diff = dateInZone - startOfYear;
return Math.floor(diff / 86400000) + 1;
};
// Token mapping with corrections
const tokens = {
'%a': () => getDateComponent('weekday', { weekday: 'short' }),
'%b': () => getDateComponent('month', { month: 'short' }),
'%c': () => padZero(getNumericComponent('month', { month: '2-digit' })),
'%C': () => getDateComponent('year', { year: '2-digit' }),
'%d': () => padZero(getNumericComponent('day', { day: '2-digit' })),
'%D': () => {
const month = padZero(getNumericComponent('month', { month: '2-digit' }));
const day = padZero(getNumericComponent('day', { day: '2-digit' }));
const year = getDateComponent('year', { year: '2-digit' });
return `${month}/${day}/${year}`;
},
'%e': () => padSpace(getNumericComponent('day', { day: 'numeric' })),
'%F': () => {
const year = getNumericComponent('year', { year: 'numeric' });
const month = padZero(getNumericComponent('month', { month: '2-digit' }));
const day = padZero(getNumericComponent('day', { day: '2-digit' }));
return `${year}-${month}-${day}`;
},
'%g': () => getDateComponent('year', { year: '2-digit' }),
'%G': () => getNumericComponent('year', { year: 'numeric' }),
'%h': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%H': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false })),
'%i': () => padZero(getNumericComponent('minute', { minute: '2-digit' })),
'%I': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%j': () => padZero(getDayOfYear(date), 3),
'%k': () => padSpace(getNumericComponent('hour', { hour: 'numeric', hour12: false })),
'%l': () => padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true })),
'%m': () => padZero(getNumericComponent('month', { month: '2-digit' })),
'%M': () => getDateComponent('month', { month: 'long' }),
'%n': () => '\\n',
'%p': () => getDateComponent('dayPeriod', { hour: 'numeric', hour12: true }),
'%r': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: true }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
const second = padZero(getNumericComponent('second', { second: '2-digit' }));
const period = getDateComponent('dayPeriod', { hour: 'numeric', hour12: true });
return `${hour}:${minute} ${period}`;
},
'%R': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
return `${hour}:${minute}`;
},
'%s': () => padZero(getNumericComponent('second', { second: '2-digit' })),
'%S': () => padZero(getNumericComponent('second', { second: '2-digit' })),
'%t': () => '\\t',
'%T': () => {
const hour = padZero(getNumericComponent('hour', { hour: '2-digit', hour12: false }));
const minute = padZero(getNumericComponent('minute', { minute: '2-digit' }));
const second = padZero(getNumericComponent('second', { second: '2-digit' }));
return `${hour}:${minute}:${second}`;
},
'%u': () => {
let day = getDateComponent('weekday', { weekday: 'short' });
const dayMap = { 'Mon': '1', 'Tue': '2', 'Wed': '3', 'Thu': '4', 'Fri': '5', 'Sat': '6', 'Sun': '7' };
return dayMap[day];
},
'%V': () => padZero(getWeekNumber(date)),
'%w': () => {
let day = getDateComponent('weekday', { weekday: 'short' });
const dayMap = { 'Sun': '0', 'Mon': '1', 'Tue': '2', 'Wed': '3', 'Thu': '4', 'Fri': '5', 'Sat': '6' };
return dayMap[day];
},
'%W': () => getDateComponent('weekday', { weekday: 'long' }),
'%y': () => getDateComponent('year', { year: '2-digit' }),
'%Y': () => getNumericComponent('year', { year: 'numeric' }),
'%z': () => {
if (zone === 'UTC') {
return '+0000';
} else {
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: zone,
timeZoneName: 'shortOffset',
});
const parts = formatter.formatToParts(date);
const offsetPart = parts.find(part => part.type === 'timeZoneName');
if (offsetPart && offsetPart.value) {
const offsetValue = offsetPart.value;
const match = offsetValue.match(/GMT([+-]\\d{1,2})(?::(\\d{2}))?/);
if (match) {
const sign = match[1][0];
const hours = padZero(Math.abs(parseInt(match[1], 10)));
const minutes = padZero(match[2] ? parseInt(match[2], 10) : 0);
return `${sign}${hours}${minutes}`;
}
}
return '';
}
},
'%%': () => '%',
};
// Replace tokens in the format string
let result = '';
let i = 0;
while (i < format.length) {
if (format[i] === '%') {
const token = format.substring(i, i + 2);
if (tokens[token]) {
result += tokens[token]();
i += 2;
} else {
// If token not found, include '%' and move to next character
result += format[i];
i += 1;
}
} else {
result += format[i];
i += 1;
}
}
return result;
}
""",
["__isHogDateTime"],
],
"__printHogStringOutput": [
"""function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } """,
["__printHogValue"],
],
"__printHogValue": [
"""
function __printHogValue(obj, marked = new Set()) {
if (typeof obj === 'object' && obj !== null && obj !== undefined) {
if (marked.has(obj) && !__isHogDateTime(obj) && !__isHogDate(obj) && !__isHogError(obj)) { return 'null'; }
marked.add(obj);
try {
if (Array.isArray(obj)) {
if (obj.__isHogTuple) { return obj.length < 2 ? `tuple(${obj.map((o) => __printHogValue(o, marked)).join(', ')})` : `(${obj.map((o) => __printHogValue(o, marked)).join(', ')})`; }
return `[${obj.map((o) => __printHogValue(o, marked)).join(', ')}]`;
}
if (__isHogDateTime(obj)) { const millis = String(obj.dt); return `DateTime(${millis}${millis.includes('.') ? '' : '.0'}, ${__escapeString(obj.zone)})`; }
if (__isHogDate(obj)) return `Date(${obj.year}, ${obj.month}, ${obj.day})`;
if (__isHogError(obj)) { return `${String(obj.type)}(${__escapeString(obj.message)}${obj.payload ? `, ${__printHogValue(obj.payload, marked)}` : ''})`; }
if (obj instanceof Map) { return `{${Array.from(obj.entries()).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`; }
return `{${Object.entries(obj).map(([key, value]) => `${__printHogValue(key, marked)}: ${__printHogValue(value, marked)}`).join(', ')}}`;
} finally {
marked.delete(obj);
}
} else if (typeof obj === 'boolean') return obj ? 'true' : 'false';
else if (obj === null || obj === undefined) return 'null';
else if (typeof obj === 'string') return __escapeString(obj);
if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`;
return obj.toString();
}
""",
[
"__isHogDateTime",
"__isHogDate",
"__isHogError",
"__escapeString",
"__escapeIdentifier",
],
],
"__escapeString": [
"""
function __escapeString(value) {
const singlequoteEscapeCharsMap = { '\\b': '\\\\b', '\\f': '\\\\f', '\\r': '\\\\r', '\\n': '\\\\n', '\\t': '\\\\t', '\\0': '\\\\0', '\\v': '\\\\v', '\\\\': '\\\\\\\\', "'": "\\\\'" }
return `'${value.split('').map((c) => singlequoteEscapeCharsMap[c] || c).join('')}'`;
}
""",
[],
],
"__escapeIdentifier": [
"""
function __escapeIdentifier(identifier) {
const backquoteEscapeCharsMap = { '\\b': '\\\\b', '\\f': '\\\\f', '\\r': '\\\\r', '\\n': '\\\\n', '\\t': '\\\\t', '\\0': '\\\\0', '\\v': '\\\\v', '\\\\': '\\\\\\\\', '`': '\\\\`' }
if (typeof identifier === 'number') return identifier.toString();
if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(identifier)) return identifier;
return `\\`${identifier.split('').map((c) => backquoteEscapeCharsMap[c] || c).join('')}\\``;
}
""",
[],
],
"__newHogError": [
"""
function __newHogError(type, message, payload) {
let error = new Error(message || 'An error occurred');
error.__hogError__ = true
error.type = type
error.payload = payload
return error
}
""",
[],
],
"__isHogError": [
"""function __isHogError(obj) {return obj && obj.__hogError__ === true}""",
[],
],
"__getNestedValue": [
"""
function __getNestedValue(obj, path, allowNull = false) {
let current = obj
for (const key of path) {
if (current == null) {
return null
}
if (current instanceof Map) {
current = current.get(key)
} else if (typeof current === 'object' && current !== null) {
current = current[key]
} else {
return null
}
}
if (current === null && !allowNull) {
return null
}
return current
}
""",
[],
],
"__like": [
"""
function __like(str, pattern, caseInsensitive = false) {
if (caseInsensitive) {
str = str.toLowerCase()
pattern = pattern.toLowerCase()
}
pattern = String(pattern)
.replaceAll(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')
.replaceAll('%', '.*')
.replaceAll('_', '.')
return new RegExp(pattern).test(str)
}
""",
[],
],
"__getProperty": [
"""
function __getProperty(objectOrArray, key, nullish) {
if ((nullish && !objectOrArray) || key === 0) { return null }
if (Array.isArray(objectOrArray)) {
return key > 0 ? objectOrArray[key - 1] : objectOrArray[objectOrArray.length + key]
} else {
return objectOrArray[key]
}
}
""",
[],
],
"__setProperty": [
"""
function __setProperty(objectOrArray, key, value) {
if (Array.isArray(objectOrArray)) {
if (key > 0) {
objectOrArray[key - 1] = value
} else {
objectOrArray[objectOrArray.length + key] = value
}
} else {
objectOrArray[key] = value
}
return objectOrArray
}
""",
[],
],
"__lambda": [
"""function __lambda (fn) { return fn }""",
[],
],
}
def import_stl_functions(requested_functions):
"""
Given a list of requested function names, returns a string containing the code
for these functions and all their dependencies, in an order suitable for evaluation.
"""
# Set to keep track of all required functions
required_functions = set()
visited = set()
# Recursive function to find all dependencies
def dfs(func_name):
if func_name in visited:
return
visited.add(func_name)
if func_name not in STL_FUNCTIONS:
raise ValueError(f"Function '{func_name}' is not defined.")
_, dependencies = STL_FUNCTIONS[func_name]
for dep in sorted(dependencies):
dfs(dep)
required_functions.add(func_name)
# Start DFS from each requested function
for func in requested_functions:
dfs(func)
# Build the dependency graph
dependency_graph = {}
for func in sorted(required_functions):
_, dependencies = STL_FUNCTIONS[func]
dependency_graph[func] = dependencies
# Perform topological sort
def topological_sort(graph):
visited = set()
temp_mark = set()
result = []
def visit(node):
if node in visited:
return
if node in temp_mark:
raise ValueError(f"Circular dependency detected involving {node}")
temp_mark.add(node)
for neighbor in sorted(graph.get(node, [])):
visit(neighbor)
temp_mark.remove(node)
visited.add(node)
result.append(node)
for node in sorted(graph):
visit(node)
return result[::-1] # reverse the list to get correct order
sorted_functions = topological_sort(dependency_graph)
# Build the final code
code_pieces = []
for func in sorted_functions:
code, _ = STL_FUNCTIONS[func]
code_pieces.append(str(code).strip())
return "\n".join(code_pieces)

View File

@ -1,6 +1,6 @@
import pytest import pytest
from posthog.hogql.bytecode import to_bytecode, execute_hog, create_bytecode from posthog.hogql.compiler.bytecode import to_bytecode, execute_hog, create_bytecode
from hogvm.python.operation import Operation as op, HOGQL_BYTECODE_IDENTIFIER as _H, HOGQL_BYTECODE_VERSION 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.hogql.errors import NotImplementedError, QueryError
from posthog.hogql.parser import parse_program from posthog.hogql.parser import parse_program

View File

@ -0,0 +1,227 @@
from posthog.hogql.compiler.javascript import JavaScriptCompiler, Local, _sanitize_identifier, to_js_program, to_js_expr
from posthog.hogql.errors import NotImplementedError, QueryError
from posthog.hogql import ast
from posthog.test.base import BaseTest
class TestSanitizeIdentifier(BaseTest):
def test_valid_identifiers(self):
self.assertEqual(_sanitize_identifier("validName"), "validName")
self.assertEqual(_sanitize_identifier("_validName123"), "_validName123")
def test_keywords(self):
self.assertEqual(_sanitize_identifier("await"), "__x_await")
self.assertEqual(_sanitize_identifier("class"), "__x_class")
def test_internal_conflicts(self):
self.assertEqual(_sanitize_identifier("__x_internal"), "__x___x_internal")
def test_invalid_identifiers(self):
self.assertEqual(_sanitize_identifier("123invalid"), '["123invalid"]')
self.assertEqual(_sanitize_identifier("invalid-name"), '["invalid-name"]')
def test_integer_identifiers(self):
self.assertEqual(_sanitize_identifier(123), '["123"]')
class TestJavaScript(BaseTest):
def test_javascript_create_basic_expressions(self):
self.assertEqual(to_js_expr("1 + 2"), "(1 + 2)")
self.assertEqual(to_js_expr("1 and 2"), "!!(1 && 2)")
self.assertEqual(to_js_expr("1 or 2"), "!!(1 || 2)")
self.assertEqual(to_js_expr("not true"), "(!true)")
self.assertEqual(to_js_expr("1 < 2"), "(1 < 2)")
self.assertEqual(to_js_expr("properties.bla"), '__getProperty(__getGlobal("properties"), "bla", true)')
def test_javascript_string_functions(self):
self.assertEqual(to_js_expr("concat('a', 'b')"), 'concat("a", "b")')
self.assertEqual(to_js_expr("lower('HELLO')"), 'lower("HELLO")')
self.assertEqual(to_js_expr("upper('hello')"), 'upper("hello")')
self.assertEqual(to_js_expr("reverse('abc')"), 'reverse("abc")')
def test_arithmetic_operations(self):
self.assertEqual(to_js_expr("3 - 1"), "(3 - 1)")
self.assertEqual(to_js_expr("2 * 3"), "(2 * 3)")
self.assertEqual(to_js_expr("5 / 2"), "(5 / 2)")
self.assertEqual(to_js_expr("10 % 3"), "(10 % 3)")
def test_comparison_operations(self):
self.assertEqual(to_js_expr("3 = 4"), "(3 == 4)")
self.assertEqual(to_js_expr("3 != 4"), "(3 != 4)")
self.assertEqual(to_js_expr("3 < 4"), "(3 < 4)")
self.assertEqual(to_js_expr("3 <= 4"), "(3 <= 4)")
self.assertEqual(to_js_expr("3 > 4"), "(3 > 4)")
self.assertEqual(to_js_expr("3 >= 4"), "(3 >= 4)")
def test_javascript_create_query_error(self):
with self.assertRaises(QueryError) as e:
to_js_expr("1 in cohort 2")
self.assertIn(
"Can't use cohorts in real-time filters. Please inline the relevant expressions", str(e.exception)
)
def test_scope_errors(self):
compiler = JavaScriptCompiler(locals=[Local(name="existing_var", depth=0)])
compiler._start_scope()
compiler._declare_local("new_var")
with self.assertRaises(QueryError):
compiler._declare_local("new_var")
compiler._end_scope()
def test_arithmetic_operation(self):
code = to_js_expr("3 + 5 * (10 / 2) - 7")
self.assertEqual(code, "((3 + (5 * (10 / 2))) - 7)")
def test_comparison(self):
code = to_js_expr("1 in 2")
self.assertEqual(code, "(2.includes(1))")
def test_if_else(self):
code = to_js_program("if (1 < 2) { return true } else { return false }")
expected_code = "if ((1 < 2)) {\n return true;\n} else {\n return false;\n}"
self.assertEqual(code.strip(), expected_code.strip())
def test_declare_local(self):
compiler = JavaScriptCompiler()
compiler._declare_local("a_var")
self.assertIn("a_var", [local.name for local in compiler.locals])
def test_visit_return_statement(self):
compiler = JavaScriptCompiler()
code = compiler.visit_return_statement(ast.ReturnStatement(expr=ast.Constant(value="test")))
self.assertEqual(code, 'return "test";')
def test_not_implemented_visit_select_query(self):
with self.assertRaises(NotImplementedError) as e:
to_js_expr("(select 1)")
self.assertEqual(str(e.exception), "JavaScriptCompiler does not support SelectQuery")
def test_throw_statement(self):
compiler = JavaScriptCompiler()
code = compiler.visit_throw_statement(ast.ThrowStatement(expr=ast.Constant(value="Error!")))
self.assertEqual(code, 'throw "Error!";')
def test_visit_dict(self):
code = to_js_expr("{'key1': 'value1', 'key2': 'value2'}")
self.assertEqual(code, '{"key1": "value1", "key2": "value2"}')
def test_visit_array(self):
code = to_js_expr("[1, 2, 3, 4]")
self.assertEqual(code, "[1, 2, 3, 4]")
def test_visit_lambda(self):
code = to_js_expr("x -> x + 1")
self.assertTrue(code.startswith("__lambda((x) => (x + 1))"))
def test_inlined_stl(self):
compiler = JavaScriptCompiler()
compiler.inlined_stl.add("concat")
stl_code = compiler.get_inlined_stl()
self.assertIn("function concat", stl_code)
def test_sanitize_keywords(self):
self.assertEqual(_sanitize_identifier("for"), "__x_for")
self.assertEqual(_sanitize_identifier("await"), "__x_await")
def test_json_parse(self):
code = to_js_expr('jsonParse(\'{"key": "value"}\')')
self.assertEqual(code, 'jsonParse("{\\"key\\": \\"value\\"}")')
def test_javascript_create_2(self):
self.assertEqual(to_js_expr("1 + 2"), "(1 + 2)")
self.assertEqual(to_js_expr("1 and 2"), "!!(1 && 2)")
self.assertEqual(to_js_expr("1 or 2"), "!!(1 || 2)")
self.assertEqual(to_js_expr("1 or (2 and 1) or 2"), "!!(1 || !!(2 && 1) || 2)")
self.assertEqual(to_js_expr("(1 or 2) and (1 or 2)"), "!!(!!(1 || 2) && !!(1 || 2))")
self.assertEqual(to_js_expr("not true"), "(!true)")
self.assertEqual(to_js_expr("true"), "true")
self.assertEqual(to_js_expr("false"), "false")
self.assertEqual(to_js_expr("null"), "null")
self.assertEqual(to_js_expr("3.14"), "3.14")
self.assertEqual(to_js_expr("properties.bla"), '__getProperty(__getGlobal("properties"), "bla", true)')
self.assertEqual(to_js_expr("concat('arg', 'another')"), 'concat("arg", "another")')
self.assertEqual(
to_js_expr("ifNull(properties.email, false)"),
'(__getProperty(__getGlobal("properties"), "email", true) ?? false)',
)
self.assertEqual(to_js_expr("1 in 2"), "(2.includes(1))")
self.assertEqual(to_js_expr("1 not in 2"), "(!2.includes(1))")
self.assertEqual(to_js_expr("match('test', 'e.*')"), 'match("test", "e.*")')
self.assertEqual(to_js_expr("not('test')"), '(!"test")')
self.assertEqual(to_js_expr("or('test', 'test2')"), '("test" || "test2")')
self.assertEqual(to_js_expr("and('test', 'test2')"), '("test" && "test2")')
def test_javascript_code_generation(self):
js_code = to_js_program("""
fun fibonacci(number) {
if (number < 2) {
return number;
} else {
return fibonacci(number - 1) + fibonacci(number - 2);
}
}
return fibonacci(6);
""")
expected_js = """function fibonacci(number) {
if ((number < 2)) {
return number;
} else {
return (fibonacci((number - 1)) + fibonacci((number - 2)));
}
}
return fibonacci(6);"""
self.assertEqual(js_code.strip(), expected_js.strip())
def test_javascript_hogqlx(self):
code = to_js_expr("<Sparkline data={[1,2,3]} />")
self.assertEqual(code.strip(), '{"__hx_tag": "Sparkline", "data": [1, 2, 3]}')
def test_sanitized_function_names(self):
code = to_js_expr("typeof('test')")
self.assertEqual(code, '__x_typeof("test")')
def test_function_name_sanitization(self):
code = to_js_expr("Error('An error occurred')")
self.assertEqual(code, '__x_Error("An error occurred")')
def test_ilike(self):
code = to_js_expr("'hello' ilike '%ELLO%'")
self.assertEqual(code, 'ilike("hello", "%ELLO%")')
def test_not_ilike(self):
code = to_js_expr("'hello' not ilike '%ELLO%'")
self.assertEqual(code, '!ilike("hello", "%ELLO%")')
def test_regex(self):
code = to_js_expr("'hello' =~ 'h.*o'")
self.assertEqual(code, 'new RegExp("h.*o").test("hello")')
def test_not_regex(self):
code = to_js_expr("'hello' !~ 'h.*o'")
self.assertEqual(code, '!(new RegExp("h.*o").test("hello"))')
def test_i_regex(self):
code = to_js_expr("'hello' =~* 'H.*O'")
self.assertEqual(code, 'new RegExp("H.*O", "i").test("hello")')
def test_not_i_regex(self):
code = to_js_expr("'hello' !~* 'H.*O'")
self.assertEqual(code, '!(new RegExp("H.*O", "i").test("hello"))')
def test_array_access(self):
code = to_js_expr("array[2]")
self.assertEqual(code, '__getProperty(__getGlobal("array"), 2, false)')
def test_tuple_access(self):
code = to_js_expr("(1, 2, 3).2")
self.assertEqual(code, "__getProperty(tuple(1, 2, 3), 2, false)")
def test_function_assignment_error(self):
compiler = JavaScriptCompiler()
with self.assertRaises(QueryError) as context:
compiler.visit_variable_assignment(
ast.VariableAssignment(left=ast.Field(chain=["globalVar"]), right=ast.Constant(value=42))
)
self.assertIn(
'Variable "globalVar" not declared in this scope. Cannot assign to globals.', str(context.exception)
)

View File

@ -2,7 +2,7 @@ from typing import Optional, cast
from django.conf import settings from django.conf import settings
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.context import HogQLContext from posthog.hogql.context import HogQLContext
from posthog.hogql.errors import ExposedHogQLError from posthog.hogql.errors import ExposedHogQLError
from posthog.hogql.filters import replace_filters from posthog.hogql.filters import replace_filters

View File

@ -90,7 +90,7 @@ class Action(models.Model):
def refresh_bytecode(self): def refresh_bytecode(self):
from posthog.hogql.property import action_to_expr from posthog.hogql.property import action_to_expr
from posthog.hogql.bytecode import create_bytecode from posthog.hogql.compiler.bytecode import create_bytecode
try: try:
new_bytecode = create_bytecode(action_to_expr(self)).bytecode new_bytecode = create_bytecode(action_to_expr(self)).bytecode