From 154bab0f6776ba5928b434fcbfbc6102073e03dd Mon Sep 17 00:00:00 2001 From: Marius Andra Date: Fri, 15 Nov 2024 09:47:54 +0100 Subject: [PATCH] compact and stable print order --- hogvm/__tests__/__snapshots__/arrays.js | 67 ++- hogvm/__tests__/__snapshots__/bytecodeStl.js | 57 +-- hogvm/__tests__/__snapshots__/catch.js | 183 +++----- hogvm/__tests__/__snapshots__/catch2.js | 179 +++----- hogvm/__tests__/__snapshots__/crypto.js | 85 ++-- hogvm/__tests__/__snapshots__/date.js | 289 ++++--------- hogvm/__tests__/__snapshots__/dateFormat.js | 211 +++------ hogvm/__tests__/__snapshots__/dicts.js | 43 +- hogvm/__tests__/__snapshots__/exceptions.js | 187 +++----- hogvm/__tests__/__snapshots__/functionVars.js | 39 +- hogvm/__tests__/__snapshots__/functions.js | 54 +-- hogvm/__tests__/__snapshots__/ifElse.js | 31 +- hogvm/__tests__/__snapshots__/ifJump.js | 51 +-- hogvm/__tests__/__snapshots__/json.js | 309 +++++--------- hogvm/__tests__/__snapshots__/keysValues.js | 61 +-- hogvm/__tests__/__snapshots__/lambdas.js | 141 ++---- hogvm/__tests__/__snapshots__/loops.js | 61 +-- hogvm/__tests__/__snapshots__/mandelbrot.js | 173 +++----- hogvm/__tests__/__snapshots__/operations.js | 242 ++++------- hogvm/__tests__/__snapshots__/printLoops.js | 263 ++++-------- hogvm/__tests__/__snapshots__/printLoops2.js | 279 ++++-------- hogvm/__tests__/__snapshots__/properties.js | 45 +- hogvm/__tests__/__snapshots__/recursion.js | 29 +- hogvm/__tests__/__snapshots__/scope.js | 39 +- hogvm/__tests__/__snapshots__/stl.js | 74 ++-- hogvm/__tests__/__snapshots__/strings.js | 93 ++-- hogvm/__tests__/__snapshots__/tuples.js | 43 +- hogvm/__tests__/__snapshots__/typeof.js | 125 ++---- hogvm/__tests__/__snapshots__/upvalues.js | 37 +- hogvm/__tests__/__snapshots__/variables.js | 35 +- posthog/hogql/compiler/javascript_stl.py | 400 ++++-------------- 31 files changed, 1241 insertions(+), 2684 deletions(-) diff --git a/hogvm/__tests__/__snapshots__/arrays.js b/hogvm/__tests__/__snapshots__/arrays.js index f67cc46080438..e0db8dc431e3f 100644 --- a/hogvm/__tests__/__snapshots__/arrays.js +++ b/hogvm/__tests__/__snapshots__/arrays.js @@ -1,23 +1,15 @@ +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 __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 arrayPopBack (arr) { if (!Array.isArray(arr)) { return [] } return arr.slice(0, arr.length - 1) } -function __lambda (fn) { return fn } function arrayPushFront (arr, item) { if (!Array.isArray(arr)) { return [item] } return [item, ...arr] } -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 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 print (...args) { console.log(...args.map(__printHogStringOutput)) } -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } -function arraySort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort() } +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) { @@ -30,32 +22,20 @@ function __setProperty(objectOrArray, key, value) { } return objectOrArray } -function arrayStringConcat (arr, separator = '') { if (!Array.isArray(arr)) { return '' } return arr.join(separator) } -function arrayReverseSort (arr) { if (!Array.isArray(arr)) { return [] } return [...arr].sort().reverse() } +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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -66,19 +46,28 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } print([]); print([1, 2, 3]); diff --git a/hogvm/__tests__/__snapshots__/bytecodeStl.js b/hogvm/__tests__/__snapshots__/bytecodeStl.js index c87b5ca75e80f..2e997fe17d9f0 100644 --- a/hogvm/__tests__/__snapshots__/bytecodeStl.js +++ b/hogvm/__tests__/__snapshots__/bytecodeStl.js @@ -1,43 +1,23 @@ function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function __lambda (fn) { return fn } function like (str, pattern) { return __like(str, pattern, false) } -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 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -48,22 +28,31 @@ function __printHogValue(obj, marked = new Set()) { 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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } 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 arrayMap (func, arr) { let result = []; for (let i = 0; i < arr.length; i++) { result = arrayPushBack(result, func(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 } print("--- arrayMap ----"); print(arrayMap(__lambda((x) => (x * 2)), [1, 2, 3])); diff --git a/hogvm/__tests__/__snapshots__/catch.js b/hogvm/__tests__/__snapshots__/catch.js index 5228a39cc89f6..5b4d790238ebd 100644 --- a/hogvm/__tests__/__snapshots__/catch.js +++ b/hogvm/__tests__/__snapshots__/catch.js @@ -1,107 +1,5 @@ -function NotImplementedError (message, payload) { return __newHogError('NotImplementedError', message, payload) } -function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function HogError (type, message, payload) { return __newHogError(type, message, payload) } -function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(arg); -} -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } +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)) { @@ -110,30 +8,24 @@ function __getProperty(objectOrArray, key, nullish) { 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -144,19 +36,60 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + 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 diff --git a/hogvm/__tests__/__snapshots__/catch2.js b/hogvm/__tests__/__snapshots__/catch2.js index e7e94bd3aa33d..d075a1b41b0ab 100644 --- a/hogvm/__tests__/__snapshots__/catch2.js +++ b/hogvm/__tests__/__snapshots__/catch2.js @@ -1,3 +1,5 @@ +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)) { @@ -6,149 +8,87 @@ function __getProperty(objectOrArray, key, nullish) { return objectOrArray[key] } } -function HogError (type, message, payload) { return __newHogError(type, message, payload) } -function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } -function print (...args) { console.log(...args.map(__printHogStringOutput)) } function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = 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'; - - // Determine if milliseconds are present const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone + 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); - - // Extract date and time components 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: - // Ignore other parts - break; + 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; } } - - // Get time zone offset let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { + if (timeZone !== 'UTC') { const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); const tzParts = tzFormatter.formatToParts(date); const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - if (timeZoneNamePart && timeZoneNamePart.value) { const offsetString = timeZoneNamePart.value; const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; } } - - // Build ISO 8601 string with time zone offset let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; if (milliseconds !== null) { isoString += `.${milliseconds.toString().padStart(3, '0')}`; } isoString += offset; - return isoString; - } - // For other types, use default string representation - 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 __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 __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } + }} +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 @@ -156,13 +96,6 @@ function __newHogError(type, message, payload) { error.payload = payload return error } -function __isHogError(obj) {return obj && obj.__hogError__ === true} -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('')}\``; -} try { try { diff --git a/hogvm/__tests__/__snapshots__/crypto.js b/hogvm/__tests__/__snapshots__/crypto.js index 41e65b205c543..51a0d4bbf5785 100644 --- a/hogvm/__tests__/__snapshots__/crypto.js +++ b/hogvm/__tests__/__snapshots__/crypto.js @@ -1,53 +1,6 @@ function sha256HmacChainHex (data, options) { return 'sha256HmacChainHex not implemented' } -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 __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 sha256Hex (str, options) { return 'SHA256 not implemented' } -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 print (...args) { console.log(...args.map(__printHogStringOutput)) } function md5Hex(string) { function cmn(q, a, b, x, s, t) { a = (((a + q) + (x >>> 0) + t) >>> 0); return (((a << s) | (a >>> (32 - s))) + b) >>> 0; } function ff(a, b, c, d, x, s, t) { return cmn((b & c) | (~b & d), a, b, x, s, t); } @@ -130,7 +83,43 @@ function md5Hex(string) { } return toHex(a) + toHex(b) + toHex(c) + toHex(d); } +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); diff --git a/hogvm/__tests__/__snapshots__/date.js b/hogvm/__tests__/__snapshots__/date.js index cf98c3bfe62a8..cd56e05ea4fc0 100644 --- a/hogvm/__tests__/__snapshots__/date.js +++ b/hogvm/__tests__/__snapshots__/date.js @@ -1,225 +1,63 @@ function toUnixTimestampMilli (input, zone) { return __toUnixTimestampMilli(input, zone) } -function fromUnixTimestampMilli (input) { return __fromUnixTimestampMilli(input) } -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 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 __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 __fromUnixTimestampMilli(input) } +function fromUnixTimestamp (input) { return __fromUnixTimestamp(input) } function __toUnixTimestampMilli(input, zone) { return __toUnixTimestamp(input, zone) * 1000 } -function toDateTime (input, zone) { return __toDateTime(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 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 toString (value) { return __STLToString(value) } -function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(arg); -} -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } +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 __fromUnixTimestamp(input) } function __fromUnixTimestamp(input) { return __toHogDateTime(input) } -function toUnixTimestamp (input, zone) { return __toUnixTimestamp(input, zone) } -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 __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: dt, zone: zone || 'UTC' }; } - return { - __hogDateTime__: true, - dt: timestamp, - 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -230,19 +68,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} let dt = fromUnixTimestamp(1234334543); print(dt); diff --git a/hogvm/__tests__/__snapshots__/dateFormat.js b/hogvm/__tests__/__snapshots__/dateFormat.js index f1b7a5b2ec047..4afe34ca1cce2 100644 --- a/hogvm/__tests__/__snapshots__/dateFormat.js +++ b/hogvm/__tests__/__snapshots__/dateFormat.js @@ -1,57 +1,33 @@ -function formatDateTime (input, format, zone) { return __formatDateTime(input, format, zone) } -function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } 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: dt, zone: zone || 'UTC' }; } - return { - __hogDateTime__: true, - dt: timestamp, - 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'); - } - - // Convert timestamp to milliseconds + 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); - - // Use 'UTC' if no zone is specified - if (!zone) { - zone = 'UTC'; - } - - // Helper functions + 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())); @@ -61,14 +37,12 @@ function __formatDateTime(input, format, zone) { 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' }), @@ -186,129 +160,23 @@ function __formatDateTime(input, format, zone) { return result; } function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -319,19 +187,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} let dt = fromUnixTimestamp(1234377543.123456); print(formatDateTime(dt, "%Y-%m-%d %H:%i:%S")); diff --git a/hogvm/__tests__/__snapshots__/dicts.js b/hogvm/__tests__/__snapshots__/dicts.js index a5acf28a86b24..a85b45440d5b9 100644 --- a/hogvm/__tests__/__snapshots__/dicts.js +++ b/hogvm/__tests__/__snapshots__/dicts.js @@ -1,37 +1,18 @@ -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 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -42,19 +23,27 @@ function __printHogValue(obj, marked = new Set()) { 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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } 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 } print({}); print({"key": "value"}); diff --git a/hogvm/__tests__/__snapshots__/exceptions.js b/hogvm/__tests__/__snapshots__/exceptions.js index 3aa10e8895c2e..75ccf5020d52c 100644 --- a/hogvm/__tests__/__snapshots__/exceptions.js +++ b/hogvm/__tests__/__snapshots__/exceptions.js @@ -1,160 +1,93 @@ -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 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)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = 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'; - - // Determine if milliseconds are present const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone + 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); - - // Extract date and time components 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: - // Ignore other parts - break; + 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; } } - - // Get time zone offset let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { + if (timeZone !== 'UTC') { const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); const tzParts = tzFormatter.formatToParts(date); const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - if (timeZoneNamePart && timeZoneNamePart.value) { const offsetString = timeZoneNamePart.value; const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; } } - - // Build ISO 8601 string with time zone offset let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; if (milliseconds !== null) { isoString += `.${milliseconds.toString().padStart(3, '0')}`; } isoString += offset; - return isoString; - } - // For other types, use default string representation - 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 __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 __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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } print("start"); try { diff --git a/hogvm/__tests__/__snapshots__/functionVars.js b/hogvm/__tests__/__snapshots__/functionVars.js index ce8944293b30e..9f7ca6b8b4ea2 100644 --- a/hogvm/__tests__/__snapshots__/functionVars.js +++ b/hogvm/__tests__/__snapshots__/functionVars.js @@ -1,32 +1,20 @@ function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function __lambda (fn) { return fn } -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -37,19 +25,20 @@ function __printHogValue(obj, marked = new Set()) { if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`; return obj.toString(); } -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 __lambda (fn) { return fn } function __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === 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"); diff --git a/hogvm/__tests__/__snapshots__/functions.js b/hogvm/__tests__/__snapshots__/functions.js index 9aafc2ec5feb7..2241faa08aa49 100644 --- a/hogvm/__tests__/__snapshots__/functions.js +++ b/hogvm/__tests__/__snapshots__/functions.js @@ -1,45 +1,24 @@ function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } -function __lambda (fn) { return fn } 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 - } + 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 -} + } 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -50,19 +29,20 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } print("-- test functions --"); function add(a, b) { diff --git a/hogvm/__tests__/__snapshots__/ifElse.js b/hogvm/__tests__/__snapshots__/ifElse.js index 9732f7796c1ec..995143425a2ed 100644 --- a/hogvm/__tests__/__snapshots__/ifElse.js +++ b/hogvm/__tests__/__snapshots__/ifElse.js @@ -2,28 +2,17 @@ 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -35,18 +24,18 @@ function __printHogValue(obj, marked = new Set()) { return obj.toString(); } function __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === 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 __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('')}'`; -} print("-- test if else --"); { diff --git a/hogvm/__tests__/__snapshots__/ifJump.js b/hogvm/__tests__/__snapshots__/ifJump.js index 7f1e65bff85ef..1a6e010aa533d 100644 --- a/hogvm/__tests__/__snapshots__/ifJump.js +++ b/hogvm/__tests__/__snapshots__/ifJump.js @@ -1,37 +1,18 @@ -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 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -42,19 +23,27 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === true} let props = {}; let email = __getProperty(props, "email", true); diff --git a/hogvm/__tests__/__snapshots__/json.js b/hogvm/__tests__/__snapshots__/json.js index 9e35868d2f0dd..4d08d40f2cc90 100644 --- a/hogvm/__tests__/__snapshots__/json.js +++ b/hogvm/__tests__/__snapshots__/json.js @@ -1,51 +1,128 @@ +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)) +} +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 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 - } + 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 - } + if (!(key in currentParsed)) { return false } current = currentParsed[key] } - } else { - return false - } + } else { return false } } - return true -} + return true } function JSONExtractBool (obj, ...path) { try { if (typeof obj === 'string') { @@ -62,72 +139,6 @@ function JSONExtractBool (obj, ...path) { } return false } -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 map = new Map() - for (const key in x) { - map.set(key, convert(x[key])) - } - return map - } - return x - } - return convert(JSON.parse(str)) -} -function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } } -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 __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 __newHogError(type, message, payload) { - let error = new Error(message || 'An error occurred'); - error.__hogError__ = true - error.type = type - error.payload = payload - return error -} function __getNestedValue(obj, path, allowNull = false) { let current = obj for (const key of path) { @@ -147,98 +158,6 @@ function __getNestedValue(obj, path, allowNull = false) { } return current } -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 __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 __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 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)) -} -function __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } print(jsonParse("[1,2,3]")); let event = {"event": "$pageview", "properties": {"$browser": "Chrome", "$os": "Windows"}}; diff --git a/hogvm/__tests__/__snapshots__/keysValues.js b/hogvm/__tests__/__snapshots__/keysValues.js index bd22695aa6add..0adcf886f2e1f 100644 --- a/hogvm/__tests__/__snapshots__/keysValues.js +++ b/hogvm/__tests__/__snapshots__/keysValues.js @@ -1,30 +1,21 @@ -function print (...args) { console.log(...args.map(__printHogStringOutput)) } +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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -35,41 +26,19 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -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 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 [] -} let a = [3, 4, 5]; let b = tuple(3, 4, 5); diff --git a/hogvm/__tests__/__snapshots__/lambdas.js b/hogvm/__tests__/__snapshots__/lambdas.js index 80889b90f9990..58fb76fbee8c3 100644 --- a/hogvm/__tests__/__snapshots__/lambdas.js +++ b/hogvm/__tests__/__snapshots__/lambdas.js @@ -1,35 +1,20 @@ -function __lambda (fn) { return fn } +function print (...args) { console.log(...args.map(__printHogStringOutput)) } function jsonStringify (value, spacing) { function convert(x, marked) { - if (!marked) { - marked = new Set() - } + if (!marked) { marked = new Set() } if (typeof x === 'object' && x !== null) { - if (marked.has(x)) { - return 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) - }) + 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) - } + 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) @@ -44,84 +29,36 @@ function jsonStringify (value, spacing) { } 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 map = new Map() - for (const key in x) { - map.set(key, convert(x[key])) - } - return map - } - return x - } - return convert(JSON.parse(str)) -} -function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } } + 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 __newHogError(type, message, payload) { - let error = new Error(message || 'An error occurred'); - error.__hogError__ = true - error.type = type - error.payload = payload - return error -} -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] + return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' }; } -} -function print (...args) { console.log(...args.map(__printHogStringOutput)) } + 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -132,19 +69,35 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } let b = __lambda((x) => (x * 2)); print(b); diff --git a/hogvm/__tests__/__snapshots__/loops.js b/hogvm/__tests__/__snapshots__/loops.js index 2d6ead7d44e85..00fd42ebbf218 100644 --- a/hogvm/__tests__/__snapshots__/loops.js +++ b/hogvm/__tests__/__snapshots__/loops.js @@ -1,40 +1,21 @@ -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 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -45,31 +26,19 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === true} -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 __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('')}'`; -} print("-- test while loop --"); { diff --git a/hogvm/__tests__/__snapshots__/mandelbrot.js b/hogvm/__tests__/__snapshots__/mandelbrot.js index efa1f61cc96be..9824fdfe94646 100644 --- a/hogvm/__tests__/__snapshots__/mandelbrot.js +++ b/hogvm/__tests__/__snapshots__/mandelbrot.js @@ -1,129 +1,23 @@ 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)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -134,19 +28,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} function mandelbrot(re, im, max_iter) { let z_re = 0.0; diff --git a/hogvm/__tests__/__snapshots__/operations.js b/hogvm/__tests__/__snapshots__/operations.js index 9a01138d6c34f..7525e642c918f 100644 --- a/hogvm/__tests__/__snapshots__/operations.js +++ b/hogvm/__tests__/__snapshots__/operations.js @@ -1,35 +1,32 @@ +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 (!marked) { marked = new Set() } if (typeof x === 'object' && x !== null) { - if (marked.has(x)) { - return 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) - }) + 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) - } + 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) @@ -42,22 +39,8 @@ function jsonStringify (value, spacing) { } return JSON.stringify(convert(value)) } -function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } -function match (str, pattern) { return new RegExp(pattern).test(str) } -function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function toUUID (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 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() @@ -69,131 +52,24 @@ function __like(str, pattern, caseInsensitive = false) { .replaceAll('_', '.') return new RegExp(pattern).test(str) } -function toString (value) { return __STLToString(value) } function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -204,30 +80,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} function test(val) { print(jsonStringify(val)); diff --git a/hogvm/__tests__/__snapshots__/printLoops.js b/hogvm/__tests__/__snapshots__/printLoops.js index ae032e5e4dab4..e197e0bf75164 100644 --- a/hogvm/__tests__/__snapshots__/printLoops.js +++ b/hogvm/__tests__/__snapshots__/printLoops.js @@ -1,35 +1,20 @@ -function concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } +function print (...args) { console.log(...args.map(__printHogStringOutput)) } function jsonStringify (value, spacing) { function convert(x, marked) { - if (!marked) { - marked = new Set() - } + if (!marked) { marked = new Set() } if (typeof x === 'object' && x !== null) { - if (marked.has(x)) { - return 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) - }) + 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) - } + 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) @@ -42,7 +27,25 @@ function jsonStringify (value, spacing) { } return JSON.stringify(convert(value)) } -function print (...args) { console.log(...args.map(__printHogStringOutput)) } +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) { @@ -55,28 +58,6 @@ function __setProperty(objectOrArray, key, value) { } return objectOrArray } -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 map = new Map() - for (const key in x) { - map.set(key, convert(x[key])) - } - return map - } - return x - } - return convert(JSON.parse(str)) -} function __newHogError(type, message, payload) { let error = new Error(message || 'An error occurred'); error.__hogError__ = true @@ -84,147 +65,24 @@ function __newHogError(type, message, payload) { error.payload = payload return error } -function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } } -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)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; - } - isoString += offset; - - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -235,19 +93,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } 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; + } + } + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} let obj = {"key": "value", "key2": "value2"}; let str = "na"; diff --git a/hogvm/__tests__/__snapshots__/printLoops2.js b/hogvm/__tests__/__snapshots__/printLoops2.js index 6e09c134dfc2e..9a3d7e17fa901 100644 --- a/hogvm/__tests__/__snapshots__/printLoops2.js +++ b/hogvm/__tests__/__snapshots__/printLoops2.js @@ -1,57 +1,20 @@ -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 map = new Map() - for (const key in x) { - map.set(key, convert(x[key])) - } - return map - } - return x - } - return convert(JSON.parse(str)) -} function print (...args) { console.log(...args.map(__printHogStringOutput)) } function jsonStringify (value, spacing) { function convert(x, marked) { - if (!marked) { - marked = new Set() - } + if (!marked) { marked = new Set() } if (typeof x === 'object' && x !== null) { - if (marked.has(x)) { - return 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) - }) + 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) - } + 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) @@ -64,154 +27,62 @@ function jsonStringify (value, spacing) { } return JSON.stringify(convert(value)) } -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 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 concat (...args) { return args.map((arg) => (arg === null ? '' : __STLToString(arg))).join('') } -function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; + return { __hogDateTime__: true, dt: dt, zone: zone || 'UTC' }; } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; - const date = new Date(dt.dt * 1000); - const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present - const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components - const options = { - timeZone, - year: 'numeric', - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - hour12: false, - }; - - // Create a formatter for the specified time zone - const formatter = new Intl.DateTimeFormat('en-US', options); - const parts = formatter.formatToParts(date); - - // Extract date and time components - 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: - // Ignore other parts - break; - } - } - - // Get time zone offset - let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; + 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 { - const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; - const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); - const tzParts = tzFormatter.formatToParts(date); - const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - - if (timeZoneNamePart && timeZoneNamePart.value) { - const offsetString = timeZoneNamePart.value; - const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } - } - } - - // Build ISO 8601 string with time zone offset - let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; - if (milliseconds !== null) { - isoString += `.${milliseconds.toString().padStart(3, '0')}`; + objectOrArray[objectOrArray.length + key] = value } - isoString += offset; - - return isoString; + } else { + objectOrArray[key] = value } - // For other types, use default string representation - return __printHogStringOutput(arg); + 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -222,32 +93,58 @@ function __printHogValue(obj, marked = new Set()) { 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 __isHogError(obj) {return obj && obj.__hogError__ === true} function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __toHogDate(year, month, day) { return { __hogDate__: true, year: year, month: month, day: day, } } function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __setProperty(objectOrArray, key, value) { - if (Array.isArray(objectOrArray)) { - if (key > 0) { - objectOrArray[key - 1] = value - } else { - objectOrArray[objectOrArray.length + key] = value +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; + } } - } else { - objectOrArray[key] = value - } - return objectOrArray -} -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('')}'`; -} + let offset = 'Z'; + if (timeZone !== 'UTC') { + const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; + const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); + const tzParts = tzFormatter.formatToParts(date); + const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); + if (timeZoneNamePart && timeZoneNamePart.value) { + const offsetString = timeZoneNamePart.value; + const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; + } + } + let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; + if (milliseconds !== null) { + isoString += `.${milliseconds.toString().padStart(3, '0')}`; + } + isoString += offset; + return isoString; + }} let root = {"key": "value", "key2": "value2"}; let leaf = {"key": "value", "key2": "value2"}; diff --git a/hogvm/__tests__/__snapshots__/properties.js b/hogvm/__tests__/__snapshots__/properties.js index a2126e32e490f..a09f437d4d763 100644 --- a/hogvm/__tests__/__snapshots__/properties.js +++ b/hogvm/__tests__/__snapshots__/properties.js @@ -1,4 +1,5 @@ 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) { @@ -11,32 +12,20 @@ function __setProperty(objectOrArray, key, value) { } return objectOrArray } -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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -47,19 +36,9 @@ function __printHogValue(obj, marked = new Set()) { if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`; return obj.toString(); } -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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === 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)) { @@ -68,6 +47,16 @@ function __getProperty(objectOrArray, key, nullish) { 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)}]; diff --git a/hogvm/__tests__/__snapshots__/recursion.js b/hogvm/__tests__/__snapshots__/recursion.js index 6375e45c37f0a..1eb354a7e504b 100644 --- a/hogvm/__tests__/__snapshots__/recursion.js +++ b/hogvm/__tests__/__snapshots__/recursion.js @@ -1,30 +1,18 @@ -function __lambda (fn) { return fn } 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -35,19 +23,20 @@ function __printHogValue(obj, marked = new Set()) { 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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } 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 __isHogError(obj) {return obj && obj.__hogError__ === true} let fibonacci = __lambda((number) => { if ((number < 2)) { diff --git a/hogvm/__tests__/__snapshots__/scope.js b/hogvm/__tests__/__snapshots__/scope.js index dcc19e81c954f..6bb0aa440317f 100644 --- a/hogvm/__tests__/__snapshots__/scope.js +++ b/hogvm/__tests__/__snapshots__/scope.js @@ -1,3 +1,4 @@ +function print (...args) { console.log(...args.map(__printHogStringOutput)) } function __setProperty(objectOrArray, key, value) { if (Array.isArray(objectOrArray)) { if (key > 0) { @@ -10,33 +11,20 @@ function __setProperty(objectOrArray, key, value) { } return objectOrArray } -function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function __lambda (fn) { return fn } 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -47,19 +35,20 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } let dbl = __lambda((x) => (x * 2)); print(dbl); diff --git a/hogvm/__tests__/__snapshots__/stl.js b/hogvm/__tests__/__snapshots__/stl.js index 8e6053f7609a7..9af5082cd8655 100644 --- a/hogvm/__tests__/__snapshots__/stl.js +++ b/hogvm/__tests__/__snapshots__/stl.js @@ -1,38 +1,37 @@ function upper (value) { return value.toUpperCase() } function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; } -function length (value) { return value.length } -function notEmpty (value) { return !empty(value) } -function lower (value) { return value.toLowerCase() } -function decodeURLComponent (str) { return decodeURIComponent(str) } +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 encodeURLComponent (str) { return encodeURIComponent(str) } 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -43,38 +42,19 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -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 base64Encode (str) { return Buffer.from(str).toString('base64') } -function base64Decode (str) { return Buffer.from(str, 'base64').toString() } -function reverse (value) { return value.split('').reverse().join('') } -function __isHogError(obj) {return obj && obj.__hogError__ === true} -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 __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('')}'`; -} print("-- empty, notEmpty, length, lower, upper, reverse --"); if (!!(empty("") && notEmpty("234"))) { diff --git a/hogvm/__tests__/__snapshots__/strings.js b/hogvm/__tests__/__snapshots__/strings.js index 634239d8285df..9ce51632b4bd4 100644 --- a/hogvm/__tests__/__snapshots__/strings.js +++ b/hogvm/__tests__/__snapshots__/strings.js @@ -1,37 +1,29 @@ -function positionCaseInsensitive (str, elem) { if (typeof str === 'string') { return str.toLowerCase().indexOf(String(elem).toLowerCase()) + 1 } else { return 0 } } -function notILike (str, pattern) { return !__like(str, pattern, true) } -function position (str, elem) { if (typeof str === 'string') { return str.indexOf(String(elem)) + 1 } else { return 0 } } -function trimLeft (str, char) { +function trimRight (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-- } - return str.slice(start) + return str.slice(0, end) } -function notLike (str, pattern) { return !__like(str, pattern, false) } -function print (...args) { console.log(...args.map(__printHogStringOutput)) } -function splitByString (separator, str, maxSplits) { if (maxSplits === undefined || maxSplits === null) { return str.split(separator) } return str.split(separator, maxSplits) } -function trimRight (str, char) { +function trimLeft (str, char) { if (char === null || char === undefined) { char = ' ' } if (char.length !== 1) { return '' } - let end = str.length - while (str[end - 1] === char) { - end-- + let start = 0 + while (str[start] === char) { + start++ } - return str.slice(0, end) + return str.slice(start) } -function __printHogStringOutput(obj) { if (typeof obj === 'string') { return obj } return __printHogValue(obj) } -function ilike (str, pattern) { return __like(str, pattern, true) } function trim (str, char) { if (char === null || char === undefined) { char = ' ' @@ -52,42 +44,28 @@ function trim (str, char) { } 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 __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 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -98,19 +76,30 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } print(trim(" hello world ")); print(trimLeft(" hello world ")); diff --git a/hogvm/__tests__/__snapshots__/tuples.js b/hogvm/__tests__/__snapshots__/tuples.js index cb21f0c8fc256..7ac28573165a4 100644 --- a/hogvm/__tests__/__snapshots__/tuples.js +++ b/hogvm/__tests__/__snapshots__/tuples.js @@ -1,38 +1,19 @@ function tuple (...args) { const tuple = args.slice(); tuple.__isHogTuple = true; return tuple; } function print (...args) { console.log(...args.map(__printHogStringOutput)) } -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 __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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -43,13 +24,21 @@ function __printHogValue(obj, marked = new Set()) { 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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === true} 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(); diff --git a/hogvm/__tests__/__snapshots__/typeof.js b/hogvm/__tests__/__snapshots__/typeof.js index 53d416161d391..e4287e026eb30 100644 --- a/hogvm/__tests__/__snapshots__/typeof.js +++ b/hogvm/__tests__/__snapshots__/typeof.js @@ -1,30 +1,42 @@ -function print (...args) { console.log(...args.map(__printHogStringOutput)) } +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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -35,64 +47,20 @@ function __printHogValue(obj, marked = new Set()) { 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 __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 __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 __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 __lambda (fn) { return fn } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } function __x_Error (message, payload) { return __newHogError('Error', message, payload) } function __newHogError(type, message, payload) { let error = new Error(message || 'An error occurred'); @@ -101,25 +69,6 @@ function __newHogError(type, message, payload) { error.payload = payload return error } -function toDate (input) { return __toDate(input) } -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 __isHogError(obj) {return obj && obj.__hogError__ === true} function test(obj) { print(__x_typeof(obj)); diff --git a/hogvm/__tests__/__snapshots__/upvalues.js b/hogvm/__tests__/__snapshots__/upvalues.js index e034cdafe2dd7..39182158f6bfc 100644 --- a/hogvm/__tests__/__snapshots__/upvalues.js +++ b/hogvm/__tests__/__snapshots__/upvalues.js @@ -2,28 +2,17 @@ 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -34,20 +23,20 @@ function __printHogValue(obj, marked = new Set()) { if (typeof obj === 'function') return `fn<${__escapeIdentifier(obj.name || 'lambda')}(${obj.length})>`; return obj.toString(); } -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 __isHogError(obj) {return obj && obj.__hogError__ === true} -function __isHogDate(obj) { return obj && obj.__hogDate__ === true } 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)); diff --git a/hogvm/__tests__/__snapshots__/variables.js b/hogvm/__tests__/__snapshots__/variables.js index ecaa275923e5d..c6cb97d354c57 100644 --- a/hogvm/__tests__/__snapshots__/variables.js +++ b/hogvm/__tests__/__snapshots__/variables.js @@ -2,28 +2,17 @@ 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -34,19 +23,19 @@ function __printHogValue(obj, marked = new Set()) { 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('')}\``; } -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 __isHogDate(obj) { return obj && obj.__hogDate__ === true } -function __isHogDateTime(obj) { return obj && obj.__hogDateTime__ === true } -function __isHogError(obj) {return obj && obj.__hogError__ === true} print("-- test variables --"); { diff --git a/posthog/hogql/compiler/javascript_stl.py b/posthog/hogql/compiler/javascript_stl.py index f8e2484dd0cc2..bd42f46bac525 100644 --- a/posthog/hogql/compiler/javascript_stl.py +++ b/posthog/hogql/compiler/javascript_stl.py @@ -35,30 +35,16 @@ ], "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; -}""", + 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; -}""", + 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": [ @@ -72,19 +58,10 @@ "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 - } + 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 -}""", + } else if (typeof value === 'number' || typeof value === 'boolean') { return false } + return !value }""", [], ], "notEmpty": [ @@ -114,60 +91,33 @@ "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 map = new Map() - for (const key in x) { - map.set(key, convert(x[key])) - } - return map - } - return x - } - return convert(JSON.parse(str)) -}""", + 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 (!marked) { marked = new Set() } if (typeof x === 'object' && x !== null) { - if (marked.has(x)) { - return 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) - }) + 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) - } + 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) @@ -187,49 +137,22 @@ 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 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 - } + 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 - } + if (!(key in currentParsed)) { return false } current = currentParsed[key] } - } else { - return false - } + } else { return false } } - return true -}""", + return true }""", [], ], "isValidJSON": [ @@ -238,13 +161,7 @@ ], "JSONLength": [ """function JSONLength (obj, ...path) { - try { - if (typeof obj === 'string') { - obj = JSON.parse(obj) - } - } catch (e) { - return 0 - } + 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)) { @@ -255,8 +172,7 @@ return Object.keys(value).length } } - return 0 -}""", + return 0 }""", ["__getNestedValue"], ], "JSONExtractBool": [ @@ -471,31 +387,11 @@ [], ], "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 [] -}""", + """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 [] -}""", + """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": [ @@ -608,60 +504,30 @@ ], "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' - } + 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"], ], - "__STLToString": [ - r"""function __STLToString(arg) { - if (arg && __isHogDate(arg)) { - // Handle HogDate objects - const month = arg.month.toString().padStart(2, '0'); - const day = arg.day.toString().padStart(2, '0'); - return `${arg.year}-${month}-${day}`; - } - if (arg && __isHogDateTime(arg)) { - // Handle HogDateTime objects - const dt = arg; + "__DateTimeToString": [ + r"""function __DateTimeToString (dt) { + if (__isHogDateTime(dt)) { const date = new Date(dt.dt * 1000); const timeZone = dt.zone || 'UTC'; - - // Determine if milliseconds are present const milliseconds = Math.floor(dt.dt * 1000 % 1000); - - // Formatting options for date and time components const options = { timeZone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }; - - // Create a formatter for the specified time zone const formatter = new Intl.DateTimeFormat('en-US', options); const parts = formatter.formatToParts(date); - - // Extract date and time components let year, month, day, hour, minute, second; for (const part of parts) { switch (part.type) { @@ -674,49 +540,34 @@ default: break; } } - - // Get time zone offset let offset = 'Z'; - if (timeZone === 'UTC') { - offset = 'Z'; - } else { + if (timeZone !== 'UTC') { const tzOptions = { timeZone, timeZoneName: 'shortOffset' }; const tzFormatter = new Intl.DateTimeFormat('en-US', tzOptions); const tzParts = tzFormatter.formatToParts(date); const timeZoneNamePart = tzParts.find(part => part.type === 'timeZoneName'); - if (timeZoneNamePart && timeZoneNamePart.value) { const offsetString = timeZoneNamePart.value; const match = offsetString.match(/GMT([+-]\d{2})(?::?(\d{2}))?/); - if (match) { - const sign = match[1][0]; - const hours = match[1].slice(1).padStart(2, '0'); - const minutes = (match[2] || '00').padStart(2, '0'); - offset = `${sign}${hours}:${minutes}`; - } else if (offsetString === 'GMT') { - offset = '+00:00'; - } else { - // Fallback for time zones with names instead of offsets - offset = ''; - } + offset = match ? `${match[1][0]}${match[1].slice(1).padStart(2, '0')}:${(match[2] || '00').padStart(2, '0')}` : offsetString === 'GMT' ? '+00:00' : ''; } } - - // Build ISO 8601 string with time zone offset let isoString = `${year}-${month}-${day}T${hour}:${minute}:${second}`; if (milliseconds !== null) { isoString += `.${milliseconds.toString().padStart(3, '0')}`; } isoString += offset; - return isoString; - } - // For other types, use default string representation - return __printHogStringOutput(arg); -} - -""", - ["__isHogDate", "__isHogDateTime", "__printHogStringOutput"], + }} + """, + [], + ], + "__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 }""", @@ -735,19 +586,9 @@ 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: dt, zone: zone || 'UTC' }; } - return { - __hogDateTime__: true, - dt: timestamp, - zone: zone || 'UTC', - }; -} -""", + return { __hogDateTime__: true, dt: timestamp, zone: zone || 'UTC' }; }""", ["__isHogDate"], ], "__now": [ @@ -756,18 +597,11 @@ ], "__toUnixTimestamp": [ """function __toUnixTimestamp(input, zone) { - if (__isHogDateTime(input)) { - return input.dt; - } - if (__isHogDate(input)) { - return __toHogDateTime(input).dt; - } + 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); -}""", + if (isNaN(date.getTime())) { throw new Error('Invalid date input'); } + return Math.floor(date.getTime() / 1000);}""", ["__isHogDateTime", "__isHogDate", "__toHogDateTime"], ], "__fromUnixTimestamp": [ @@ -787,81 +621,38 @@ ["__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(), - }; -} -""", + """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', - }; -} -""", + """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'); - } - - // Convert timestamp to milliseconds + 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); - - // Use 'UTC' if no zone is specified - if (!zone) { - zone = 'UTC'; - } - - // Helper functions + 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())); @@ -871,14 +662,12 @@ 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' }), @@ -1006,28 +795,17 @@ """ 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'; - } + 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(', ')})`; - } + 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 (__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(', ')}}`; - } + 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); @@ -1178,7 +956,7 @@ def dfs(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 dependencies: + for dep in sorted(dependencies): dfs(dep) required_functions.add(func_name) @@ -1188,7 +966,7 @@ def dfs(func_name): # Build the dependency graph dependency_graph = {} - for func in required_functions: + for func in sorted(required_functions): _, dependencies = STL_FUNCTIONS[func] dependency_graph[func] = dependencies @@ -1204,13 +982,13 @@ def visit(node): if node in temp_mark: raise ValueError("Circular dependency detected") temp_mark.add(node) - for neighbor in graph.get(node, []): + for neighbor in sorted(graph.get(node, [])): visit(neighbor) temp_mark.remove(node) visited.add(node) result.append(node) - for node in graph: + for node in sorted(graph): visit(node) return result[::-1] # reverse the list to get correct order