@supercmd/calculator 1.0.3 → 1.0.4

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.
package/dist/index.js CHANGED
@@ -1883,12 +1883,31 @@ function tryDateIntent(input) {
1883
1883
  }
1884
1884
  return null;
1885
1885
  }
1886
- var CONVERSION_PATTERN = /^([\d.,]+)?\s*([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)$/i;
1886
+ var CONVERSION_PATTERN = /^([\d.,]+[kKmMbB]?)?\s*([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)$/i;
1887
+ function parseAmountWithSuffix(str) {
1888
+ if (!str) return 1;
1889
+ const m = str.match(/^([\d.,]+)([kKmMbB]?)$/);
1890
+ if (!m) return parseFloat(str) || 1;
1891
+ let numStr = m[1];
1892
+ const suffix = m[2].toLowerCase();
1893
+ if (numStr.includes(",")) {
1894
+ const lastComma = numStr.lastIndexOf(",");
1895
+ const afterComma = numStr.slice(lastComma + 1);
1896
+ if (afterComma.length <= 2 && !numStr.includes(".")) {
1897
+ numStr = numStr.slice(0, lastComma) + "." + afterComma;
1898
+ } else {
1899
+ numStr = numStr.replace(/,/g, "");
1900
+ }
1901
+ }
1902
+ const base = parseFloat(numStr);
1903
+ const multipliers = { k: 1e3, m: 1e6, b: 1e9 };
1904
+ return isNaN(base) ? 1 : base * (multipliers[suffix] ?? 1);
1905
+ }
1887
1906
  function tryCurrencyOrCryptoIntent(input) {
1888
1907
  const normalized = normalizeConversionInput(input);
1889
1908
  const match = normalized.match(CONVERSION_PATTERN);
1890
1909
  if (!match) return null;
1891
- const amount = match[1] ? parseFloat(match[1].replace(/,/g, "")) : 1;
1910
+ const amount = parseAmountWithSuffix(match[1]);
1892
1911
  const fromToken = match[2].trim();
1893
1912
  const toToken = match[3].trim();
1894
1913
  const fromCrypto = resolveCrypto(fromToken);
@@ -1906,13 +1925,13 @@ function tryCurrencyOrCryptoIntent(input) {
1906
1925
  }
1907
1926
  return null;
1908
1927
  }
1909
- var UNIT_PATTERN = /^(-?[\d.,]+)\s*([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)$/i;
1910
- var UNIT_PATTERN_NO_SPACE = /^(-?[\d.,]+)([a-zA-Z°]+)\s+(?:to|in|into|as)\s+([a-zA-Z°]+(?:\s+[a-zA-Z]+)?)$/i;
1928
+ var UNIT_PATTERN = /^(-?[\d.,]+[kKmMbB]?)\s*([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)$/i;
1929
+ var UNIT_PATTERN_NO_SPACE = /^(-?[\d.,]+[kKmMbB]?)([a-zA-Z°]+)\s+(?:to|in|into|as)\s+([a-zA-Z°]+(?:\s+[a-zA-Z]+)?)$/i;
1911
1930
  function tryUnitIntent(input) {
1912
1931
  const normalized = normalizeConversionInput(input);
1913
1932
  const match = normalized.match(UNIT_PATTERN) || normalized.match(UNIT_PATTERN_NO_SPACE);
1914
1933
  if (!match) return null;
1915
- const amount = parseFloat(match[1].replace(/,/g, ""));
1934
+ const amount = parseAmountWithSuffix(match[1]);
1916
1935
  const fromToken = match[2].trim().toLowerCase();
1917
1936
  const toToken = match[3].trim().toLowerCase();
1918
1937
  const fromUnit = lookupUnit(fromToken);
@@ -2199,6 +2218,16 @@ var Parser = class {
2199
2218
  if (this.pos < this.expr.length && /[+-]/.test(this.expr[this.pos])) this.pos++;
2200
2219
  while (this.pos < this.expr.length && /[0-9]/.test(this.expr[this.pos])) this.pos++;
2201
2220
  }
2221
+ const numEnd = this.pos;
2222
+ if (/[kKmMbB]/.test(this.charAt(this.pos)) && !/[a-zA-Z0-9_]/.test(this.charAt(this.pos + 1))) {
2223
+ const suffix = this.expr[this.pos].toLowerCase();
2224
+ this.pos++;
2225
+ const multipliers = { k: 1e3, m: 1e6, b: 1e9 };
2226
+ const numStr2 = this.expr.slice(start, numEnd);
2227
+ const num2 = parseFloat(numStr2);
2228
+ if (isNaN(num2)) throw new Error(`Invalid number: '${numStr2}'`);
2229
+ return num2 * multipliers[suffix];
2230
+ }
2202
2231
  const numStr = this.expr.slice(start, this.pos);
2203
2232
  const num = parseFloat(numStr);
2204
2233
  if (isNaN(num)) throw new Error(`Invalid number: '${numStr}'`);
@@ -2303,6 +2332,25 @@ function evaluateMath(expression, locale = "en-US", precision = 10) {
2303
2332
  };
2304
2333
  }
2305
2334
  }
2335
+ function normalizeEuropeanDecimalSeparators(expr) {
2336
+ let result = "";
2337
+ let depth = 0;
2338
+ for (let i = 0; i < expr.length; i++) {
2339
+ const ch = expr[i];
2340
+ if (ch === "(") depth++;
2341
+ else if (ch === ")") depth--;
2342
+ else if (ch === "," && depth === 0) {
2343
+ const prevIsDigit = i > 0 && /[0-9]/.test(expr[i - 1]);
2344
+ const nextIsDigit = i < expr.length - 1 && /[0-9]/.test(expr[i + 1]);
2345
+ if (prevIsDigit && nextIsDigit) {
2346
+ result += ".";
2347
+ continue;
2348
+ }
2349
+ }
2350
+ result += ch;
2351
+ }
2352
+ return result;
2353
+ }
2306
2354
  function normalizeMathExpression(expression) {
2307
2355
  let normalized = expression.trim().replace(/\s+/g, " ");
2308
2356
  normalized = normalized.replace(/^(?:(?:what(?:'s|s| is))\s+the\s+|(?:what(?:'s|s| is))\s+|calculate\s+)/i, "");
@@ -2336,6 +2384,7 @@ function normalizeMathExpression(expression) {
2336
2384
  }
2337
2385
  }
2338
2386
  normalized = normalized.replace(/\bdivided by\b/gi, "/").replace(/\btimes\b/gi, "*").replace(/\bplus\b/gi, "+").replace(/\bminus\b/gi, "-");
2387
+ normalized = normalizeEuropeanDecimalSeparators(normalized);
2339
2388
  return normalized.replace(/\s+/g, " ").trim();
2340
2389
  }
2341
2390
 
package/dist/index.mjs CHANGED
@@ -1846,12 +1846,31 @@ function tryDateIntent(input) {
1846
1846
  }
1847
1847
  return null;
1848
1848
  }
1849
- var CONVERSION_PATTERN = /^([\d.,]+)?\s*([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)$/i;
1849
+ var CONVERSION_PATTERN = /^([\d.,]+[kKmMbB]?)?\s*([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z$€£¥₹₩₽₺₦₵₪฿]+(?:\s+[a-zA-Z]+)?)$/i;
1850
+ function parseAmountWithSuffix(str) {
1851
+ if (!str) return 1;
1852
+ const m = str.match(/^([\d.,]+)([kKmMbB]?)$/);
1853
+ if (!m) return parseFloat(str) || 1;
1854
+ let numStr = m[1];
1855
+ const suffix = m[2].toLowerCase();
1856
+ if (numStr.includes(",")) {
1857
+ const lastComma = numStr.lastIndexOf(",");
1858
+ const afterComma = numStr.slice(lastComma + 1);
1859
+ if (afterComma.length <= 2 && !numStr.includes(".")) {
1860
+ numStr = numStr.slice(0, lastComma) + "." + afterComma;
1861
+ } else {
1862
+ numStr = numStr.replace(/,/g, "");
1863
+ }
1864
+ }
1865
+ const base = parseFloat(numStr);
1866
+ const multipliers = { k: 1e3, m: 1e6, b: 1e9 };
1867
+ return isNaN(base) ? 1 : base * (multipliers[suffix] ?? 1);
1868
+ }
1850
1869
  function tryCurrencyOrCryptoIntent(input) {
1851
1870
  const normalized = normalizeConversionInput(input);
1852
1871
  const match = normalized.match(CONVERSION_PATTERN);
1853
1872
  if (!match) return null;
1854
- const amount = match[1] ? parseFloat(match[1].replace(/,/g, "")) : 1;
1873
+ const amount = parseAmountWithSuffix(match[1]);
1855
1874
  const fromToken = match[2].trim();
1856
1875
  const toToken = match[3].trim();
1857
1876
  const fromCrypto = resolveCrypto(fromToken);
@@ -1869,13 +1888,13 @@ function tryCurrencyOrCryptoIntent(input) {
1869
1888
  }
1870
1889
  return null;
1871
1890
  }
1872
- var UNIT_PATTERN = /^(-?[\d.,]+)\s*([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)$/i;
1873
- var UNIT_PATTERN_NO_SPACE = /^(-?[\d.,]+)([a-zA-Z°]+)\s+(?:to|in|into|as)\s+([a-zA-Z°]+(?:\s+[a-zA-Z]+)?)$/i;
1891
+ var UNIT_PATTERN = /^(-?[\d.,]+[kKmMbB]?)\s*([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)\s+(?:to|in|into|as|=)\s+([a-zA-Z°/µμ'"²³]+(?:\s+[a-zA-Z]+(?:\s+[a-zA-Z]+)?)?)$/i;
1892
+ var UNIT_PATTERN_NO_SPACE = /^(-?[\d.,]+[kKmMbB]?)([a-zA-Z°]+)\s+(?:to|in|into|as)\s+([a-zA-Z°]+(?:\s+[a-zA-Z]+)?)$/i;
1874
1893
  function tryUnitIntent(input) {
1875
1894
  const normalized = normalizeConversionInput(input);
1876
1895
  const match = normalized.match(UNIT_PATTERN) || normalized.match(UNIT_PATTERN_NO_SPACE);
1877
1896
  if (!match) return null;
1878
- const amount = parseFloat(match[1].replace(/,/g, ""));
1897
+ const amount = parseAmountWithSuffix(match[1]);
1879
1898
  const fromToken = match[2].trim().toLowerCase();
1880
1899
  const toToken = match[3].trim().toLowerCase();
1881
1900
  const fromUnit = lookupUnit(fromToken);
@@ -2162,6 +2181,16 @@ var Parser = class {
2162
2181
  if (this.pos < this.expr.length && /[+-]/.test(this.expr[this.pos])) this.pos++;
2163
2182
  while (this.pos < this.expr.length && /[0-9]/.test(this.expr[this.pos])) this.pos++;
2164
2183
  }
2184
+ const numEnd = this.pos;
2185
+ if (/[kKmMbB]/.test(this.charAt(this.pos)) && !/[a-zA-Z0-9_]/.test(this.charAt(this.pos + 1))) {
2186
+ const suffix = this.expr[this.pos].toLowerCase();
2187
+ this.pos++;
2188
+ const multipliers = { k: 1e3, m: 1e6, b: 1e9 };
2189
+ const numStr2 = this.expr.slice(start, numEnd);
2190
+ const num2 = parseFloat(numStr2);
2191
+ if (isNaN(num2)) throw new Error(`Invalid number: '${numStr2}'`);
2192
+ return num2 * multipliers[suffix];
2193
+ }
2165
2194
  const numStr = this.expr.slice(start, this.pos);
2166
2195
  const num = parseFloat(numStr);
2167
2196
  if (isNaN(num)) throw new Error(`Invalid number: '${numStr}'`);
@@ -2266,6 +2295,25 @@ function evaluateMath(expression, locale = "en-US", precision = 10) {
2266
2295
  };
2267
2296
  }
2268
2297
  }
2298
+ function normalizeEuropeanDecimalSeparators(expr) {
2299
+ let result = "";
2300
+ let depth = 0;
2301
+ for (let i = 0; i < expr.length; i++) {
2302
+ const ch = expr[i];
2303
+ if (ch === "(") depth++;
2304
+ else if (ch === ")") depth--;
2305
+ else if (ch === "," && depth === 0) {
2306
+ const prevIsDigit = i > 0 && /[0-9]/.test(expr[i - 1]);
2307
+ const nextIsDigit = i < expr.length - 1 && /[0-9]/.test(expr[i + 1]);
2308
+ if (prevIsDigit && nextIsDigit) {
2309
+ result += ".";
2310
+ continue;
2311
+ }
2312
+ }
2313
+ result += ch;
2314
+ }
2315
+ return result;
2316
+ }
2269
2317
  function normalizeMathExpression(expression) {
2270
2318
  let normalized = expression.trim().replace(/\s+/g, " ");
2271
2319
  normalized = normalized.replace(/^(?:(?:what(?:'s|s| is))\s+the\s+|(?:what(?:'s|s| is))\s+|calculate\s+)/i, "");
@@ -2299,6 +2347,7 @@ function normalizeMathExpression(expression) {
2299
2347
  }
2300
2348
  }
2301
2349
  normalized = normalized.replace(/\bdivided by\b/gi, "/").replace(/\btimes\b/gi, "*").replace(/\bplus\b/gi, "+").replace(/\bminus\b/gi, "-");
2350
+ normalized = normalizeEuropeanDecimalSeparators(normalized);
2302
2351
  return normalized.replace(/\s+/g, " ").trim();
2303
2352
  }
2304
2353
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supercmd/calculator",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Powerful natural language calculator — math, units, currency, crypto, time zones, dates",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",