js_regex 1.0.19 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2429773ea6bbeb5d7eed4ffa01742f0cecc4df77
4
- data.tar.gz: b5b3d8380986a844cf8577cf76a55ec846fc8eb6
3
+ metadata.gz: 1eb9a34e224340fb10bcaac25b7adee037279a2e
4
+ data.tar.gz: a50cc191b462e501e194308f9eb06c893b177657
5
5
  SHA512:
6
- metadata.gz: 2728a8ceb9ee272aabd1d30b6bd2c16154abc104f1c7cd3f2da7bc357582773c6d68afea5cc3a5844cae49bf7090ec3da6eacca86252b0543406533120b264fe
7
- data.tar.gz: 25d2e9dd9fbeda9cf624200565bca5e6603fd082549ecc0dab790fa3fb979eb03cab4105c15156123569dad079ac3ca31afe76e532a29d0f0161b60e8d95bc2f
6
+ metadata.gz: d7156fa441d772630f4d1f947e029f11355c74c85a563cf8b5ed09f977930e238c0e7951856da0163d8c255620ae3a64f225d50b5d77f77fbe60025052929f46
7
+ data.tar.gz: 72bb8e367bc70bef958957ecc8444af459e26e4aa4111fcae10d6bd80fb863120161c13a5beedb9210a700fcfa0896004b4f06605a54bebe96dd32a4831ab7f6
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class JsRegex
2
4
  #
3
5
  # This class acts as a facade, creating specific Converters and
@@ -12,17 +14,17 @@ class JsRegex
12
14
  attr_reader :ruby_regex, :context, :converters, :source, :options, :warnings
13
15
 
14
16
  def initialize(ruby_regex)
15
- @ruby_regex = ruby_regex
17
+ self.ruby_regex = ruby_regex
16
18
 
17
- @context = Converter::Context.new
18
- @converters = {}
19
+ self.context = Converter::Context.new
20
+ self.converters = {}
19
21
 
20
- @source = ''
21
- @options = ''
22
- @warnings = []
22
+ self.source = ''.dup
23
+ self.options = ''.dup
24
+ self.warnings = []
23
25
 
24
- convert_source(ruby_regex)
25
- convert_options(ruby_regex)
26
+ convert_source
27
+ convert_options
26
28
  perform_sanity_check
27
29
  end
28
30
 
@@ -33,6 +35,8 @@ class JsRegex
33
35
 
34
36
  private
35
37
 
38
+ attr_writer :ruby_regex, :context, :converters, :source, :options, :warnings
39
+
36
40
  CONVERTER_MAP = Hash.new(Converter::UnsupportedTokenConverter).merge(
37
41
  anchor: Converter::AnchorConverter,
38
42
  assertion: Converter::AssertionConverter,
@@ -51,36 +55,34 @@ class JsRegex
51
55
  type: Converter::TypeConverter
52
56
  ).freeze
53
57
 
54
- def convert_source(ruby_regex)
58
+ def convert_source
55
59
  Regexp::Scanner.scan(ruby_regex) do |token_class, subtype, data, s, e|
56
60
  # There might be a lot of tokens, so don't wrap their data in objects.
57
61
  # Even just wrapping them in simple structs or attr_reader objects
58
62
  # can lead to 60%+ longer processing times for large regexes.
59
- convert_token(token_class, subtype, data, s, e)
63
+ converter_for_token_class(token_class)
64
+ .convert(token_class, subtype, data, s, e)
60
65
  end
61
- converters.clear
62
- end
63
-
64
- def convert_token(token_class, subtype, data, s, e)
65
- converter = converter_for_token_class(token_class)
66
- converter.convert(token_class, subtype, data, s, e)
67
66
  end
68
67
 
69
68
  def converter_for_token_class(token_class)
70
69
  converters[token_class] ||= CONVERTER_MAP[token_class].new(self, context)
71
70
  end
72
71
 
73
- def convert_options(ruby_regex)
74
- @options = 'g' # all Ruby regexes are what is called "global" in JS
75
- @options << 'i' if ruby_regex.options & Regexp::IGNORECASE > 0
72
+ def convert_options
73
+ options << 'g' # all Ruby regexes are what is called "global" in JS
74
+ options << 'i' if (ruby_regex.options & Regexp::IGNORECASE).nonzero?
76
75
  end
77
76
 
77
+ SURROGATE_CODEPOINT_PATTERN = /\\uD[89A-F]\h\h/i
78
+
78
79
  def perform_sanity_check
79
80
  # Ruby regex capabilities are a superset of JS regex capabilities in
80
- # the source part. So if this raises an Error, a Converter messed up:
81
- Regexp.new(source, options)
81
+ # the source part. So if this raises an Error, a Converter messed up.
82
+ # Ignore that Ruby won't accept surrogate pairs, though.
83
+ Regexp.new(source.gsub(SURROGATE_CODEPOINT_PATTERN, '.'))
82
84
  rescue ArgumentError, RegexpError, SyntaxError => e
83
- @source = ''
85
+ self.source = ''
84
86
  warnings << e.message
85
87
  end
86
88
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
  require_relative 'group_converter'
3
5
 
@@ -14,9 +16,9 @@ class JsRegex
14
16
  def convert_data
15
17
  case subtype
16
18
  when :lookahead, :nlookahead
17
- open_group(non_capturing: true)
19
+ open_group(capturing: false)
18
20
  when :nlookbehind
19
- context.negative_lookbehind = true
21
+ context.start_negative_lookbehind
20
22
  warn_of_unsupported_feature('negative lookbehind assertion')
21
23
  else # :lookbehind, ...
22
24
  open_unsupported_group
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class JsRegex
2
4
  module Converter
3
5
  #
@@ -31,7 +33,7 @@ class JsRegex
31
33
  def warn_of_unsupported_feature(description = nil)
32
34
  description ||= "#{subtype} #{token_class} '#{data}'".tr('_', ' ')
33
35
  target.warnings << "Dropped unsupported #{description} "\
34
- "at index #{start_index}..#{end_index}"
36
+ "at index #{start_index}...#{end_index}"
35
37
  ''
36
38
  end
37
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -11,7 +13,7 @@ class JsRegex
11
13
  def convert_data
12
14
  case subtype
13
15
  when :open
14
- warn_of_unsupported_feature("conditional '(?'")
16
+ warn_of_unsupported_feature("conditional '(?('")
15
17
  '('
16
18
  when :separator, :close
17
19
  pass_through
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class JsRegex
2
4
  module Converter
3
5
  #
@@ -6,25 +8,20 @@ class JsRegex
6
8
  # The Converters themselves are stateless.
7
9
  #
8
10
  class Context
9
- attr_accessor :buffered_set_extractions,
10
- :buffered_set_members,
11
- :captured_group_count,
12
- :group_count_changed,
13
- :group_level,
14
- :group_level_for_backreference,
15
- :negative_lookbehind,
16
- :negative_set_levels,
17
- :previous_quantifier_end,
18
- :previous_quantifier_subtype,
19
- :set_level
11
+ attr_accessor :previous_quantifier_end # , :previous_quantifier_type
12
+
13
+ attr_reader :buffered_set_extractions,
14
+ :buffered_set_members,
15
+ :captured_group_count,
16
+ :group_count_changed,
17
+ :group_level_for_backreference,
18
+ :negative_lookbehind
20
19
 
21
20
  def initialize
22
21
  self.buffered_set_members = []
23
22
  self.buffered_set_extractions = []
24
23
  self.captured_group_count = 0
25
- self.group_count_changed = false
26
24
  self.group_level = 0
27
- self.negative_lookbehind = false
28
25
  self.negative_set_levels = []
29
26
  self.set_level = 0
30
27
  end
@@ -36,16 +33,24 @@ class JsRegex
36
33
  # set context
37
34
 
38
35
  def open_set
39
- self.set_level += 1
36
+ self.set_level = set_level + 1
40
37
  if set_level == 1
41
38
  buffered_set_members.clear
42
39
  buffered_set_extractions.clear
43
40
  end
44
- self.negative_set_levels -= [set_level]
41
+ negative_set_levels.delete(set_level)
45
42
  end
46
43
 
47
44
  def negate_set
48
- self.negative_set_levels |= [set_level]
45
+ self.negative_set_levels = negative_set_levels | [set_level]
46
+ end
47
+
48
+ def close_set
49
+ self.set_level = set_level - 1
50
+ end
51
+
52
+ def set?
53
+ set_level > 0
49
54
  end
50
55
 
51
56
  def negative_set?(level = set_level)
@@ -53,12 +58,69 @@ class JsRegex
53
58
  end
54
59
 
55
60
  def nested_negation?
56
- set_level > 1 && negative_set?
61
+ nested_set? && negative_set?
57
62
  end
58
63
 
59
- def close_set
60
- self.set_level -= 1
64
+ def nested_set?
65
+ set_level > 1
66
+ end
67
+
68
+ # group context
69
+
70
+ def open_group
71
+ self.group_level = group_level + 1
72
+ end
73
+
74
+ def capture_group
75
+ self.captured_group_count = captured_group_count + 1
76
+ end
77
+
78
+ def start_atomic_group
79
+ self.group_level_for_backreference = group_level
80
+ end
81
+
82
+ def start_negative_lookbehind
83
+ self.negative_lookbehind = true
84
+ end
85
+
86
+ def close_group
87
+ self.group_level = group_level - 1
88
+ end
89
+
90
+ def close_atomic_group
91
+ close_group
92
+ self.group_level_for_backreference = nil
93
+ self.group_count_changed = true
61
94
  end
95
+
96
+ def close_negative_lookbehind
97
+ close_group
98
+ self.negative_lookbehind = false
99
+ end
100
+
101
+ def group?
102
+ group_level > 0
103
+ end
104
+
105
+ def atomic_group?
106
+ group_level_for_backreference
107
+ end
108
+
109
+ def base_level_of_atomic_group?
110
+ group_level_for_backreference &&
111
+ group_level.equal?(group_level_for_backreference + 1)
112
+ end
113
+
114
+ private
115
+
116
+ attr_accessor :group_level, :negative_set_levels, :set_level
117
+
118
+ attr_writer :buffered_set_extractions,
119
+ :buffered_set_members,
120
+ :captured_group_count,
121
+ :group_count_changed,
122
+ :group_level_for_backreference,
123
+ :negative_lookbehind
62
124
  end
63
125
  end
64
126
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
  require_relative 'literal_converter'
3
5
 
@@ -24,19 +26,17 @@ class JsRegex
24
26
  :newline,
25
27
  :octal,
26
28
  :one_or_more,
27
- :return,
28
29
  :set_close,
29
30
  :set_open,
30
- :space,
31
31
  :tab,
32
32
  :vertical_tab,
33
33
  :zero_or_more,
34
34
  :zero_or_one
35
35
  pass_through
36
36
  when :literal
37
- LiteralConverter.convert(data, self)
37
+ LiteralConverter.convert_data(data)
38
38
  else
39
- # Backspace, Bell, HexWide, Control, Meta, MetaControl, ...
39
+ # Bell, Escape, HexWide, Control, Meta, MetaControl, ...
40
40
  warn_of_unsupported_feature
41
41
  end
42
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -13,7 +15,7 @@ class JsRegex
13
15
  when :atomic then open_atomic_group
14
16
  when :capture then open_group
15
17
  when :close then close_group
16
- when :comment then '' # drop whole group w/o warning
18
+ when :comment then '' # drop whole group without warning
17
19
  when :named_ab, :named_sq then open_named_group
18
20
  when :options then open_options_group
19
21
  when :passive then open_passive_group
@@ -25,12 +27,16 @@ class JsRegex
25
27
  # Atomicity is emulated using backreferenced lookahead groups:
26
28
  # http://instanceof.me/post/52245507631
27
29
  # regex-emulate-atomic-grouping-with-lookahead
28
- context.group_level_for_backreference = context.group_level
29
- open_group(head: '(?=(')
30
+ if context.atomic_group?
31
+ open_unsupported_group('nested atomic group')
32
+ else
33
+ context.start_atomic_group
34
+ open_group(head: '(?=(')
35
+ end
30
36
  end
31
37
 
32
38
  def open_named_group
33
- # drop name w/o warning
39
+ # drop name without warning
34
40
  open_group(head: '(')
35
41
  end
36
42
 
@@ -40,47 +46,33 @@ class JsRegex
40
46
  end
41
47
 
42
48
  def open_passive_group
43
- open_group(head: '(?:', non_capturing: true)
49
+ open_group(head: '(?:', capturing: false)
44
50
  end
45
51
 
46
- def open_unsupported_group
47
- warn_of_unsupported_feature
52
+ def open_unsupported_group(description = nil)
53
+ warn_of_unsupported_feature(description)
48
54
  open_passive_group
49
55
  end
50
56
 
51
- def open_group(options = {})
52
- context.group_level += 1
53
- context.captured_group_count += 1 unless options[:non_capturing]
54
- options[:head] || pass_through
57
+ def open_group(opts = {})
58
+ context.open_group
59
+ context.capture_group unless opts[:capturing].equal?(false)
60
+ opts[:head] || pass_through
55
61
  end
56
62
 
57
63
  def close_group
58
- context.group_level -= 1
59
64
  if context.negative_lookbehind
60
- close_negative_lookbehind
61
- elsif end_of_atomic_group?
62
- close_atomic_group
65
+ context.close_negative_lookbehind
66
+ ''
67
+ elsif context.base_level_of_atomic_group?
68
+ context.close_atomic_group
69
+ # an empty passive group (?:) is appended as literal digits may follow
70
+ "))\\#{context.captured_group_count}(?:)"
63
71
  else
72
+ context.close_group
64
73
  ')'
65
74
  end
66
75
  end
67
-
68
- def close_negative_lookbehind
69
- context.negative_lookbehind = false
70
- ''
71
- end
72
-
73
- def end_of_atomic_group?
74
- return false unless context.group_level_for_backreference
75
- context.group_level_for_backreference == context.group_level
76
- end
77
-
78
- def close_atomic_group
79
- context.group_level_for_backreference = nil
80
- context.group_count_changed = true
81
- # the empty passive group (?:) is appended in case literal digits follow
82
- "))\\#{context.captured_group_count}(?:)"
83
- end
84
76
  end
85
77
  end
86
78
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -6,30 +8,42 @@ class JsRegex
6
8
  # Template class implementation.
7
9
  #
8
10
  class LiteralConverter < JsRegex::Converter::Base
9
- def self.convert(data, converter)
10
- utf8_data = data.dup.force_encoding('UTF-8')
11
- if /[\u{10000}-\u{FFFFF}]/ =~ utf8_data
12
- converter.send(:warn_of_unsupported_feature, 'astral plane character')
13
- else
14
- escape_literal_forward_slashes(utf8_data)
15
- ensure_json_compatibility(utf8_data)
16
- utf8_data
11
+ class << self
12
+ ASTRAL_PLANE_CODEPOINT_PATTERN = /\A[\u{10000}-\u{FFFFF}]\z/
13
+
14
+ def convert_data(data)
15
+ if data =~ ASTRAL_PLANE_CODEPOINT_PATTERN
16
+ surrogate_pair_for(data)
17
+ else
18
+ escape_literal_forward_slashes(data)
19
+ ensure_json_compatibility(data)
20
+ data
21
+ end
17
22
  end
18
- end
19
23
 
20
- def self.escape_literal_forward_slashes(data)
21
- # literal slashes would be mistaken for the pattern end in JsRegex#to_s
22
- data.gsub!('/', '\\/')
23
- end
24
+ private
24
25
 
25
- def self.ensure_json_compatibility(data)
26
- data.gsub!(/\\?[\f\n\r\t]/) { |lit| Regexp.escape(lit.delete('\\')) }
26
+ def surrogate_pair_for(astral_char)
27
+ base = astral_char.codepoints.first - 65_536
28
+ high = ((base / 1024).floor + 55_296).to_s(16)
29
+ low = (base % 1024 + 56_320).to_s(16)
30
+ "\\u#{high}\\u#{low}"
31
+ end
32
+
33
+ def escape_literal_forward_slashes(data)
34
+ # literal slashes would signify the pattern end in JsRegex#to_s
35
+ data.gsub!('/', '\\/')
36
+ end
37
+
38
+ def ensure_json_compatibility(data)
39
+ data.gsub!(/\\?[\f\n\r\t]/) { |lit| Regexp.escape(lit.delete('\\')) }
40
+ end
27
41
  end
28
42
 
29
43
  private
30
44
 
31
45
  def convert_data
32
- self.class.convert(data, self)
46
+ self.class.convert_data(data)
33
47
  end
34
48
  end
35
49
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -20,8 +22,7 @@ class JsRegex
20
22
  end
21
23
 
22
24
  def ruby_multiline_mode?
23
- return false if @rb_mm == false
24
- @rb_mm ||= target.ruby_regex.options & Regexp::MULTILINE > 0
25
+ (target.ruby_regex.options & Regexp::MULTILINE).nonzero?
25
26
  end
26
27
  end
27
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
  require_relative 'property_converter'
3
5
 
@@ -12,7 +14,12 @@ class JsRegex
12
14
  private
13
15
 
14
16
  def convert_data
15
- convert_property(true)
17
+ if context.set?
18
+ context.buffered_set_extractions << convert_property(true)
19
+ ''
20
+ else
21
+ convert_property(true)
22
+ end
16
23
  end
17
24
  end
18
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
  require_relative File.join('..', 'property_map')
3
5
 
@@ -7,18 +9,21 @@ class JsRegex
7
9
  # Template class implementation.
8
10
  #
9
11
  class PropertyConverter < JsRegex::Converter::Base
10
- def self.property_replacement(property_name, negated = false)
11
- replacement = JsRegex::PROPERTY_MAP[property_name.downcase.to_sym]
12
- negated ? negated_property_replacement(replacement) : replacement
13
- end
12
+ class << self
13
+ def property_replacement(property_name, negated = nil)
14
+ replacement = PROPERTY_MAP[property_name.downcase.to_sym]
15
+ negated ? negated_property_replacement(replacement) : replacement
16
+ end
17
+
18
+ private
14
19
 
15
- def self.negated_property_replacement(property_string)
16
- # take care not to use destructive methods on elements in the map
17
- return nil unless property_string
18
- if property_string.start_with?('[^')
19
- property_string.sub('[^', '[')
20
- else
21
- property_string.sub('[', '[^')
20
+ def negated_property_replacement(property_string)
21
+ return nil unless property_string
22
+ if property_string.start_with?('[^')
23
+ property_string.sub('[^', '[')
24
+ else
25
+ property_string.sub('[', '[^')
26
+ end
22
27
  end
23
28
  end
24
29
 
@@ -28,7 +33,7 @@ class JsRegex
28
33
  convert_property
29
34
  end
30
35
 
31
- def convert_property(negated = false)
36
+ def convert_property(negated = nil)
32
37
  replace = self.class.property_replacement(subtype, negated)
33
38
  replace || warn_of_unsupported_feature
34
39
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -10,9 +12,9 @@ class JsRegex
10
12
 
11
13
  def convert_data
12
14
  if multiplicative_interval?
13
- warn_of_unsupported_feature('multiplicative interval \'{x}{x}\'')
15
+ warn_of_unsupported_feature('adjacent quantifiers')
14
16
  else
15
- context.previous_quantifier_subtype = subtype
17
+ # context.previous_quantifier_type = subtype
16
18
  context.previous_quantifier_end = end_index
17
19
  convert_quantifier
18
20
  end
@@ -28,9 +30,9 @@ class JsRegex
28
30
  end
29
31
 
30
32
  def multiplicative_interval?
31
- subtype == :interval &&
32
- context.previous_quantifier_subtype == :interval &&
33
- context.previous_quantifier_end == start_index
33
+ # subtype == :interval &&
34
+ # context.previous_quantifier_type == :interval &&
35
+ context.previous_quantifier_end.equal?(start_index)
34
36
  end
35
37
  end
36
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
  require_relative 'literal_converter'
3
5
  require_relative 'property_converter'
@@ -26,12 +28,14 @@ class JsRegex
26
28
  when :member, :range, :escape then convert_member_subtype
27
29
  when /\Aclass_/ then convert_class_subtype
28
30
  when /\Atype_/ then convert_type_subtype
31
+ when :backspace then convert_backspace_subtype
29
32
  when :intersection
30
33
  warn_of_unsupported_feature("set intersection '&&'")
31
34
  else
32
- # TODO: I think it's a bug in Regexp::Scanner that some property
33
- # tokens (only positive ones?) are returned with the token class :set
34
- # within sets. If this's fixed, just warn_of_unsupported_feature here.
35
+ # Note that, within sets, Regexp::Scanner returns
36
+ # - positive property tokens in the \p{-style with class :set
37
+ # - negative property tokens in the \P{-style with class :set
38
+ # - negative property tokens in the \p{^-style with class :nonproperty
35
39
  try_replacing_potential_property_subtype
36
40
  end
37
41
  end
@@ -42,7 +46,7 @@ class JsRegex
42
46
  end
43
47
 
44
48
  def convert_negate_subtype
45
- if context.set_level > 1
49
+ if context.nested_set?
46
50
  warn_of_unsupported_feature('nested negative set data')
47
51
  end
48
52
  context.negate_set
@@ -51,25 +55,28 @@ class JsRegex
51
55
 
52
56
  def convert_close_subtype
53
57
  context.close_set
54
- context.set_level == 0 ? finalize_set : ''
58
+ context.set? ? '' : finalize_set
55
59
  end
56
60
 
57
61
  def convert_member_subtype
58
- literal_conversion = LiteralConverter.convert(data, self)
59
- return '' if literal_conversion == ''
60
- buffer_set_member(literal_conversion)
62
+ utf8_data = data.force_encoding('UTF-8')
63
+ if /[\u{10000}-\u{FFFFF}]/ =~ utf8_data
64
+ warn_of_unsupported_feature('astral plane set member')
65
+ else
66
+ literal_conversion = LiteralConverter.convert_data(utf8_data)
67
+ buffer_set_member(literal_conversion)
68
+ end
61
69
  end
62
70
 
63
71
  def convert_class_subtype
64
72
  negated = subtype.to_s.start_with?('class_non')
65
- name = subtype.to_s[(negated ? 9 : 6)..-1]
73
+ name = subtype[(negated ? 9 : 6)..-1]
66
74
  try_replacing_property(name, negated)
67
75
  end
68
76
 
69
77
  def try_replacing_potential_property_subtype
70
- negated = subtype.to_s.start_with?('non')
71
- name = negated ? subtype.to_s[3..-1] : subtype.to_s
72
- try_replacing_property(name, negated)
78
+ negated = data.start_with?('\\P')
79
+ try_replacing_property(subtype, negated)
73
80
  end
74
81
 
75
82
  def try_replacing_property(name, negated)
@@ -82,47 +89,40 @@ class JsRegex
82
89
  end
83
90
 
84
91
  def convert_type_subtype
85
- if subtype == :type_hex
92
+ if subtype.equal?(:type_hex)
86
93
  buffer_set_extraction(TypeConverter::HEX_EXPANSION)
87
- elsif subtype == :type_nonhex
94
+ elsif subtype.equal?(:type_nonhex)
88
95
  buffer_set_extraction(TypeConverter::NONHEX_EXPANSION)
89
96
  else
90
97
  buffer_set_member(data)
91
98
  end
92
99
  end
93
100
 
94
- def buffer_set_member(string)
95
- buffered_members << string unless context.nested_negation?
96
- ''
101
+ def convert_backspace_subtype
102
+ buffer_set_extraction('[\b]')
97
103
  end
98
104
 
99
- def buffer_set_extraction(string)
100
- buffered_extractions << string unless context.nested_negation?
105
+ def buffer_set_member(m)
106
+ context.buffered_set_members << m unless context.nested_negation?
101
107
  ''
102
108
  end
103
109
 
104
- def buffered_members
105
- context.buffered_set_members
106
- end
107
-
108
- def buffered_extractions
109
- context.buffered_set_extractions
110
+ def buffer_set_extraction(e)
111
+ context.buffered_set_extractions << e unless context.nested_negation?
112
+ ''
110
113
  end
111
114
 
112
115
  def finalize_set
113
- if buffered_members.none?
114
- finalize_depleted_set
116
+ buffered_members = context.buffered_set_members
117
+ buffered_extractions = context.buffered_set_extractions
118
+ if buffered_members.empty?
119
+ finalize_depleted_set(buffered_extractions)
115
120
  else
116
- set = build_set(buffered_members, context.negative_set?(1))
117
- if buffered_extractions.any?
118
- "(?:#{set}|#{buffered_extractions.join('|')})"
119
- else
120
- set
121
- end
121
+ finalize_nondepleted_set(buffered_members, buffered_extractions)
122
122
  end
123
123
  end
124
124
 
125
- def finalize_depleted_set
125
+ def finalize_depleted_set(buffered_extractions)
126
126
  case buffered_extractions.count
127
127
  when 0 then ''
128
128
  when 1 then buffered_extractions.first
@@ -130,8 +130,13 @@ class JsRegex
130
130
  end
131
131
  end
132
132
 
133
- def build_set(members, negative)
134
- "[#{negative ? '^' : ''}#{members.join}]"
133
+ def finalize_nondepleted_set(buffered_members, buffered_extractions)
134
+ set = "[#{'^' if context.negative_set?(1)}#{buffered_members.join}]"
135
+ if buffered_extractions.empty?
136
+ set
137
+ else
138
+ "(?:#{set}|#{buffered_extractions.join('|')})"
139
+ end
135
140
  end
136
141
  end
137
142
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -6,8 +8,8 @@ class JsRegex
6
8
  # Template class implementation.
7
9
  #
8
10
  class TypeConverter < JsRegex::Converter::Base
9
- HEX_EXPANSION = '[A-Fa-f0-9]'.freeze
10
- NONHEX_EXPANSION = '[^A-Fa-f0-9]'.freeze
11
+ HEX_EXPANSION = '[A-Fa-f0-9]'
12
+ NONHEX_EXPANSION = '[^A-Fa-f0-9]'
11
13
 
12
14
  private
13
15
 
@@ -15,7 +17,7 @@ class JsRegex
15
17
  case subtype
16
18
  when :hex then HEX_EXPANSION
17
19
  when :nonhex then NONHEX_EXPANSION
18
- when :any, :digit, :nondigit, :word, :nonword, :space, :nonspace
20
+ when :digit, :nondigit, :word, :nonword, :space, :nonspace
19
21
  pass_through
20
22
  else
21
23
  warn_of_unsupported_feature
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base'
2
4
 
3
5
  class JsRegex
@@ -1,3 +1,4 @@
1
+ # encoding: utf-8
1
2
  # frozen_string_literal: true
2
3
  #
3
4
  # This hash maps named properties that are available in Ruby's ::Regexp to
@@ -11,8 +12,6 @@
11
12
  # Note that the names emitted by Scanner are slightly inconsistent at times,
12
13
  # e.g. 'grapheme_extend' vs. 'other_grapheme_extended'.
13
14
  #
14
- # Surrogate blocks are left out because Ruby sees them as invalid unicode range.
15
- #
16
15
  # rubocop:disable ClassLength, LineLength
17
16
  #
18
17
  class JsRegex
@@ -99,6 +98,8 @@ class JsRegex
99
98
  block_inhangul_syllables: '[\uAC00-\uD7AF]',
100
99
  block_inhanunoo: '[\u1720-\u173F]',
101
100
  block_inhebrew: '[\u0590-\u05FF]',
101
+ block_inhigh_private_use_surrogates: '[\uDB80–\uDBFF]',
102
+ block_inhigh_surrogates: '[\uD800–\uDBFF]',
102
103
  block_inhiragana: '[\u3040-\u309F]',
103
104
  block_inideographic_description_characters: '[\u2FF0-\u2FFF]',
104
105
  block_inipa_extensions: '[\u0250-\u02AF]',
@@ -116,6 +117,7 @@ class JsRegex
116
117
  block_inlatin_extended_b: '[\u0180-\u024F]',
117
118
  block_inletterlike_symbols: '[\u2100-\u214F]',
118
119
  block_inlimbu: '[\u1900-\u194F]',
120
+ block_inlow_surrogates: '[\uDC00–\uDFFF]',
119
121
  block_inmalayalam: '[\u0D00-\u0D7F]',
120
122
  block_inmathematical_operators: '[\u2200-\u22FF]',
121
123
  block_inmiscellaneous_mathematical_symbols_a: '[\u27C0-\u27EF]',
@@ -310,6 +312,7 @@ class JsRegex
310
312
  separator_space: '[\x20\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]',
311
313
  soft_dotted: '[\u0069-\u006A\u012F\u0249\u0268\u029D\u02B2\u03F3\u0456\u0458\u1D62\u1D96\u1DA4\u1DA8\u1E2D\u1ECB\u2071\u2148-\u2149\u2C7C]',
312
314
  space: '[\s]',
315
+ surrogate: '[\uD800-\uDFFF]',
313
316
  symbol: '[\x24\x2B\x3C-\x3E\x5E\x60\x7C\x7E\u00A2-\u00A6\u00A8\u00A9\u00AC\u00AE-\u00B1\u00B4\u00B8\u00D7\u00F7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20BD\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u2190-\u2307\u230C-\u2328\u232B-\u23FA\u2400-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD1\u2CE5-\u2CEA\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u32FE\u3300-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uFB29\uFBB2-\uFBC1\uFDFC\uFDFD\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD]',
314
317
  symbol_currency: '[\x24\u00A2-\u00A5\u058F\u060B\u09F2\u09F3\u09FB\u0AF1\u0BF9\u0E3F\u17DB\u20A0-\u20BD\uA838\uFDFC\uFE69\uFF04\uFFE0\uFFE1\uFFE5\uFFE6]',
315
318
  symbol_math: '[\x2B\x3C-\x3E\x7C\x7E\u00AC\u00B1\u00D7\u00F7\u03F6\u0606-\u0608\u2044\u2052\u207A-\u207C\u208A-\u208C\u2118\u2140-\u2144\u214B\u2190-\u2194\u219A\u219B\u21A0\u21A3\u21A6\u21AE\u21CE\u21CF\u21D2\u21D4\u21F4-\u22FF\u2320\u2321\u237C\u239B-\u23B3\u23DC-\u23E1\u25B7\u25C1\u25F8-\u25FF\u266F\u27C0-\u27C4\u27C7-\u27E5\u27F0-\u27FF\u2900-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2AFF\u2B30-\u2B44\u2B47-\u2B4C\uFB29\uFE62\uFE64-\uFE66\uFF0B\uFF1C-\uFF1E\uFF5C\uFF5E\uFFE2\uFFE9-\uFFEC]',
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class JsRegex
4
+ VERSION = '1.1.0'
5
+ end
data/lib/js_regex.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # JsRegex converts ::Regexp instances to JavaScript.
3
4
  #
metadata CHANGED
@@ -1,59 +1,65 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: js_regex
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.19
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janosch Müller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-15 00:00:00.000000000 Z
11
+ date: 2016-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: regexp_parser
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 0.3.6
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.4.1
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - '='
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 0.3.6
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.4.1
27
33
  - !ruby/object:Gem::Dependency
28
- name: mutant-rspec
34
+ name: rake
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
37
  - - "~>"
32
38
  - !ruby/object:Gem::Version
33
- version: '0.8'
39
+ version: '11.3'
34
40
  type: :development
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
- version: '0.8'
46
+ version: '11.3'
41
47
  - !ruby/object:Gem::Dependency
42
- name: rake
48
+ name: rspec-core
43
49
  requirement: !ruby/object:Gem::Requirement
44
50
  requirements:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
- version: '11.3'
53
+ version: '3.5'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
51
57
  requirements:
52
58
  - - "~>"
53
59
  - !ruby/object:Gem::Version
54
- version: '11.3'
60
+ version: '3.5'
55
61
  - !ruby/object:Gem::Dependency
56
- name: rspec-core
62
+ name: rspec-expectations
57
63
  requirement: !ruby/object:Gem::Requirement
58
64
  requirements:
59
65
  - - "~>"
@@ -67,7 +73,7 @@ dependencies:
67
73
  - !ruby/object:Gem::Version
68
74
  version: '3.5'
69
75
  - !ruby/object:Gem::Dependency
70
- name: rspec-expectations
76
+ name: rspec-mocks
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
79
  - - "~>"
@@ -108,6 +114,20 @@ dependencies:
108
114
  - - "~>"
109
115
  - !ruby/object:Gem::Version
110
116
  version: '0.6'
117
+ - !ruby/object:Gem::Dependency
118
+ name: mutant-rspec
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
111
131
  description: JsRegex converts Ruby's native regular expressions for JavaScript, taking
112
132
  care of various incompatibilities and returning warnings for unsolvable differences.
113
133
  email:
@@ -136,6 +156,7 @@ files:
136
156
  - lib/js_regex/converter/type_converter.rb
137
157
  - lib/js_regex/converter/unsupported_token_converter.rb
138
158
  - lib/js_regex/property_map.rb
159
+ - lib/js_regex/version.rb
139
160
  homepage: https://github.com/janosch-x/js_regex
140
161
  licenses:
141
162
  - MIT
@@ -148,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
148
169
  requirements:
149
170
  - - ">="
150
171
  - !ruby/object:Gem::Version
151
- version: '0'
172
+ version: 1.9.1
152
173
  required_rubygems_version: !ruby/object:Gem::Requirement
153
174
  requirements:
154
175
  - - ">="
@@ -156,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
177
  version: '0'
157
178
  requirements: []
158
179
  rubyforge_project:
159
- rubygems_version: 2.5.1
180
+ rubygems_version: 2.5.2
160
181
  signing_key:
161
182
  specification_version: 4
162
183
  summary: Converts Ruby regexes to JavaScript regexes.