camada 0.2.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d3fbef4ecb9e7381ff18ff7f00298f155db47809898b7a2f62ee2d0a3d8d90f
4
- data.tar.gz: 25e2f5efe68f77b02d8903d04b59752ea25587c1ac9834c930ea28d24c14eb98
3
+ metadata.gz: 6ff3ec1e73a6da51f20af06916fb4438fe50fc9fa87a9023445e90561c1a7cf8
4
+ data.tar.gz: d9d10163bdb349345ccb3a65666966624640fef0352d4727f848485abc0ea734
5
5
  SHA512:
6
- metadata.gz: d2f3c3bb076396b25ced5e13ad2371b53709cbb8c73f7a48fb6553ebdb4e576736b88ca22731fc18c2abbd45cddc65b43861eb99f5073f8bb94af7f92b23b6e3
7
- data.tar.gz: 61947d1b869a9b5ea49fdaca8e8c8e62c66d9677c52889756a369fae54356e51927bc04767fa9b0c250a3bf128bb24d07971ddef7aecb8a5c6936ac3d8a40259
6
+ metadata.gz: 42beca1c8a2d5299814aa3b09963e21963e0e25344238e70cf087d19d484feb7cd91dbaae8930ec0c5236eb77c6906c5f9de1b5cd265704798061a99fae40040
7
+ data.tar.gz: 79148a9dd5427db583f916520b64726a6baede851368a2677a55e846ed6f4ed83500ec020544b6ba90c263ee189d42acd8c11a52f8f1a2a713d908d7d867476f
@@ -163,7 +163,8 @@ module Camada
163
163
  # "ff", and an ASCII-only pattern does the same once Ruby recompiles it for a non-ASCII value. Extra matches only, so a rule hits a
164
164
  # value JS would not and never misses one; splitting every literal run would slow string search (an accepted gap, camada-all-wdii).
165
165
  # Inside a lookbehind js_to_onigmo splits adjacent literals (camada-all-ccjo) so it stays fixed-length, which stops ss folding to ß,
166
- # but a lone ß or ff there still matches "SS" / "FF".
166
+ # and reads a multi-fold char (ß, ff, İ) case-sensitively, as JS matches it: Onigmo there reads it as its fold alone (ß as "ss",
167
+ # never "ß"; Ruby 3.3.13 and 3.4.12 do the same for ff and İ, camada-all-ozix).
167
168
  # A backreference JS and Onigmo read differently is rewritten or, when it cannot be, fails open (scan_groups).
168
169
  # A `(?` or `(*` group JS lacks (`(?i)`, `(?>`, `(*FAIL)`, ...) fails open too: JS rejects it or V8 mis-evaluates it (camada-all-lsl8).
169
170
  # Only a UTF-8 or ASCII pattern is walked: snapshot patterns arrive as UTF-8 JSON, and the binary walk
@@ -366,9 +367,11 @@ module Camada
366
367
  # Under IGNORECASE Onigmo folds several chars at once (`ss` ~ ß, and a positive class holding
367
368
  # `\S` gains such folds), which makes a lookbehind variable-length and uncompilable; JS folds one
368
369
  # char at a time. So inside a lookbehind adjacent literal chars are split with `(?:)` (`ab` -> `a(?:)b`; a
369
- # lone literal stays a plain string node, which Onigmo can still expand for a multi-char fold) and a positive
370
+ # lone literal stays a plain string node) and a positive
370
371
  # class with `\S` (or `\W`/`\D`) becomes `(?:[^WS]|[rest])`, and a closed one-char class counts as a literal for
371
- # that split (camada-all-ccjo, camada-all-fe1r).
372
+ # that split (camada-all-ccjo, camada-all-fe1r). A char whose fold is several chars (ß, ff, İ; raw, `\u00df` or `\ß`)
373
+ # becomes `(?-i:ß)` there, pulled out of a positive class like `\S` unless a `-` is beside it (camada-all-ozix):
374
+ # JS /i matches it only to itself, and Onigmo would read it as its fold alone (version-dependent).
372
375
  # In a class `\c` is a control char only before a letter, digit or `_` (ClassControlLetter); before
373
376
  # anything else (`[\c*]`) it is a literal `\` then `c` (camada-all-mqcb).
374
377
  # A `{m,n}` bound above Onigmo's 100000 is clamped: m to 100000, and an n above it drops (`{m,}`,
@@ -396,10 +399,17 @@ module Camada
396
399
  lbs = [false] # per open group, the root first: inside a lookbehind (camada-all-ccjo)
397
400
  lit_end = -1 # the pattern index right after the last literal emitted inside a lookbehind
398
401
  class_start = nil # the out index of an open positive class's `[` inside a lookbehind
399
- class_alts = [] # the `\S`/`\W`/`\D` that class held
402
+ class_alts = [] # the `\S`/`\W`/`\D` and multi-fold chars that class held
400
403
  last_named = {} # name -> the index of its last opener
401
404
  while i < n
402
405
  ch = pattern[i]
406
+ # in a lookbehind (positive class too, unless a range end) a multi-fold char becomes `(?-i:c)` (camada-all-ozix)
407
+ if lbs.last && (!in_class || class_start) && (w = multi_fold(pattern, i)) &&
408
+ !(in_class && (pattern[i - 1] == "-" || pattern[i + w[1]] == "-"))
409
+ in_class ? class_alts << "(?-i:#{w[0]})" : out << "(?-i:#{w[0]})"
410
+ i += w[1]
411
+ next
412
+ end
403
413
  if ch == "\\" && i + 1 < n
404
414
  if !in_class && (r = plan[i])
405
415
  if r[1] == :empty
@@ -614,6 +624,27 @@ module Camada
614
624
  str.encoding == Encoding::BINARY ? str : str.b
615
625
  end
616
626
 
627
+ # [its text, its length] when the char at i (raw UTF-8, `\uXXXX` or an identity escape `\ß`) has a case fold of
628
+ # several chars (ß, İ, ff: 104 of them), else nil. JS /i without `u` matches each of them only to itself.
629
+ def self.multi_fold(pattern, i)
630
+ if pattern[i] == "\\"
631
+ return (w = multi_fold(pattern, i + 1)) && [w[0], w[1] + 1] if pattern.getbyte(i + 1).to_i >= 0x80
632
+
633
+ m = pattern.match(/\G\\u(\h{4})/n, i)
634
+ return nil unless m && !(cp = m[1].hex).between?(0xD800, 0xDFFF)
635
+
636
+ c = cp.chr(Encoding::UTF_8)
637
+ len = 6
638
+ else
639
+ b = pattern.getbyte(i)
640
+ return nil unless b.between?(0xC2, 0xEF) # a 2- or 3-byte lead: every such char is in the BMP
641
+
642
+ len = b < 0xE0 ? 2 : 3
643
+ c = pattern.byteslice(i, len).force_encoding(Encoding::UTF_8)
644
+ end
645
+ [pattern.byteslice(i, len), len] if c.valid_encoding? && c.downcase(:fold).length > 1
646
+ end
647
+
617
648
  def self.brace_quantifier(match)
618
649
  lo, hi = canonical_bounds(match[1], match[3])
619
650
  return match[0] unless lo # m > n is a JS SyntaxError: leave it for Onigmo to reject
@@ -6,6 +6,6 @@ module Camada
6
6
  # camada-web, camada-mkt) parse this file with /^\s*VERSION = "([^"]+)"/m the way they parse
7
7
  # a package.json. Plain X.Y.Z only, and @camada/ruby must stay in the analyst's SDK_NAMES:
8
8
  # its sdkId drops anything else.
9
- VERSION = "0.2.0"
9
+ VERSION = "0.2.1"
10
10
  SDK_ID = "@camada/ruby/#{VERSION}".freeze
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: camada
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - camada