0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 13:39:22 +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",
"cwd": "${workspaceFolder}",
"env": {
"SKIP_ASYNC_MIGRATIONS_SETUP": "0",
"SKIP_ASYNC_MIGRATIONS_SETUP": "1",
"DEBUG": "1",
"BILLING_SERVICE_URL": "https://billing.dev.posthog.dev",
"SKIP_SERVICE_VERSION_REQUIREMENTS": "1"

View File

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

View File

@ -1,7 +1,7 @@
import dataclasses
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.property import action_to_expr
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,
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,
-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
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,
16, 32, "Problem with your food: ", 36, 0, 32, "message", 45, 2, "concat", 2, 2, "print", 1, 35, 39, 2, 35, 49, 35, 35,
51, 39, 55, 36, 0, 32, "type", 45, 32, "FishError", 36, 1, 11, 40, 16, 32, "FishError: ", 36, 0, 32, "message", 45, 2,
"concat", 2, 2, "print", 1, 35, 39, 25, 32, "Error of type ", 36, 0, 32, "name", 45, 32, ": ", 36, 0, 32, "message", 45,
"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,
"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,
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,
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
Error of type : 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 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,
"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", 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]

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', 'other': 'thing'}
{'key': {'otherKey': 'value'}}
{3: 'value'}
{'kk': '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",
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,
"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,
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",
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,
"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,
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,
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",
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,
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,
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,
"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,
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,
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,
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,
"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",
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, 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]
"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,
"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,
"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,
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,
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",
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,
"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,
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,
"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,
"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,
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,
"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,
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,
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,
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,
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,
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
false
false
true
true
true
"arganother"
"1"
"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]
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) {
print(f'FishError: {e.message}')
} catch (e: Error) {
print(f'Error of type {e.name}: {e.message}')
print(f'Error of type {e.type}: {e.message}')
}
try {
@ -29,7 +29,7 @@ try {
print(f'Problem with your food: {e.message}')
}
} catch (e: Error) {
print(f'Error of type {e.name}: {e.message}')
print(f'Error of type {e.type}: {e.message}')
} catch (e: FishError) {
print(f'FishError: {e.message}')
}

View File

@ -3,7 +3,10 @@ print({'key': 'value'})
print({'key': 'value', 'other': 'thing'})
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', }.key)

View File

@ -37,6 +37,9 @@ test('a' not ilike 'b') // true
test('a' in 'car') // true
test('a' in 'foo') // 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(1, NULL)) // '1'
test(concat(true, false)) // 'truefalse'

View File

@ -10,7 +10,7 @@ from hogvm.python.operation import (
HOGQL_BYTECODE_VERSION as VERSION,
)
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

View File

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

View File

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

View File

@ -1,32 +1,119 @@
#!/bin/bash
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
pnpm run build
cd ..
# Navigate to the project root (parent directory of 'hogvm')
cd ..
rm -f hogvm/__tests__/__snapshots__/*.stdout.nodejs
rm -f hogvm/__tests__/__snapshots__/*.stdout.python
# Function to compute the basename for a given file
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"
# from hogvm/__tests__/*.hog get hogvm/__tests__/__snapshots__/*
basename="${file%.hog}"
basename="${basename##*/}"
basename="hogvm/__tests__/__snapshots__/$basename"
basename=$(get_basename "$file")
filename=$(basename "$file")
./bin/hoge $file $basename.hoge
./bin/hog --nodejs $basename.hoge > $basename.stdout.nodejs
./bin/hog --python $basename.hoge > $basename.stdout.python
./bin/hoge "$file" "$basename.hoge"
./bin/hog --nodejs "$basename.hoge" > "$basename.stdout.nodejs"
./bin/hog --python "$basename.hoge" > "$basename.stdout.python"
# Check if the current file should skip the compiledjs tests
if is_in_array "$filename" "${SKIP_COMPILEDJS_FILES[@]}"; then
# Skip compiledjs steps for this file
echo "Skipping compiledjs tests for $filename"
set +e
diff $basename.stdout.nodejs $basename.stdout.python
diff "$basename.stdout.nodejs" "$basename.stdout.python"
if [ $? -eq 0 ]; then
mv $basename.stdout.nodejs $basename.stdout
rm $basename.stdout.python
mv "$basename.stdout.nodejs" "$basename.stdout"
rm "$basename.stdout.python"
echo "Test passed"
else
echo "Test failed"
echo "Test failed: Output differs between Node.js and Python interpreters."
fi
set -e
else
# 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
done

View File

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

View File

@ -19,6 +19,8 @@ import {
} from './date'
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 {
if (isHogDate(args[0])) {
const month = args[0].month
@ -71,9 +73,7 @@ export const STL: Record<string, STLFunction> = {
},
toString: { fn: STLToString, minArgs: 1, maxArgs: 1 },
toUUID: {
fn: (args) => {
return String(args[0])
},
fn: STLToString,
minArgs: 1,
maxArgs: 1,
},
@ -148,8 +148,8 @@ export const STL: Record<string, STLFunction> = {
},
tuple: {
fn: (args) => {
const tuple = args.slice()
;(tuple as any).__isHogTuple = true
const tuple = args.slice();
(tuple as any).__isHogTuple = true
return tuple
},
minArgs: 0,

View File

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

View File

@ -76,7 +76,7 @@
"@medv/finder": "^3.1.0",
"@microlink/react-json-view": "^1.21.3",
"@monaco-editor/react": "4.6.0",
"@posthog/hogvm": "^1.0.58",
"@posthog/hogvm": "^1.0.59",
"@posthog/icons": "0.9.1",
"@posthog/plugin-scaffold": "^1.4.4",
"@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:devNoWatch": "NODE_ENV=dev BASE_DIR=.. node -r @swc-node/register src/index.ts",
"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:check": "tsc --noEmit -p .",
"compile": "pnpm typescript:compile",
@ -54,7 +54,7 @@
"@maxmind/geoip2-node": "^3.4.0",
"@posthog/clickhouse": "^1.7.0",
"@posthog/cyclotron": "file:../rust/cyclotron-node",
"@posthog/hogvm": "^1.0.58",
"@posthog/hogvm": "^1.0.59",
"@posthog/plugin-scaffold": "1.4.4",
"@sentry/node": "^7.49.0",
"@sentry/profiling-node": "^0.3.0",

View File

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

View File

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

View File

@ -6,7 +6,7 @@ from rest_framework.response import Response
from hogql_parser import parse_program
from posthog.api.mixins import PydanticModelMixin
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.schema import HogCompileResponse

View File

@ -7,7 +7,7 @@ from rest_framework.exceptions import ValidationError
from hogvm.python.debugger import color_bytecode
from posthog.clickhouse.query_tagging import tag_queries
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.context import HogQLContext
from posthog.hogql.database.database import create_hogql_database, serialize_database

View File

@ -1,6 +1,6 @@
from typing import Optional
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.property import action_to_expr, property_to_expr, ast
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 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.test.base import APIBaseTest, ClickhouseTestMixin, QueryMatchingTest

View File

@ -2,7 +2,7 @@ import logging
from typing import Any, Optional
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
logger = logging.getLogger(__name__)

View File

@ -2,7 +2,8 @@ import sys
import json
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("-")]
args = [arg for arg in sys.argv if arg != "" and not arg.startswith("-")]
@ -14,12 +15,19 @@ if not filename.endswith(".hog") and not filename.endswith(".hoge"):
with open(filename) as file:
code = file.read()
if filename.endswith(".hog"):
bytecode = create_bytecode(parse_program(code)).bytecode
if "--compile" in modifiers and len(args) == 3 and args[2].endswith(".js"):
target = args[2]
js_program = to_js_program(code)
with open(target, "w") as file:
file.write(js_program + "\n")
else:
if filename.endswith(".hog"):
bytecode = create_bytecode(parse_program(code)).bytecode
else:
bytecode = json.loads(code)
if "--run" in modifiers:
if "--run" in modifiers:
if len(args) != 2:
raise ValueError("Must specify exactly one filename")
@ -27,12 +35,12 @@ if "--run" in modifiers:
for line in response.stdout:
print(line) # noqa: T201
elif "--out" in modifiers:
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:
elif "--compile" in modifiers:
if len(args) == 3:
target = args[2]
else:
@ -55,5 +63,5 @@ elif "--compile" in modifiers:
elif line != "":
file.write(line + "\n")
else:
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
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 posthog.hogql.errors import NotImplementedError, QueryError
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 posthog.hogql.bytecode import create_bytecode
from posthog.hogql.compiler.bytecode import create_bytecode
from posthog.hogql.context import HogQLContext
from posthog.hogql.errors import ExposedHogQLError
from posthog.hogql.filters import replace_filters

View File

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