@soundor/expression 0.0.0-stage → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpp.mjs","names":[],"sources":["../../src/compiler/cpp.ts"],"sourcesContent":["/**\n * Compiles expressions to C++17: a C++ expression per tree, over helpers\n * ({@link cppRuntime}) that give it the language's semantics — the same as the\n * JavaScript compiler's, operation for operation. The output is plain C++ of\n * the standard library: no framework, no allocation except for strings, no\n * exceptions; reusable by any C++ plugin framework.\n *\n * Emit {@link CPP_PRAGMAS} at the top of the file and {@link CPP_INCLUDES},\n * then `cppRuntime()` in the namespace the compiled code is in. The pragmas\n * stop the compiler from fusing a multiplication and an addition (which\n * JavaScript never does), so both languages compute the same bits. Code\n * built with `-ffast-math` does not have these semantics.\n */\n\nimport type { ExpressionNode } from '../ast';\nimport { assertExpression, type ExpressionInputs } from '../check';\nimport {\n INTEGERS,\n baseType,\n isFloatType,\n isIntegerType,\n isNullableType,\n type ExpressionType,\n type IntegerType,\n type NumericType,\n} from '../types';\n\n/** The namespace {@link cppRuntime} declares, which compiled code calls. */\nexport const CPP_RUNTIME_NAMESPACE = 'soundor_expression';\n\n/** The headers compiled code and the runtime need. */\nexport const CPP_INCLUDES: readonly string[] = [\n '<cmath>',\n '<cstddef>',\n '<cstdint>',\n '<cstdio>',\n '<limits>',\n '<optional>',\n '<string>',\n '<string_view>',\n '<type_traits>',\n];\n\n/**\n * File-scope pragmas: no floating-point contraction in this file, and no\n * warning for comparing floats exactly, which the language defines.\n */\nexport const CPP_PRAGMAS = `#if defined(__clang__)\n#pragma clang fp contract(off)\n#pragma clang diagnostic ignored \"-Wfloat-equal\"\n#elif defined(__GNUC__)\n#pragma GCC optimize(\"fp-contract=off\")\n#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n#elif defined(_MSC_VER)\n#pragma fp_contract(off)\n#endif\n`;\n\nconst N = CPP_RUNTIME_NAMESPACE;\n\n/** The C++ type of an expression type. */\nexport function cppType(type: ExpressionType): string {\n if (isNullableType(type)) return `std::optional<${cppType(baseType(type))}>`;\n switch (type) {\n case 'boolean':\n return 'bool';\n case 'string':\n return 'std::string';\n case 'float32':\n return 'float';\n case 'float64':\n return 'double';\n default:\n return `std::${type}_t`;\n }\n}\n\n/** The helpers compiled code calls, in `namespace soundor_expression`. */\nexport function cppRuntime(): string {\n return `namespace ${N}\n{\n constexpr double pow10[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,\n 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 };\n\n // Integers wrap around (two's complement): computed modulo 2^64.\n template <class T> inline T iadd(T a, T b) noexcept { return static_cast<T>(static_cast<std::uint64_t>(a) + static_cast<std::uint64_t>(b)); }\n template <class T> inline T isub(T a, T b) noexcept { return static_cast<T>(static_cast<std::uint64_t>(a) - static_cast<std::uint64_t>(b)); }\n template <class T> inline T imul(T a, T b) noexcept { return static_cast<T>(static_cast<std::uint64_t>(a) * static_cast<std::uint64_t>(b)); }\n template <class T> inline T ineg(T a) noexcept { return static_cast<T>(std::uint64_t { 0 } - static_cast<std::uint64_t>(a)); }\n template <class T> inline T iabs(T a) noexcept\n {\n if constexpr (std::is_signed_v<T>)\n return a < 0 ? ineg(a) : a;\n else\n return a;\n }\n // Truncates toward zero; by zero gives 0, and the minimum by -1 wraps.\n template <class T> inline T idiv(T a, T b) noexcept\n {\n if (b == 0)\n return 0;\n if constexpr (std::is_signed_v<T>)\n if (a == std::numeric_limits<T>::min() && b == -1)\n return a;\n return static_cast<T>(a / b);\n }\n template <class T> inline T imin(T a, T b) noexcept { return b < a ? b : a; }\n template <class T> inline T imax(T a, T b) noexcept { return a < b ? b : a; }\n\n // JavaScript's Math.min and Math.max: NaN wins, -0 is below +0.\n template <class F> inline F fmin(F a, F b) noexcept\n {\n if (a != a || b != b)\n return std::numeric_limits<F>::quiet_NaN();\n if (a == b)\n return std::signbit(a) ? a : b;\n return a < b ? a : b;\n }\n template <class F> inline F fmax(F a, F b) noexcept\n {\n if (a != a || b != b)\n return std::numeric_limits<F>::quiet_NaN();\n if (a == b)\n return std::signbit(a) ? b : a;\n return a < b ? b : a;\n }\n\n // To the nearest binary32, overflowing to infinity (as Math.fround).\n inline float f32(double x) noexcept\n {\n constexpr double overflow = 3.4028235677973366e38; // halfway between FLT_MAX and 2^128\n if (x >= overflow)\n return std::numeric_limits<float>::infinity();\n if (x <= -overflow)\n return -std::numeric_limits<float>::infinity();\n return static_cast<float>(x);\n }\n template <class F> inline F narrow(double x) noexcept\n {\n if constexpr (std::is_same_v<F, float>)\n return f32(x);\n else\n return x;\n }\n template <class F> inline F fsqrt(F x) noexcept { return narrow<F>(std::sqrt(static_cast<double>(x))); }\n template <class F> inline F fexp(F x) noexcept { return narrow<F>(std::exp(static_cast<double>(x))); }\n template <class F> inline F flog(F x) noexcept { return narrow<F>(std::log(static_cast<double>(x))); }\n // Math.pow: NaN for a NaN exponent, and for ±1 to an infinite power.\n template <class F> inline F fpow(F a, F b) noexcept\n {\n const double x = a;\n const double y = b;\n if (y != y || ((x == 1.0 || x == -1.0) && std::isinf(y)))\n return std::numeric_limits<F>::quiet_NaN();\n return narrow<F>(std::pow(x, y));\n }\n\n // Toward zero, saturated, NaN to 0.\n template <class T, class F> inline T toInt(F value) noexcept\n {\n const double x = static_cast<double>(value);\n if (x != x)\n return 0;\n constexpr double low = static_cast<double>(std::numeric_limits<T>::min());\n constexpr double limit = std::is_signed_v<T> ? -low : 2.0 * (static_cast<double>(std::numeric_limits<T>::max() / 2) + 1.0);\n if (x <= low)\n return std::numeric_limits<T>::min();\n if (x >= limit)\n return std::numeric_limits<T>::max();\n return static_cast<T>(std::trunc(x));\n }\n\n inline std::string fixedFloat(double x, int digits)\n {\n if (x != x)\n return \"NaN\";\n if (std::isinf(x))\n return x < 0 ? \"-Infinity\" : \"Infinity\";\n const double scaled = x * pow10[digits];\n if (std::isinf(scaled))\n return x < 0 ? \"-Infinity\" : \"Infinity\";\n const double n = std::round(scaled);\n const double magnitude = std::fabs(n);\n std::string text;\n if (magnitude < 18446744073709551616.0)\n text = std::to_string(static_cast<unsigned long long>(magnitude));\n else\n {\n char buffer[512];\n std::snprintf(buffer, sizeof buffer, \"%.0f\", magnitude);\n text = buffer;\n }\n if (digits > 0)\n {\n const auto width = static_cast<std::size_t>(digits) + 1;\n if (text.size() < width)\n text.insert(0, width - text.size(), '0');\n text.insert(text.size() - static_cast<std::size_t>(digits), 1, '.');\n }\n if (n < 0)\n text.insert(0, 1, '-');\n return text;\n }\n\n template <class T> inline std::string fixedInt(T x, int digits)\n {\n std::string text = std::to_string(x);\n if (digits > 0)\n {\n text += '.';\n text.append(static_cast<std::size_t>(digits), '0');\n }\n return text;\n }\n\n inline bool isSpace(char c) noexcept { return c == ' ' || c == '\\\\t' || c == '\\\\n' || c == '\\\\r' || c == '\\\\f' || c == '\\\\v'; }\n inline bool isDigit(char c) noexcept { return c >= '0' && c <= '9'; }\n\n inline std::string_view trimView(std::string_view text) noexcept\n {\n while (! text.empty() && isSpace(text.front()))\n text.remove_prefix(1);\n while (! text.empty() && isSpace(text.back()))\n text.remove_suffix(1);\n return text;\n }\n inline std::string trim(std::string_view text) { return std::string(trimView(text)); }\n inline std::string lower(std::string text)\n {\n for (char& c : text)\n if (c >= 'A' && c <= 'Z')\n c = static_cast<char>(c - 'A' + 'a');\n return text;\n }\n inline bool endsWith(std::string_view text, std::string_view suffix) noexcept\n {\n return text.size() >= suffix.size() && text.substr(text.size() - suffix.size()) == suffix;\n }\n inline std::string removeSuffix(std::string_view text, std::string_view suffix)\n {\n return std::string(endsWith(text, suffix) ? text.substr(0, text.size() - suffix.size()) : text);\n }\n\n template <class T> inline std::optional<T> parseInt(std::string_view text) noexcept\n {\n text = trimView(text);\n std::size_t i = 0;\n bool negative = false;\n if (i < text.size() && (text[i] == '+' || text[i] == '-'))\n negative = text[i++] == '-';\n if (i == text.size())\n return std::nullopt;\n std::uint64_t magnitude = 0;\n bool overflow = false;\n for (; i < text.size(); ++i)\n {\n if (! isDigit(text[i]))\n return std::nullopt;\n const auto digit = static_cast<std::uint64_t>(text[i] - '0');\n if (magnitude > (std::numeric_limits<std::uint64_t>::max() - digit) / 10)\n overflow = true;\n else\n magnitude = magnitude * 10 + digit;\n }\n if (overflow)\n return std::nullopt;\n if (negative)\n {\n if constexpr (std::is_signed_v<T>)\n {\n const auto limit = static_cast<std::uint64_t>(-(std::numeric_limits<T>::min() + 1)) + 1;\n if (magnitude > limit)\n return std::nullopt;\n return static_cast<T>(std::uint64_t { 0 } - magnitude);\n }\n else\n {\n if (magnitude != 0)\n return std::nullopt;\n return T { 0 };\n }\n }\n if (magnitude > static_cast<std::uint64_t>(std::numeric_limits<T>::max()))\n return std::nullopt;\n return static_cast<T>(magnitude);\n }\n\n // Decimal notation to a double, by the same operations as the JavaScript\n // runtime (exact for up to 15 significant digits and exponents to ±22).\n inline std::optional<double> parseDecimal(std::string_view text) noexcept\n {\n text = trimView(text);\n std::size_t i = 0;\n bool negative = false;\n if (i < text.size() && (text[i] == '+' || text[i] == '-'))\n negative = text[i++] == '-';\n const std::size_t integerStart = i;\n while (i < text.size() && isDigit(text[i]))\n ++i;\n const std::string_view integerDigits = text.substr(integerStart, i - integerStart);\n std::string_view fractionDigits;\n if (i < text.size() && text[i] == '.')\n {\n const std::size_t fractionStart = ++i;\n while (i < text.size() && isDigit(text[i]))\n ++i;\n fractionDigits = text.substr(fractionStart, i - fractionStart);\n }\n if (integerDigits.empty() && fractionDigits.empty())\n return std::nullopt;\n long exponent = 0;\n if (i < text.size() && (text[i] == 'e' || text[i] == 'E'))\n {\n ++i;\n bool exponentNegative = false;\n if (i < text.size() && (text[i] == '+' || text[i] == '-'))\n exponentNegative = text[i++] == '-';\n const std::size_t exponentStart = i;\n while (i < text.size() && isDigit(text[i]))\n ++i;\n if (i == exponentStart)\n return std::nullopt;\n std::size_t first = exponentStart;\n while (first < i && text[first] == '0')\n ++first;\n if (i - first > 6)\n exponent = 100000;\n else\n for (std::size_t k = first; k < i; ++k)\n exponent = exponent * 10 + (text[k] - '0');\n if (exponentNegative)\n exponent = -exponent;\n }\n if (i != text.size())\n return std::nullopt;\n exponent -= static_cast<long>(fractionDigits.size());\n\n std::string digits;\n digits.reserve(integerDigits.size() + fractionDigits.size());\n digits.append(integerDigits);\n digits.append(fractionDigits);\n std::size_t start = 0;\n while (start < digits.size() && digits[start] == '0')\n ++start;\n std::size_t end = digits.size();\n double value = 0;\n if (start < end)\n {\n while (digits[end - 1] == '0')\n {\n --end;\n ++exponent;\n }\n if (end - start > 19)\n {\n exponent += static_cast<long>(end - start - 19);\n end = start + 19;\n }\n std::uint64_t mantissa = 0;\n for (std::size_t k = start; k < end; ++k)\n mantissa = mantissa * 10 + static_cast<std::uint64_t>(digits[k] - '0');\n value = static_cast<double>(mantissa);\n while (exponent > 22 && ! std::isinf(value))\n {\n value *= 1e22;\n exponent -= 22;\n }\n while (exponent < -22 && value != 0)\n {\n value /= 1e22;\n exponent += 22;\n }\n if (exponent > 22)\n value = std::numeric_limits<double>::infinity();\n else if (exponent < -22)\n value = 0;\n else\n value = exponent >= 0 ? value * pow10[exponent] : value / pow10[-exponent];\n }\n return negative ? -value : value;\n }\n\n template <class F> inline std::optional<F> parseFloat(std::string_view text) noexcept\n {\n const auto value = parseDecimal(text);\n if (! value)\n return std::nullopt;\n const F result = narrow<F>(*value);\n if (std::isinf(result))\n return std::nullopt;\n return result;\n }\n} // namespace ${N}\n`;\n}\n\nexport interface CompileCppOptions {\n /** The C++ name of each input; by default its name. */\n readonly names?: Readonly<Record<string, string>>;\n}\n\n/**\n * The C++ expression that computes `expression` from its `inputs` (of the C++\n * types {@link cppType} gives). Checks the tree first; throws\n * `ExpressionError` for an invalid one.\n */\nexport function compileCpp(\n expression: unknown,\n inputs: ExpressionInputs,\n options: CompileCppOptions = {},\n): string {\n assertExpression(expression, inputs);\n return emit(expression, (name) => {\n const identifier = options.names?.[name] ?? name;\n if (!/^[A-Za-z_][\\w]*$/.test(identifier))\n throw new TypeError(`'${identifier}' is not a C++ identifier`);\n return identifier;\n });\n}\n\n/** A C++ string literal of `value`'s UTF-8 bytes, as a `std::string`. */\nexport function cppStringLiteral(value: string): string {\n const bytes = new TextEncoder().encode(value);\n let body = '';\n for (const byte of bytes) {\n if (byte === 0x22) body += '\\\\\"';\n else if (byte === 0x5c) body += '\\\\\\\\';\n else if (byte === 0x3f) body += '\\\\?';\n else if (byte >= 0x20 && byte < 0x7f) body += String.fromCharCode(byte);\n else body += `\\\\${byte.toString(8).padStart(3, '0')}`;\n }\n return `\"${body}\"`;\n}\n\nfunction floatLiteral(type: 'float32' | 'float64', value: number): string {\n const number = type === 'float32' ? Math.fround(value) : value;\n let text = String(number);\n if (!/[.e]/.test(text)) text += '.0';\n if (type === 'float32') text += 'f';\n return number < 0 ? `(${text})` : text;\n}\n\nfunction integerLiteral(type: IntegerType, value: number | string): string {\n const integer = BigInt(value);\n if (type === 'uint64') return `std::uint64_t { ${integer}ULL }`;\n if (type === 'int64') {\n return integer === INTEGERS.int64.min\n ? '(-9223372036854775807LL - 1)'\n : `std::int64_t { ${integer}LL }`;\n }\n return `static_cast<${cppType(type)}>(${integer}LL)`;\n}\n\nfunction literal(\n type: ExpressionType,\n value: number | string | boolean | null,\n): string {\n if (value === null) return `${cppType(type)}()`;\n const base = baseType(type);\n if (base === 'string')\n return `std::string(${cppStringLiteral(value as string)}, ${new TextEncoder().encode(value as string).length})`;\n if (base === 'boolean') return value ? 'true' : 'false';\n if (isFloatType(base)) return floatLiteral(base, value as number);\n return integerLiteral(base as IntegerType, value as number | string);\n}\n\nconst COMPARISON: Record<string, string> = {\n eq: '==',\n ne: '!=',\n lt: '<',\n lte: '<=',\n gt: '>',\n gte: '>=',\n and: '&&',\n or: '||',\n};\n\nconst FLOAT_OPERATOR: Record<string, string> = {\n add: '+',\n sub: '-',\n mul: '*',\n div: '/',\n};\n\n/** Code of type `node.type`, as `as` when that is the nullable of it. */\nfunction as(code: string, from: ExpressionType, to: ExpressionType): string {\n return from === to ? code : `${cppType(to)}(${code})`;\n}\n\nfunction emit(node: ExpressionNode, name: (input: string) => string): string {\n const go = (child: ExpressionNode) => emit(child, name);\n switch (node.kind) {\n case 'literal':\n return literal(node.type, node.value);\n case 'input':\n return name(node.name);\n case 'variable':\n return `m_${node.name}`;\n case 'unary': {\n const a = go(node.operand);\n const type = node.operand.type;\n const t = cppType(type);\n switch (node.operator) {\n case 'not':\n return `(! ${a})`;\n case 'trim':\n return `${N}::trim(${a})`;\n case 'lower':\n return `${N}::lower(${a})`;\n case 'neg':\n return isIntegerType(type) ? `${N}::ineg<${t}>(${a})` : `(-${a})`;\n case 'abs':\n return isIntegerType(type)\n ? `${N}::iabs<${t}>(${a})`\n : `std::fabs(${a})`;\n case 'floor':\n case 'ceil':\n case 'round':\n return `static_cast<${t}>(std::${node.operator}(${a}))`;\n default:\n return `${N}::f${node.operator}<${t}>(${a})`;\n }\n }\n case 'binary': {\n const a = go(node.left);\n const b = go(node.right);\n const type = node.left.type;\n const t = cppType(type);\n switch (node.operator) {\n case 'endsWith':\n return `${N}::endsWith(${a}, ${b})`;\n case 'removeSuffix':\n return `${N}::removeSuffix(${a}, ${b})`;\n case 'pow':\n return `${N}::fpow<${t}>(${a}, ${b})`;\n case 'min':\n case 'max':\n return `${N}::${isIntegerType(type) ? 'i' : 'f'}${node.operator}<${t}>(${a}, ${b})`;\n case 'add':\n case 'sub':\n case 'mul':\n case 'div':\n if (isIntegerType(type))\n return `${N}::i${node.operator}<${t}>(${a}, ${b})`;\n return `static_cast<${t}>(${a} ${FLOAT_OPERATOR[node.operator]} ${b})`;\n default:\n return `(${a} ${COMPARISON[node.operator]} ${b})`;\n }\n }\n case 'if':\n return `(${go(node.condition)} ? ${as(go(node.then), node.then.type, node.type)} : ${as(go(node.else), node.else.type, node.type)})`;\n case 'convert':\n return convert(\n go(node.operand),\n node.operand.type as NumericType,\n node.type,\n );\n case 'fixed': {\n const a = go(node.operand);\n return isFloatType(node.operand.type)\n ? `${N}::fixedFloat(static_cast<double>(${a}), ${node.digits})`\n : `${N}::fixedInt(${a}, ${node.digits})`;\n }\n case 'concat':\n return node.operands.length === 1\n ? go(node.operands[0]!)\n : `(${node.operands.map(go).join(' + ')})`;\n case 'parse': {\n const type = baseType(node.type) as NumericType;\n return `${N}::parse${isFloatType(type) ? 'Float' : 'Int'}<${cppType(type)}>(${go(node.operand)})`;\n }\n case 'coalesce':\n return `(${go(node.operand)}).value_or(${go(node.fallback)})`;\n case 'isNull':\n return `(! (${go(node.operand)}).has_value())`;\n case 'let': {\n const inner = cppType(node.value.type);\n return `[&](const ${inner} m_${node.variable}) -> ${cppType(node.type)} { return ${go(node.body)}; }(${go(node.value)})`;\n }\n case 'map': {\n const inner = cppType(baseType(node.operand.type));\n return `[&](const std::optional<${inner}>& o_${node.variable}) -> ${cppType(node.type)} { if (! o_${node.variable}) return std::nullopt; const ${inner} m_${node.variable} = *o_${node.variable}; return ${go(node.body)}; }(${go(node.operand)})`;\n }\n }\n}\n\nfunction convert(code: string, from: NumericType, to: NumericType): string {\n if (from === to) return code;\n const target = cppType(to);\n if (to === 'float32') return `${N}::f32(static_cast<double>(${code}))`;\n if (to === 'float64') return `static_cast<double>(${code})`;\n if (isFloatType(from)) return `${N}::toInt<${target}>(${code})`;\n return `static_cast<${target}>(${code})`;\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Compiles expressions to JavaScript: a JavaScript expression per tree, over
3
+ * a small runtime ({@link JS_RUNTIME}) that the code it emits calls. The
4
+ * output depends on nothing else, so a generated module can carry it into a
5
+ * browser, a worker or a test environment.
6
+ *
7
+ * The semantics are the language's, shared with the C++ compiler: numbers of
8
+ * up to 32 bits and floats are JavaScript numbers, 64-bit integers bigints,
9
+ * `null` a nullable's absence. `float32` arithmetic is rounded to binary32
10
+ * after every operation (`Math.fround`), integers wrap.
11
+ */
12
+ import { r as ExpressionInputs } from "../check-DMbokSSO.mjs";
13
+ /** The name the emitted code calls the runtime by. */
14
+ export declare const JS_RUNTIME_NAME = "$x";
15
+ /**
16
+ * The runtime the emitted code calls, as JavaScript source that declares
17
+ * `const $x`. Emit it once in a module before compiled expressions.
18
+ */
19
+ export declare const JS_RUNTIME = "const $x = (() => {\n const P10 = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22];\n const SPACE = /^[ \\t\\n\\r\\f\\v]+|[ \\t\\n\\r\\f\\v]+$/g;\n const INTEGER = /^([+-]?)([0-9]+)$/;\n const DECIMAL = /^([+-]?)(?:([0-9]+)(?:\\.([0-9]*))?|\\.([0-9]+))(?:[eE]([+-]?[0-9]+))?$/;\n const round = (x) => (x < 0 ? -Math.round(-x) : Math.round(x));\n const trim = (s) => s.replace(SPACE, '');\n const decimal = (text) => {\n const match = DECIMAL.exec(trim(text));\n if (match === null) return null;\n const fraction = match[3] ?? match[4] ?? '';\n let digits = (match[2] ?? '') + fraction;\n const exponentText = match[5] ?? '0';\n let exponent = exponentText.replace(/^[+-]?0*/, '').length > 6 ? (exponentText[0] === '-' ? -100000 : 100000) : Number(exponentText);\n exponent -= fraction.length;\n digits = digits.replace(/^0+/, '');\n let value;\n if (digits === '') value = 0;\n else {\n const trailing = /0*$/.exec(digits)[0].length;\n digits = digits.slice(0, digits.length - trailing);\n exponent += trailing;\n if (digits.length > 19) {\n exponent += digits.length - 19;\n digits = digits.slice(0, 19);\n }\n value = Number(BigInt(digits));\n while (exponent > 22 && value !== Infinity) { value *= 1e22; exponent -= 22; }\n while (exponent < -22 && value !== 0) { value /= 1e22; exponent += 22; }\n if (exponent > 22) value = Infinity;\n else if (exponent < -22) value = 0;\n else value = exponent >= 0 ? value * P10[exponent] : value / P10[-exponent];\n }\n return match[1] === '-' ? -value : value;\n };\n return {\n f32: Math.fround,\n round,\n trim,\n lower: (s) => s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32)),\n removeSuffix: (s, suffix) => (s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s),\n w8: (x) => (x << 24) >> 24,\n wu8: (x) => x & 0xff,\n w16: (x) => (x << 16) >> 16,\n wu16: (x) => x & 0xffff,\n w32: (x) => x | 0,\n wu32: (x) => x >>> 0,\n i64: (x) => BigInt.asIntN(64, x),\n u64: (x) => BigInt.asUintN(64, x),\n idiv: (a, b) => (b === 0 ? 0 : Math.trunc(a / b)),\n bdiv: (a, b) => (b === 0n ? 0n : a / b),\n bmin: (a, b) => (a < b ? a : b),\n bmax: (a, b) => (a > b ? a : b),\n toInt: (x, lo, hi) => (x !== x ? 0 : x <= lo ? lo : x >= hi + 1 ? hi : Math.trunc(x) + 0),\n toBig: (x, lo, hi, limit) => (x !== x ? 0n : x <= Number(lo) ? lo : x >= limit ? hi : BigInt(Math.trunc(x))),\n fixed: (x, digits) => {\n if (x !== x) return 'NaN';\n if (x === Infinity || x === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';\n const scaled = x * P10[digits];\n if (scaled === Infinity || scaled === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';\n const n = round(scaled);\n let text = BigInt(Math.abs(n)).toString();\n if (digits > 0) {\n text = text.padStart(digits + 1, '0');\n text = text.slice(0, text.length - digits) + '.' + text.slice(text.length - digits);\n }\n return n < 0 ? '-' + text : text;\n },\n fixedInt: (x, digits) => String(x) + (digits > 0 ? '.' + '0'.repeat(digits) : ''),\n parseInt: (text, lo, hi, big) => {\n const match = INTEGER.exec(trim(text));\n if (match === null) return null;\n const value = BigInt(match[1] + match[2]);\n if (value < lo || value > hi) return null;\n return big ? value : Number(value);\n },\n parseFloat: (text, single) => {\n let value = decimal(text);\n if (value === null) return null;\n if (single) value = Math.fround(value);\n return value === Infinity || value === -Infinity ? null : value;\n },\n };\n})();\n";
20
+ export interface CompileJsOptions {
21
+ /** The JavaScript identifier of each input; by default its name. */
22
+ readonly names?: Readonly<Record<string, string>>;
23
+ }
24
+ /**
25
+ * The JavaScript expression that computes `expression` from its `inputs`.
26
+ * Checks the tree first; throws `ExpressionError` for an invalid one.
27
+ */
28
+ export declare function compileJs(expression: unknown, inputs: ExpressionInputs, options?: CompileJsOptions): string;
29
+ /**
30
+ * A function that evaluates `expression`, its arguments the inputs in the
31
+ * order of `inputs`' keys. Builds it with `Function`, so where code
32
+ * generation is not allowed (a strict CSP), emit a module with
33
+ * {@link compileJs} instead.
34
+ */
35
+ export declare function compileJsFunction(expression: unknown, inputs: ExpressionInputs): (...values: unknown[]) => unknown;
36
+ //# sourceMappingURL=js.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js.d.mts","names":[],"sources":["../../src/compiler/js.ts"],"mappings":";;;;;;;;;;;;;qBA0Ba;;;;;qBAMA;iBAsFI;;WAEN,QAAQ,SAAS;;;;;;wBAOZ,UACd,qBACA,QAAQ,kBACR,UAAS;;;;;;;wBAiBK,kBACd,qBACA,QAAQ,uBACH"}
@@ -0,0 +1,258 @@
1
+ import { _ as isFloatType, a as assertExpression, h as isBigIntType, m as baseType, u as INTEGERS, v as isIntegerType } from "../check-Db6XRMc8.mjs";
2
+ // src/compiler/js.ts
3
+ /** The name the emitted code calls the runtime by. */
4
+ const JS_RUNTIME_NAME = "$x";
5
+ /**
6
+ * The runtime the emitted code calls, as JavaScript source that declares
7
+ * `const $x`. Emit it once in a module before compiled expressions.
8
+ */
9
+ const JS_RUNTIME = `const \$x = (() => {
10
+ const P10 = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22];
11
+ const SPACE = /^[ \\t\\n\\r\\f\\v]+|[ \\t\\n\\r\\f\\v]+$/g;
12
+ const INTEGER = /^([+-]?)([0-9]+)$/;
13
+ const DECIMAL = /^([+-]?)(?:([0-9]+)(?:\\.([0-9]*))?|\\.([0-9]+))(?:[eE]([+-]?[0-9]+))?$/;
14
+ const round = (x) => (x < 0 ? -Math.round(-x) : Math.round(x));
15
+ const trim = (s) => s.replace(SPACE, '');
16
+ const decimal = (text) => {
17
+ const match = DECIMAL.exec(trim(text));
18
+ if (match === null) return null;
19
+ const fraction = match[3] ?? match[4] ?? '';
20
+ let digits = (match[2] ?? '') + fraction;
21
+ const exponentText = match[5] ?? '0';
22
+ let exponent = exponentText.replace(/^[+-]?0*/, '').length > 6 ? (exponentText[0] === '-' ? -100000 : 100000) : Number(exponentText);
23
+ exponent -= fraction.length;
24
+ digits = digits.replace(/^0+/, '');
25
+ let value;
26
+ if (digits === '') value = 0;
27
+ else {
28
+ const trailing = /0*$/.exec(digits)[0].length;
29
+ digits = digits.slice(0, digits.length - trailing);
30
+ exponent += trailing;
31
+ if (digits.length > 19) {
32
+ exponent += digits.length - 19;
33
+ digits = digits.slice(0, 19);
34
+ }
35
+ value = Number(BigInt(digits));
36
+ while (exponent > 22 && value !== Infinity) { value *= 1e22; exponent -= 22; }
37
+ while (exponent < -22 && value !== 0) { value /= 1e22; exponent += 22; }
38
+ if (exponent > 22) value = Infinity;
39
+ else if (exponent < -22) value = 0;
40
+ else value = exponent >= 0 ? value * P10[exponent] : value / P10[-exponent];
41
+ }
42
+ return match[1] === '-' ? -value : value;
43
+ };
44
+ return {
45
+ f32: Math.fround,
46
+ round,
47
+ trim,
48
+ lower: (s) => s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32)),
49
+ removeSuffix: (s, suffix) => (s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s),
50
+ w8: (x) => (x << 24) >> 24,
51
+ wu8: (x) => x & 0xff,
52
+ w16: (x) => (x << 16) >> 16,
53
+ wu16: (x) => x & 0xffff,
54
+ w32: (x) => x | 0,
55
+ wu32: (x) => x >>> 0,
56
+ i64: (x) => BigInt.asIntN(64, x),
57
+ u64: (x) => BigInt.asUintN(64, x),
58
+ idiv: (a, b) => (b === 0 ? 0 : Math.trunc(a / b)),
59
+ bdiv: (a, b) => (b === 0n ? 0n : a / b),
60
+ bmin: (a, b) => (a < b ? a : b),
61
+ bmax: (a, b) => (a > b ? a : b),
62
+ toInt: (x, lo, hi) => (x !== x ? 0 : x <= lo ? lo : x >= hi + 1 ? hi : Math.trunc(x) + 0),
63
+ toBig: (x, lo, hi, limit) => (x !== x ? 0n : x <= Number(lo) ? lo : x >= limit ? hi : BigInt(Math.trunc(x))),
64
+ fixed: (x, digits) => {
65
+ if (x !== x) return 'NaN';
66
+ if (x === Infinity || x === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';
67
+ const scaled = x * P10[digits];
68
+ if (scaled === Infinity || scaled === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';
69
+ const n = round(scaled);
70
+ let text = BigInt(Math.abs(n)).toString();
71
+ if (digits > 0) {
72
+ text = text.padStart(digits + 1, '0');
73
+ text = text.slice(0, text.length - digits) + '.' + text.slice(text.length - digits);
74
+ }
75
+ return n < 0 ? '-' + text : text;
76
+ },
77
+ fixedInt: (x, digits) => String(x) + (digits > 0 ? '.' + '0'.repeat(digits) : ''),
78
+ parseInt: (text, lo, hi, big) => {
79
+ const match = INTEGER.exec(trim(text));
80
+ if (match === null) return null;
81
+ const value = BigInt(match[1] + match[2]);
82
+ if (value < lo || value > hi) return null;
83
+ return big ? value : Number(value);
84
+ },
85
+ parseFloat: (text, single) => {
86
+ let value = decimal(text);
87
+ if (value === null) return null;
88
+ if (single) value = Math.fround(value);
89
+ return value === Infinity || value === -Infinity ? null : value;
90
+ },
91
+ };
92
+ })();
93
+ `;
94
+ /**
95
+ * The JavaScript expression that computes `expression` from its `inputs`.
96
+ * Checks the tree first; throws `ExpressionError` for an invalid one.
97
+ */
98
+ function compileJs(expression, inputs, options = {}) {
99
+ assertExpression(expression, inputs);
100
+ return emit(expression, (name) => {
101
+ const identifier = options.names?.[name] ?? name;
102
+ if (!/^[A-Za-z_$][\w$]*$/.test(identifier)) throw new TypeError(`'${identifier}' is not a JavaScript identifier`);
103
+ return identifier;
104
+ });
105
+ }
106
+ /**
107
+ * A function that evaluates `expression`, its arguments the inputs in the
108
+ * order of `inputs`' keys. Builds it with `Function`, so where code
109
+ * generation is not allowed (a strict CSP), emit a module with
110
+ * {@link compileJs} instead.
111
+ */
112
+ function compileJsFunction(expression, inputs) {
113
+ const names = Object.keys(inputs).map((_, index) => `a${index}`);
114
+ const body = compileJs(expression, inputs, { names: Object.fromEntries(Object.keys(inputs).map((name, index) => [name, names[index]])) });
115
+ return new Function(`${JS_RUNTIME}return (${names.join(", ")}) => ${body};`)();
116
+ }
117
+ const R = "$x";
118
+ const WRAP = {
119
+ int8: "w8",
120
+ uint8: "wu8",
121
+ int16: "w16",
122
+ uint16: "wu16",
123
+ int32: "w32",
124
+ uint32: "wu32"
125
+ };
126
+ /** Wraps a JavaScript value of an integer computation into its type. */
127
+ function wrap(type, code) {
128
+ if (type === "int64") return `${R}.i64(${code})`;
129
+ if (type === "uint64") return `${R}.u64(${code})`;
130
+ return `${R}.${WRAP[type]}(${code})`;
131
+ }
132
+ function literal(type, value) {
133
+ if (value === null) return "null";
134
+ const base = baseType(type);
135
+ if (base === "string") return JSON.stringify(value);
136
+ if (base === "boolean") return value ? "true" : "false";
137
+ if (isBigIntType(base)) return `${String(value)}n`;
138
+ const number = base === "float32" ? Math.fround(value) : value;
139
+ return number < 0 ? `(${String(number)})` : String(number);
140
+ }
141
+ function emit(node, name) {
142
+ const go = (child) => emit(child, name);
143
+ switch (node.kind) {
144
+ case "literal": return literal(node.type, node.value);
145
+ case "input": return name(node.name);
146
+ case "variable": return `$m${node.name}`;
147
+ case "unary": {
148
+ const a = go(node.operand);
149
+ const type = node.operand.type;
150
+ switch (node.operator) {
151
+ case "not": return `(!${a})`;
152
+ case "trim": return `${R}.trim(${a})`;
153
+ case "lower": return `${R}.lower(${a})`;
154
+ case "neg":
155
+ if (isIntegerType(type)) return wrap(type, `-${a}`);
156
+ return `(-${a})`;
157
+ case "abs":
158
+ if (type === "int64") return `${R}.i64(${a} < 0n ? -${a} : ${a})`;
159
+ if (type === "uint64") return a;
160
+ if (isIntegerType(type)) return wrap(type, `Math.abs(${a})`);
161
+ return `Math.abs(${a})`;
162
+ case "round": return `${R}.round(${a})`;
163
+ default: {
164
+ const fn = `Math.${node.operator}(${a})`;
165
+ return type === "float32" ? `${R}.f32(${fn})` : fn;
166
+ }
167
+ }
168
+ }
169
+ case "binary": {
170
+ const a = go(node.left);
171
+ const b = go(node.right);
172
+ const type = node.left.type;
173
+ const op = BINARY_JS[node.operator];
174
+ switch (node.operator) {
175
+ case "endsWith": return `${a}.endsWith(${b})`;
176
+ case "removeSuffix": return `${R}.removeSuffix(${a}, ${b})`;
177
+ case "eq":
178
+ case "ne":
179
+ case "lt":
180
+ case "lte":
181
+ case "gt":
182
+ case "gte":
183
+ case "and":
184
+ case "or": return `(${a} ${op} ${b})`;
185
+ case "pow": return type === "float32" ? `${R}.f32(Math.pow(${a}, ${b}))` : `Math.pow(${a}, ${b})`;
186
+ case "min":
187
+ case "max":
188
+ if (isBigIntType(type)) return `${R}.b${node.operator}(${a}, ${b})`;
189
+ return `Math.${node.operator}(${a}, ${b})`;
190
+ }
191
+ if (isFloatType(type)) {
192
+ const code = `(${a} ${op} ${b})`;
193
+ return type === "float32" ? `${R}.f32${code}` : code;
194
+ }
195
+ const integer = type;
196
+ if (isBigIntType(integer)) {
197
+ if (node.operator === "div") return wrap(integer, `${R}.bdiv(${a}, ${b})`);
198
+ return wrap(integer, `${a} ${op} ${b}`);
199
+ }
200
+ if (node.operator === "div") return wrap(integer, `${R}.idiv(${a}, ${b})`);
201
+ if (node.operator === "mul" && INTEGERS[integer].bits === 32) return wrap(integer, `Math.imul(${a}, ${b})`);
202
+ return wrap(integer, `${a} ${op} ${b}`);
203
+ }
204
+ case "if": return `(${go(node.condition)} ? ${go(node.then)} : ${go(node.else)})`;
205
+ case "convert": return convert(go(node.operand), node.operand.type, node.type);
206
+ case "fixed": {
207
+ const a = go(node.operand);
208
+ return isFloatType(node.operand.type) ? `${R}.fixed(${a}, ${node.digits})` : `${R}.fixedInt(${a}, ${node.digits})`;
209
+ }
210
+ case "concat": return node.operands.length === 1 ? go(node.operands[0]) : `(${node.operands.map(go).join(" + ")})`;
211
+ case "parse": {
212
+ const a = go(node.operand);
213
+ const type = baseType(node.type);
214
+ if (isFloatType(type)) return `${R}.parseFloat(${a}, ${type === "float32"})`;
215
+ const { min, max } = INTEGERS[type];
216
+ return `${R}.parseInt(${a}, ${min}n, ${max}n, ${isBigIntType(type)})`;
217
+ }
218
+ case "coalesce": return `(${go(node.operand)} ?? ${go(node.fallback)})`;
219
+ case "isNull": return `(${go(node.operand)} === null)`;
220
+ case "let": return `((\$m${node.variable}) => ${go(node.body)})(${go(node.value)})`;
221
+ case "map": {
222
+ const variable = `$m${node.variable}`;
223
+ return `((${variable}) => ${variable} === null ? null : ${go(node.body)})(${go(node.operand)})`;
224
+ }
225
+ }
226
+ }
227
+ const BINARY_JS = {
228
+ add: "+",
229
+ sub: "-",
230
+ mul: "*",
231
+ div: "/",
232
+ eq: "===",
233
+ ne: "!==",
234
+ lt: "<",
235
+ lte: "<=",
236
+ gt: ">",
237
+ gte: ">=",
238
+ and: "&&",
239
+ or: "||"
240
+ };
241
+ function convert(code, from, to) {
242
+ if (from === to) return code;
243
+ if (isFloatType(to)) {
244
+ const number = isBigIntType(from) ? `Number(${code})` : code;
245
+ return to === "float32" ? `${R}.f32(${number})` : number;
246
+ }
247
+ const { min, max, bits, signed } = INTEGERS[to];
248
+ if (isFloatType(from)) {
249
+ if (isBigIntType(to)) return `${R}.toBig(${code}, ${min}n, ${max}n, ${2 ** (signed ? bits - 1 : bits)})`;
250
+ return `${R}.toInt(${code}, ${min}, ${max})`;
251
+ }
252
+ if (isBigIntType(to)) return wrap(to, isBigIntType(from) ? code : `BigInt(${code})`);
253
+ if (isBigIntType(from)) return `Number(BigInt.as${signed ? "Int" : "Uint"}N(${bits}, ${code}))`;
254
+ return wrap(to, code);
255
+ }
256
+ export { JS_RUNTIME, JS_RUNTIME_NAME, compileJs, compileJsFunction };
257
+
258
+ //# sourceMappingURL=js.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"js.mjs","names":[],"sources":["../../src/compiler/js.ts"],"sourcesContent":["/**\n * Compiles expressions to JavaScript: a JavaScript expression per tree, over\n * a small runtime ({@link JS_RUNTIME}) that the code it emits calls. The\n * output depends on nothing else, so a generated module can carry it into a\n * browser, a worker or a test environment.\n *\n * The semantics are the language's, shared with the C++ compiler: numbers of\n * up to 32 bits and floats are JavaScript numbers, 64-bit integers bigints,\n * `null` a nullable's absence. `float32` arithmetic is rounded to binary32\n * after every operation (`Math.fround`), integers wrap.\n */\n\nimport type { ExpressionNode } from '../ast';\nimport { assertExpression, type ExpressionInputs } from '../check';\nimport {\n INTEGERS,\n baseType,\n isBigIntType,\n isFloatType,\n isIntegerType,\n type ExpressionType,\n type IntegerType,\n type NumericType,\n} from '../types';\n\n/** The name the emitted code calls the runtime by. */\nexport const JS_RUNTIME_NAME = '$x';\n\n/**\n * The runtime the emitted code calls, as JavaScript source that declares\n * `const $x`. Emit it once in a module before compiled expressions.\n */\nexport const JS_RUNTIME = `const ${JS_RUNTIME_NAME} = (() => {\n const P10 = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22];\n const SPACE = /^[ \\\\t\\\\n\\\\r\\\\f\\\\v]+|[ \\\\t\\\\n\\\\r\\\\f\\\\v]+$/g;\n const INTEGER = /^([+-]?)([0-9]+)$/;\n const DECIMAL = /^([+-]?)(?:([0-9]+)(?:\\\\.([0-9]*))?|\\\\.([0-9]+))(?:[eE]([+-]?[0-9]+))?$/;\n const round = (x) => (x < 0 ? -Math.round(-x) : Math.round(x));\n const trim = (s) => s.replace(SPACE, '');\n const decimal = (text) => {\n const match = DECIMAL.exec(trim(text));\n if (match === null) return null;\n const fraction = match[3] ?? match[4] ?? '';\n let digits = (match[2] ?? '') + fraction;\n const exponentText = match[5] ?? '0';\n let exponent = exponentText.replace(/^[+-]?0*/, '').length > 6 ? (exponentText[0] === '-' ? -100000 : 100000) : Number(exponentText);\n exponent -= fraction.length;\n digits = digits.replace(/^0+/, '');\n let value;\n if (digits === '') value = 0;\n else {\n const trailing = /0*$/.exec(digits)[0].length;\n digits = digits.slice(0, digits.length - trailing);\n exponent += trailing;\n if (digits.length > 19) {\n exponent += digits.length - 19;\n digits = digits.slice(0, 19);\n }\n value = Number(BigInt(digits));\n while (exponent > 22 && value !== Infinity) { value *= 1e22; exponent -= 22; }\n while (exponent < -22 && value !== 0) { value /= 1e22; exponent += 22; }\n if (exponent > 22) value = Infinity;\n else if (exponent < -22) value = 0;\n else value = exponent >= 0 ? value * P10[exponent] : value / P10[-exponent];\n }\n return match[1] === '-' ? -value : value;\n };\n return {\n f32: Math.fround,\n round,\n trim,\n lower: (s) => s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32)),\n removeSuffix: (s, suffix) => (s.endsWith(suffix) ? s.slice(0, s.length - suffix.length) : s),\n w8: (x) => (x << 24) >> 24,\n wu8: (x) => x & 0xff,\n w16: (x) => (x << 16) >> 16,\n wu16: (x) => x & 0xffff,\n w32: (x) => x | 0,\n wu32: (x) => x >>> 0,\n i64: (x) => BigInt.asIntN(64, x),\n u64: (x) => BigInt.asUintN(64, x),\n idiv: (a, b) => (b === 0 ? 0 : Math.trunc(a / b)),\n bdiv: (a, b) => (b === 0n ? 0n : a / b),\n bmin: (a, b) => (a < b ? a : b),\n bmax: (a, b) => (a > b ? a : b),\n toInt: (x, lo, hi) => (x !== x ? 0 : x <= lo ? lo : x >= hi + 1 ? hi : Math.trunc(x) + 0),\n toBig: (x, lo, hi, limit) => (x !== x ? 0n : x <= Number(lo) ? lo : x >= limit ? hi : BigInt(Math.trunc(x))),\n fixed: (x, digits) => {\n if (x !== x) return 'NaN';\n if (x === Infinity || x === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';\n const scaled = x * P10[digits];\n if (scaled === Infinity || scaled === -Infinity) return x < 0 ? '-Infinity' : 'Infinity';\n const n = round(scaled);\n let text = BigInt(Math.abs(n)).toString();\n if (digits > 0) {\n text = text.padStart(digits + 1, '0');\n text = text.slice(0, text.length - digits) + '.' + text.slice(text.length - digits);\n }\n return n < 0 ? '-' + text : text;\n },\n fixedInt: (x, digits) => String(x) + (digits > 0 ? '.' + '0'.repeat(digits) : ''),\n parseInt: (text, lo, hi, big) => {\n const match = INTEGER.exec(trim(text));\n if (match === null) return null;\n const value = BigInt(match[1] + match[2]);\n if (value < lo || value > hi) return null;\n return big ? value : Number(value);\n },\n parseFloat: (text, single) => {\n let value = decimal(text);\n if (value === null) return null;\n if (single) value = Math.fround(value);\n return value === Infinity || value === -Infinity ? null : value;\n },\n };\n})();\n`;\n\nexport interface CompileJsOptions {\n /** The JavaScript identifier of each input; by default its name. */\n readonly names?: Readonly<Record<string, string>>;\n}\n\n/**\n * The JavaScript expression that computes `expression` from its `inputs`.\n * Checks the tree first; throws `ExpressionError` for an invalid one.\n */\nexport function compileJs(\n expression: unknown,\n inputs: ExpressionInputs,\n options: CompileJsOptions = {},\n): string {\n assertExpression(expression, inputs);\n return emit(expression, (name) => {\n const identifier = options.names?.[name] ?? name;\n if (!/^[A-Za-z_$][\\w$]*$/.test(identifier))\n throw new TypeError(`'${identifier}' is not a JavaScript identifier`);\n return identifier;\n });\n}\n\n/**\n * A function that evaluates `expression`, its arguments the inputs in the\n * order of `inputs`' keys. Builds it with `Function`, so where code\n * generation is not allowed (a strict CSP), emit a module with\n * {@link compileJs} instead.\n */\nexport function compileJsFunction(\n expression: unknown,\n inputs: ExpressionInputs,\n): (...values: unknown[]) => unknown {\n const names = Object.keys(inputs).map((_, index) => `a${index}`);\n const body = compileJs(expression, inputs, {\n names: Object.fromEntries(\n Object.keys(inputs).map((name, index) => [name, names[index]!]),\n ),\n });\n return new Function(\n `${JS_RUNTIME}return (${names.join(', ')}) => ${body};`,\n )() as (...values: unknown[]) => unknown;\n}\n\nconst R = JS_RUNTIME_NAME;\n\nconst WRAP: Record<Exclude<IntegerType, 'int64' | 'uint64'>, string> = {\n int8: 'w8',\n uint8: 'wu8',\n int16: 'w16',\n uint16: 'wu16',\n int32: 'w32',\n uint32: 'wu32',\n};\n\n/** Wraps a JavaScript value of an integer computation into its type. */\nfunction wrap(type: IntegerType, code: string): string {\n if (type === 'int64') return `${R}.i64(${code})`;\n if (type === 'uint64') return `${R}.u64(${code})`;\n return `${R}.${WRAP[type]}(${code})`;\n}\n\nfunction literal(\n type: ExpressionType,\n value: number | string | boolean | null,\n): string {\n if (value === null) return 'null';\n const base = baseType(type);\n if (base === 'string') return JSON.stringify(value);\n if (base === 'boolean') return value ? 'true' : 'false';\n if (isBigIntType(base)) return `${String(value)}n`;\n const number =\n base === 'float32' ? Math.fround(value as number) : (value as number);\n return number < 0 ? `(${String(number)})` : String(number);\n}\n\nfunction emit(node: ExpressionNode, name: (input: string) => string): string {\n const go = (child: ExpressionNode) => emit(child, name);\n switch (node.kind) {\n case 'literal':\n return literal(node.type, node.value);\n case 'input':\n return name(node.name);\n case 'variable':\n return `$m${node.name}`;\n case 'unary': {\n const a = go(node.operand);\n const type = node.operand.type;\n switch (node.operator) {\n case 'not':\n return `(!${a})`;\n case 'trim':\n return `${R}.trim(${a})`;\n case 'lower':\n return `${R}.lower(${a})`;\n case 'neg':\n if (isIntegerType(type)) return wrap(type, `-${a}`);\n return `(-${a})`;\n case 'abs':\n if (type === 'int64') return `${R}.i64(${a} < 0n ? -${a} : ${a})`;\n if (type === 'uint64') return a;\n if (isIntegerType(type)) return wrap(type, `Math.abs(${a})`);\n return `Math.abs(${a})`;\n case 'round':\n return `${R}.round(${a})`;\n default: {\n const fn = `Math.${node.operator}(${a})`;\n return type === 'float32' ? `${R}.f32(${fn})` : fn;\n }\n }\n }\n case 'binary': {\n const a = go(node.left);\n const b = go(node.right);\n const type = node.left.type;\n const op = BINARY_JS[node.operator];\n switch (node.operator) {\n case 'endsWith':\n return `${a}.endsWith(${b})`;\n case 'removeSuffix':\n return `${R}.removeSuffix(${a}, ${b})`;\n case 'eq':\n case 'ne':\n case 'lt':\n case 'lte':\n case 'gt':\n case 'gte':\n case 'and':\n case 'or':\n return `(${a} ${op} ${b})`;\n case 'pow':\n return type === 'float32'\n ? `${R}.f32(Math.pow(${a}, ${b}))`\n : `Math.pow(${a}, ${b})`;\n case 'min':\n case 'max':\n if (isBigIntType(type)) return `${R}.b${node.operator}(${a}, ${b})`;\n return `Math.${node.operator}(${a}, ${b})`;\n default:\n break;\n }\n if (isFloatType(type)) {\n const code = `(${a} ${op} ${b})`;\n return type === 'float32' ? `${R}.f32${code}` : code;\n }\n const integer = type as IntegerType;\n if (isBigIntType(integer)) {\n if (node.operator === 'div')\n return wrap(integer, `${R}.bdiv(${a}, ${b})`);\n return wrap(integer, `${a} ${op} ${b}`);\n }\n if (node.operator === 'div')\n return wrap(integer, `${R}.idiv(${a}, ${b})`);\n if (node.operator === 'mul' && INTEGERS[integer].bits === 32)\n return wrap(integer, `Math.imul(${a}, ${b})`);\n return wrap(integer, `${a} ${op} ${b}`);\n }\n case 'if':\n return `(${go(node.condition)} ? ${go(node.then)} : ${go(node.else)})`;\n case 'convert':\n return convert(\n go(node.operand),\n node.operand.type as NumericType,\n node.type,\n );\n case 'fixed': {\n const a = go(node.operand);\n return isFloatType(node.operand.type)\n ? `${R}.fixed(${a}, ${node.digits})`\n : `${R}.fixedInt(${a}, ${node.digits})`;\n }\n case 'concat':\n return node.operands.length === 1\n ? go(node.operands[0]!)\n : `(${node.operands.map(go).join(' + ')})`;\n case 'parse': {\n const a = go(node.operand);\n const type = baseType(node.type) as NumericType;\n if (isFloatType(type))\n return `${R}.parseFloat(${a}, ${type === 'float32'})`;\n const { min, max } = INTEGERS[type];\n return `${R}.parseInt(${a}, ${min}n, ${max}n, ${isBigIntType(type)})`;\n }\n case 'coalesce':\n return `(${go(node.operand)} ?? ${go(node.fallback)})`;\n case 'isNull':\n return `(${go(node.operand)} === null)`;\n case 'let':\n return `((${'$m'}${node.variable}) => ${go(node.body)})(${go(node.value)})`;\n case 'map': {\n const variable = `$m${node.variable}`;\n return `((${variable}) => ${variable} === null ? null : ${go(node.body)})(${go(node.operand)})`;\n }\n }\n}\n\nconst BINARY_JS: Record<string, string> = {\n add: '+',\n sub: '-',\n mul: '*',\n div: '/',\n eq: '===',\n ne: '!==',\n lt: '<',\n lte: '<=',\n gt: '>',\n gte: '>=',\n and: '&&',\n or: '||',\n};\n\nfunction convert(code: string, from: NumericType, to: NumericType): string {\n if (from === to) return code;\n if (isFloatType(to)) {\n const number = isBigIntType(from) ? `Number(${code})` : code;\n return to === 'float32' ? `${R}.f32(${number})` : number;\n }\n const { min, max, bits, signed } = INTEGERS[to];\n if (isFloatType(from)) {\n if (isBigIntType(to))\n return `${R}.toBig(${code}, ${min}n, ${max}n, ${2 ** (signed ? bits - 1 : bits)})`;\n return `${R}.toInt(${code}, ${min}, ${max})`;\n }\n // Integer to integer: two's complement wrap-around.\n if (isBigIntType(to))\n return wrap(to, isBigIntType(from) ? code : `BigInt(${code})`);\n if (isBigIntType(from))\n return `Number(BigInt.as${signed ? 'Int' : 'Uint'}N(${bits}, ${code}))`;\n return wrap(to, code);\n}\n"],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Soundor's expression language: small, statically typed, deterministic and
3
+ * portable. An expression is a JSON-serializable tree ({@link Expression}),
4
+ * written with the typed builder and compiled to C++
5
+ * (`@soundor/expression/compiler/cpp`) and JavaScript
6
+ * (`@soundor/expression/compiler/js`) with the same semantics.
7
+ */
8
+ import { $ as isNullableType, A as VariableNode, B as IntegerType, C as LetNode, D as UNARY_OPERATORS, E as ParseNode, F as FLOAT_TYPES, G as NumericType, H as LiteralValue, I as FloatType, J as baseType, K as SCALAR_TYPES, L as INTEGERS, M as BaseType, N as BigIntType, O as UnaryNode, P as ExpressionType, Q as isIntegerType, R as INTEGER_TYPES, S as IsNullNode, T as MapNode, U as NUMERIC_TYPES, V as JsValue, W as NullableType, X as isExpressionType, Y as isBigIntType, Z as isFloatType, _ as ExpressionKind, a as MAX_DEPTH, b as IfNode, c as assertExpression, d as BinaryNode, et as isNumericType, f as BinaryOperator, g as Expression, h as ConvertNode, i as ExpressionIssue, j as isExpression, k as UnaryOperator, l as checkExpression, m as ConcatNode, n as ExpressionError, o as MAX_FIXED_DIGITS, p as CoalesceNode, q as ScalarType, r as ExpressionInputs, s as MAX_NODES, t as CheckOptions, u as BINARY_OPERATORS, v as ExpressionNode, w as LiteralNode, x as InputNode, y as FixedNode, z as IntegerInfo } from "./check-DMbokSSO.mjs";
9
+ /** A literal of a numeric type: a number, or a bigint for 64-bit integers. */
10
+ type NumericLiteral<T extends NumericType> = T extends BigIntType ? bigint : number;
11
+ /** An operand of type `T`: an expression, or a literal of it. */
12
+ type Operand<T extends NumericType> = Expression<T> | NumericLiteral<T>;
13
+ /** A string operand: an expression or a plain string. */
14
+ type Text = Expression<'string'> | string;
15
+ /** The type `map` gives: the body's type, nullable. */
16
+ type MapResult<U extends NumericType | NullableType> = `${BaseType<U> & NumericType}?`;
17
+ /** The types a branch may have when the other is an `A`: `A`, or a number and its nullable. */
18
+ type IfBranch<A extends ExpressionType> = A | (BaseType<A> extends NumericType ? BaseType<A> | `${BaseType<A>}?` : never);
19
+ /** The type `if` gives for two branch types. */
20
+ type IfResult<A extends ExpressionType, B extends ExpressionType> = [A] extends [B] ? A : BaseType<A> extends BaseType<B> ? BaseType<B> extends BaseType<A> ? `${BaseType<A> & NumericType}?` : never : never;
21
+ /** Functions that build expressions. */
22
+ interface ExpressionBuilder {
23
+ int8(value: number): Expression<'int8'>;
24
+ uint8(value: number): Expression<'uint8'>;
25
+ int16(value: number): Expression<'int16'>;
26
+ uint16(value: number): Expression<'uint16'>;
27
+ int32(value: number): Expression<'int32'>;
28
+ uint32(value: number): Expression<'uint32'>;
29
+ int64(value: bigint): Expression<'int64'>;
30
+ uint64(value: bigint): Expression<'uint64'>;
31
+ float32(value: number): Expression<'float32'>;
32
+ float64(value: number): Expression<'float64'>;
33
+ boolean(value: boolean): Expression<'boolean'>;
34
+ string(value: string): Expression<'string'>;
35
+ /** A literal of any scalar type. */
36
+ literal<T extends ScalarType>(type: T, value: LiteralValue<T>): Expression<T>;
37
+ /** `null`, of a nullable number type. */
38
+ null<T extends NumericType>(type: T): Expression<`${T}?`>;
39
+ /** `a + b`. Integers wrap around (two's complement); floats round to their type. */
40
+ add<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
41
+ add<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
42
+ sub<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
43
+ sub<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
44
+ mul<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
45
+ mul<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
46
+ /** `a / b`. Integers truncate toward zero, and an integer divided by zero is 0. */
47
+ div<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
48
+ div<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
49
+ /** The smaller; NaN if either is NaN, and -0 is less than +0. */
50
+ min<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
51
+ min<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
52
+ max<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
53
+ max<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
54
+ /** `a` to the power `b`. */
55
+ pow<T extends FloatType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<T>;
56
+ pow<T extends FloatType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<T>;
57
+ neg<T extends NumericType>(a: Expression<T>): Expression<T>;
58
+ abs<T extends NumericType>(a: Expression<T>): Expression<T>;
59
+ sqrt<T extends FloatType>(a: Expression<T>): Expression<T>;
60
+ /** e to the power `a`. */
61
+ exp<T extends FloatType>(a: Expression<T>): Expression<T>;
62
+ /** The natural logarithm. */
63
+ log<T extends FloatType>(a: Expression<T>): Expression<T>;
64
+ floor<T extends FloatType>(a: Expression<T>): Expression<T>;
65
+ ceil<T extends FloatType>(a: Expression<T>): Expression<T>;
66
+ /** To the nearest integer; halves away from zero (2.5 → 3, -2.5 → -3). */
67
+ round<T extends FloatType>(a: Expression<T>): Expression<T>;
68
+ /**
69
+ * The number as another numeric type. To an integer: truncated toward
70
+ * zero, saturated at the type's bounds, NaN → 0. Between integers: wrapped
71
+ * (two's complement). To a float: the nearest value.
72
+ */
73
+ convert<T extends NumericType>(a: Expression<NumericType>, type: T): Expression<T>;
74
+ eq<T extends ScalarType>(a: Expression<T>, b: NoInfer<Expression<T> | LiteralValue<T>>): Expression<'boolean'>;
75
+ ne<T extends ScalarType>(a: Expression<T>, b: NoInfer<Expression<T> | LiteralValue<T>>): Expression<'boolean'>;
76
+ lt<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<'boolean'>;
77
+ lt<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<'boolean'>;
78
+ lte<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<'boolean'>;
79
+ lte<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<'boolean'>;
80
+ gt<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<'boolean'>;
81
+ gt<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<'boolean'>;
82
+ gte<T extends NumericType>(a: Expression<T>, b: NoInfer<Operand<T>>): Expression<'boolean'>;
83
+ gte<T extends NumericType>(a: NumericLiteral<NoInfer<T>>, b: Expression<T>): Expression<'boolean'>;
84
+ and(...operands: [Expression<'boolean'>, ...Expression<'boolean'>[]]): Expression<'boolean'>;
85
+ or(...operands: [Expression<'boolean'>, ...Expression<'boolean'>[]]): Expression<'boolean'>;
86
+ not(a: Expression<'boolean'>): Expression<'boolean'>;
87
+ /** `then` when `condition` holds, else `otherwise`. A number and its nullable join into the nullable. */
88
+ if(condition: Expression<'boolean'>, then: Text, otherwise: Text): Expression<'string'>;
89
+ if<A extends ExpressionType, B extends IfBranch<A>>(condition: Expression<'boolean'>, then: Expression<A>, otherwise: Expression<B>): Expression<IfResult<A, B>>;
90
+ /**
91
+ * `a` in decimal with exactly `digits` decimals (0 to 15), rounded half
92
+ * away from zero; never a negative zero ("-0.00" is "0.00").
93
+ */
94
+ fixed(a: Expression<NumericType>, digits: number): Expression<'string'>;
95
+ concat(...parts: [Text, ...Text[]]): Expression<'string'>;
96
+ /** Without leading and trailing ASCII whitespace. */
97
+ trim(a: Text): Expression<'string'>;
98
+ /** With ASCII letters in lower case. */
99
+ lower(a: Text): Expression<'string'>;
100
+ endsWith(a: Text, suffix: Text): Expression<'boolean'>;
101
+ /** `a` without `suffix` when it ends with it; `a` otherwise. */
102
+ removeSuffix(a: Text, suffix: Text): Expression<'string'>;
103
+ /**
104
+ * Text as a number of `type`, or null when it is not one: decimal
105
+ * notation (`-12`, `0.5`, `1e3`; integers without a fraction or exponent),
106
+ * surrounding ASCII whitespace ignored. An integer outside its type, or a
107
+ * float outside its finite range, is null.
108
+ */
109
+ parse<T extends NumericType>(text: Text, type: T): Expression<`${T}?`>;
110
+ parseInt8(text: Text): Expression<'int8?'>;
111
+ parseUint8(text: Text): Expression<'uint8?'>;
112
+ parseInt16(text: Text): Expression<'int16?'>;
113
+ parseUint16(text: Text): Expression<'uint16?'>;
114
+ parseInt32(text: Text): Expression<'int32?'>;
115
+ parseUint32(text: Text): Expression<'uint32?'>;
116
+ parseInt64(text: Text): Expression<'int64?'>;
117
+ parseUint64(text: Text): Expression<'uint64?'>;
118
+ parseFloat32(text: Text): Expression<'float32?'>;
119
+ parseFloat64(text: Text): Expression<'float64?'>;
120
+ isNull(a: Expression<NullableType>): Expression<'boolean'>;
121
+ /** The value of `a`, or `fallback` when it is null. */
122
+ coalesce<T extends NumericType>(a: Expression<`${T}?`>, fallback: NoInfer<Operand<T>>): Expression<T>;
123
+ /** `body` of `value`, computed once however often the body uses it. */
124
+ let<T extends ExpressionType, U extends ExpressionType>(value: Expression<T>, body: (value: Expression<T>) => Expression<U>): Expression<U>;
125
+ /** null when `a` is null; otherwise `body` of its value. */
126
+ map<T extends NumericType, U extends NumericType | NullableType>(a: Expression<`${T}?`>, body: (value: Expression<T>) => Expression<U>): Expression<MapResult<U>>;
127
+ }
128
+ /** A builder with a context's inputs: `e.value`, `e.text`. */
129
+ type ExpressionContext<I extends Readonly<Record<string, ExpressionType>>> = ExpressionBuilder & { readonly [K in keyof I]: Expression<I[K]>; };
130
+ /** The builder, without inputs. */
131
+ export declare const expressions: ExpressionBuilder;
132
+ /** An input of an expression context, such as `value`. */
133
+ export declare function input<T extends ExpressionType>(name: string, type: T): Expression<T>;
134
+ /**
135
+ * The builder with the context's inputs as properties: what a callback such
136
+ * as `format: (e) => …` receives.
137
+ */
138
+ export declare function createContext<const I extends Readonly<Record<string, ExpressionType>>>(inputs: I): ExpressionContext<I>;
139
+ /**
140
+ * `node` with each input named in `replacements` replaced by that
141
+ * expression, of the same type. The variables `node` binds are renamed (with
142
+ * `suffix`), so they never capture a variable of a replacement.
143
+ */
144
+ export declare function substituteInputs<T extends Expression>(node: T, replacements: Readonly<Record<string, Expression>>, suffix?: string): T;
145
+ export { BINARY_OPERATORS, type BaseType, type BigIntType, type BinaryNode, type BinaryOperator, type CheckOptions, type CoalesceNode, type ConcatNode, type ConvertNode, type Expression, type ExpressionBuilder, type ExpressionContext, ExpressionError, type ExpressionInputs, type ExpressionIssue, type ExpressionKind, type ExpressionNode, type ExpressionType, FLOAT_TYPES, type FixedNode, type FloatType, INTEGERS, INTEGER_TYPES, type IfBranch, type IfNode, type IfResult, type InputNode, type IntegerInfo, type IntegerType, type IsNullNode, type JsValue, type LetNode, type LiteralNode, type LiteralValue, MAX_DEPTH, MAX_FIXED_DIGITS, MAX_NODES, type MapNode, type MapResult, NUMERIC_TYPES, type NullableType, type NumericLiteral, type NumericType, type Operand, type ParseNode, SCALAR_TYPES, type ScalarType, type Text, UNARY_OPERATORS, type UnaryNode, type UnaryOperator, type VariableNode, assertExpression, baseType, checkExpression, isBigIntType, isExpression, isExpressionType, isFloatType, isIntegerType, isNullableType, isNumericType };
146
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/builder.ts","../src/substitute.ts"],"mappings":"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"}