@scriptc/runtime 0.0.1 → 0.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scriptc/runtime",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "scriptc native runtime — C sources, compiled into every scriptc binary",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scriptc.dev",
package/src/scr_runtime.h CHANGED
@@ -458,6 +458,16 @@ ScrStr *scr_regexp_escape(ScrStr *s);
458
458
  double scr_parse_int(ScrStr *s, double radix);
459
459
  double scr_parse_float(ScrStr *s);
460
460
 
461
+ /* ToNumber(string) — ECMA-262 7.1.4.1 StringToNumber exactly: trim
462
+ * StrWhiteSpace (the JS set, line terminators included) from both ends,
463
+ * empty/whitespace-only → +0, then the WHOLE remaining span must be one
464
+ * StrNumericLiteral — a signed decimal literal ("Infinity" exact-case
465
+ * included, correctly rounded via strtod over the validated span) or an
466
+ * UNSIGNED 0x/0o/0b integer literal (exact value rounded to nearest-even;
467
+ * a sign on those is NaN, per grammar). Any trailing garbage → NaN.
468
+ * Borrows s; never throws. */
469
+ double scr_string_to_number(ScrStr *s);
470
+
461
471
  /* encodeURIComponent — ECMA-262 Encode with the component unreserved set
462
472
  * (ALPHA/DIGIT/- _ . ! ~ * ' ( )): every other byte of the UTF-8 string
463
473
  * percent-encodes as uppercase %XX (the spec's per-code-point UTF-8
package/src/scr_string.c CHANGED
@@ -944,6 +944,94 @@ double scr_parse_float(ScrStr *s) {
944
944
  return r;
945
945
  }
946
946
 
947
+ /* ToNumber(string) — ECMA-262 7.1.4.1 StringToNumber, exactly. The
948
+ * grammar (StringNumericLiteral) differs from parseFloat's in all three
949
+ * directions: the WHOLE trimmed span must match (trailing garbage → NaN,
950
+ * where parseFloat keeps the longest prefix), the non-decimal 0x/0o/0b
951
+ * integer literals join (UNSIGNED only — a sign on them is NaN), and the
952
+ * empty/whitespace-only string is +0 (parseFloat: NaN). Decimal spans
953
+ * convert through strtod like parseFloat (copied first — strtod's own
954
+ * grammar is wider — inheriting correct rounding, the JSON precedent);
955
+ * 0x/0o/0b digits are the exact mathematical value rounded to nearest-
956
+ * even via scr_digits_to_double (u64 fast path, bignum beyond 2^64 —
957
+ * Node-exact for giant hex, Infinity overflow included: power-of-two
958
+ * radixes take the exact path, never V8's approximate chunk loop). */
959
+ double scr_string_to_number(ScrStr *s) {
960
+ const char *p = s->data;
961
+ size_t b = 0, e = s->len;
962
+ while (b < e) {
963
+ size_t adv;
964
+ uint32_t cp = scr_utf8_decode(p + b, &adv);
965
+ if (!scr_is_js_whitespace(cp)) break;
966
+ b += adv;
967
+ }
968
+ while (e > b) {
969
+ size_t cs = e - 1; /* back up to the lead byte of the last char */
970
+ while (cs > b && ((unsigned char)p[cs] & 0xC0) == 0x80) cs--;
971
+ size_t adv;
972
+ uint32_t cp = scr_utf8_decode(p + cs, &adv);
973
+ if (!scr_is_js_whitespace(cp)) break;
974
+ e = cs;
975
+ }
976
+ if (b == e) return 0.0; /* empty or all StrWhiteSpace */
977
+ p += b;
978
+ size_t n = e - b;
979
+ /* NonDecimalIntegerLiteral: 0x/0X, 0o/0O, 0b/0B — no sign admitted. */
980
+ if (n >= 2 && p[0] == '0' &&
981
+ (p[1] == 'x' || p[1] == 'X' || p[1] == 'o' || p[1] == 'O' ||
982
+ p[1] == 'b' || p[1] == 'B')) {
983
+ int radix = (p[1] == 'x' || p[1] == 'X') ? 16
984
+ : (p[1] == 'o' || p[1] == 'O') ? 8
985
+ : 2;
986
+ size_t i = 2, dig_start = 2;
987
+ while (i < n && scr_digit_value(p[i]) < radix) i++;
988
+ if (i == dig_start || i != n) return NAN; /* no digits, or garbage */
989
+ while (dig_start < i && p[dig_start] == '0') dig_start++;
990
+ if (dig_start == i) return 0.0;
991
+ return scr_digits_to_double(p + dig_start, i - dig_start, radix);
992
+ }
993
+ /* StrDecimalLiteral, whole-span: [+-]? (Infinity | digits [. digits*]
994
+ * | . digits) ([eE][+-]?digits)? — nothing before, nothing after. */
995
+ size_t i = 0;
996
+ double sign = 1.0;
997
+ if (p[0] == '+' || p[0] == '-') {
998
+ if (p[0] == '-') sign = -1.0;
999
+ i = 1;
1000
+ }
1001
+ if (n - i == 8 && memcmp(p + i, "Infinity", 8) == 0) {
1002
+ return sign * (double)INFINITY;
1003
+ }
1004
+ size_t int_digits = 0, frac_digits = 0;
1005
+ while (i < n && p[i] >= '0' && p[i] <= '9') {
1006
+ i++;
1007
+ int_digits++;
1008
+ }
1009
+ if (i < n && p[i] == '.') {
1010
+ i++;
1011
+ while (i < n && p[i] >= '0' && p[i] <= '9') {
1012
+ i++;
1013
+ frac_digits++;
1014
+ }
1015
+ }
1016
+ if (int_digits == 0 && frac_digits == 0) return NAN; /* ".", "+", "e5" */
1017
+ if (i < n && (p[i] == 'e' || p[i] == 'E')) {
1018
+ i++;
1019
+ if (i < n && (p[i] == '+' || p[i] == '-')) i++;
1020
+ size_t ed = i;
1021
+ while (i < n && p[i] >= '0' && p[i] <= '9') i++;
1022
+ if (i == ed) return NAN; /* "1e", "1e+" — exponent needs digits */
1023
+ }
1024
+ if (i != n) return NAN; /* trailing garbage ("1_000", "1.2.3", "12px") */
1025
+ char buf[64];
1026
+ char *tmp = n < sizeof(buf) ? buf : malloc(n + 1);
1027
+ if (!tmp) scr_oom();
1028
+ memcpy(tmp, p, n);
1029
+ tmp[n] = '\0';
1030
+ double r = strtod(tmp, NULL);
1031
+ if (tmp != buf) free(tmp);
1032
+ return r;
1033
+ }
1034
+
947
1035
  ScrStr *scr_f64_to_scrstr(double x) {
948
1036
  char buf[32];
949
1037
  size_t len = scr_f64_to_str(x, buf);