mutant 0.10.23 → 0.10.24

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant.rb +20 -4
  3. data/lib/mutant/ast/regexp.rb +37 -0
  4. data/lib/mutant/ast/regexp/transformer.rb +150 -0
  5. data/lib/mutant/ast/regexp/transformer/direct.rb +121 -0
  6. data/lib/mutant/ast/regexp/transformer/named_group.rb +50 -0
  7. data/lib/mutant/ast/regexp/transformer/options_group.rb +68 -0
  8. data/lib/mutant/ast/regexp/transformer/quantifier.rb +90 -0
  9. data/lib/mutant/ast/regexp/transformer/recursive.rb +56 -0
  10. data/lib/mutant/ast/regexp/transformer/root.rb +28 -0
  11. data/lib/mutant/ast/regexp/transformer/text.rb +58 -0
  12. data/lib/mutant/ast/types.rb +115 -2
  13. data/lib/mutant/cli/command/environment.rb +9 -3
  14. data/lib/mutant/config.rb +8 -54
  15. data/lib/mutant/config/coverage_criteria.rb +61 -0
  16. data/lib/mutant/expression.rb +0 -12
  17. data/lib/mutant/matcher.rb +2 -2
  18. data/lib/mutant/matcher/config.rb +25 -6
  19. data/lib/mutant/matcher/method.rb +2 -3
  20. data/lib/mutant/meta/example/dsl.rb +6 -1
  21. data/lib/mutant/mutator/node/kwargs.rb +2 -2
  22. data/lib/mutant/mutator/node/literal/regex.rb +26 -0
  23. data/lib/mutant/mutator/node/regexp.rb +31 -0
  24. data/lib/mutant/mutator/node/regexp/alternation_meta.rb +20 -0
  25. data/lib/mutant/mutator/node/regexp/capture_group.rb +25 -0
  26. data/lib/mutant/mutator/node/regexp/character_type.rb +31 -0
  27. data/lib/mutant/mutator/node/regexp/end_of_line_anchor.rb +20 -0
  28. data/lib/mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor.rb +20 -0
  29. data/lib/mutant/mutator/node/regexp/greedy_zero_or_more.rb +24 -0
  30. data/lib/mutant/subject.rb +1 -3
  31. data/lib/mutant/subject/method/instance.rb +1 -3
  32. data/lib/mutant/version.rb +1 -1
  33. data/lib/mutant/world.rb +1 -2
  34. metadata +40 -4
  35. data/lib/mutant/warnings.rb +0 -106
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: de1bb499dcb7be47ab3ffecf16968d48f17e34825a8cdb64f3a16aded7c76c35
4
- data.tar.gz: eccbf7d336879f50a9f018b404ef64289d112f5cf377bccf38c7a6a73d28c7d7
3
+ metadata.gz: 34fe96de54f1f220934b79bdf26e6103693d1a8cd4986bc05259d90117016801
4
+ data.tar.gz: 1a3acf6dcc73c191f6264a5827910b76d2da54aaff79a75a903f45595bd653be
5
5
  SHA512:
6
- metadata.gz: ec30feb496c9f71dfef0766febd898508f41b252a2a5b678f98cc811947a50b8a57bbaee1eae40175b4593e9292ee858cf337d1ff5d3fb5e951ed93fc4117eb6
7
- data.tar.gz: 98b4ec5a27cfef0a94d167d281fa7735c514b34bee7d0f9b677cc49a5a046146e1e06d467bffeed6a8f8b0467201727b6901bffcfa4d48d57a6d43f2016ed15a
6
+ metadata.gz: fc6d72fcd52b9167dd3d0cd030c5f29076f61b8c296d9019802106200567e60c28e011fd68b6f1e928cca1bd5c9787291b1b35e7b77a86dc8a74b3895c323dd2
7
+ data.tar.gz: 53eb52604ab5b02cb412f990efa2770495df25c56cab860447ac210f4179a17cb6cf76f0641f4e362882188ee403e6402908d09d7c3673500aa00b2722084ae2
@@ -17,6 +17,7 @@ require 'optparse'
17
17
  require 'parser'
18
18
  require 'parser/current'
19
19
  require 'pathname'
20
+ require 'regexp_parser'
20
21
  require 'set'
21
22
  require 'singleton'
22
23
  require 'stringio'
@@ -40,6 +41,7 @@ module Mutant
40
41
  SCOPE_OPERATOR = '::'
41
42
  end # Mutant
42
43
 
44
+ require 'mutant/transform'
43
45
  require 'mutant/bootstrap'
44
46
  require 'mutant/version'
45
47
  require 'mutant/env'
@@ -53,6 +55,15 @@ require 'mutant/ast/nodes'
53
55
  require 'mutant/ast/named_children'
54
56
  require 'mutant/ast/node_predicates'
55
57
  require 'mutant/ast/find_metaclass_containing'
58
+ require 'mutant/ast/regexp'
59
+ require 'mutant/ast/regexp/transformer'
60
+ require 'mutant/ast/regexp/transformer/direct'
61
+ require 'mutant/ast/regexp/transformer/named_group'
62
+ require 'mutant/ast/regexp/transformer/options_group'
63
+ require 'mutant/ast/regexp/transformer/quantifier'
64
+ require 'mutant/ast/regexp/transformer/recursive'
65
+ require 'mutant/ast/regexp/transformer/root'
66
+ require 'mutant/ast/regexp/transformer/text'
56
67
  require 'mutant/ast/meta'
57
68
  require 'mutant/ast/meta/send'
58
69
  require 'mutant/ast/meta/const'
@@ -75,6 +86,13 @@ require 'mutant/mutator/util/array'
75
86
  require 'mutant/mutator/util/symbol'
76
87
  require 'mutant/mutator/node'
77
88
  require 'mutant/mutator/node/generic'
89
+ require 'mutant/mutator/node/regexp'
90
+ require 'mutant/mutator/node/regexp/alternation_meta'
91
+ require 'mutant/mutator/node/regexp/capture_group'
92
+ require 'mutant/mutator/node/regexp/character_type'
93
+ require 'mutant/mutator/node/regexp/end_of_line_anchor'
94
+ require 'mutant/mutator/node/regexp/end_of_string_or_before_end_of_line_anchor'
95
+ require 'mutant/mutator/node/regexp/greedy_zero_or_more'
78
96
  require 'mutant/mutator/node/literal'
79
97
  require 'mutant/mutator/node/literal/boolean'
80
98
  require 'mutant/mutator/node/literal/range'
@@ -159,7 +177,6 @@ require 'mutant/expression/methods'
159
177
  require 'mutant/expression/namespace'
160
178
  require 'mutant/test'
161
179
  require 'mutant/timer'
162
- require 'mutant/transform'
163
180
  require 'mutant/integration'
164
181
  require 'mutant/integration/null'
165
182
  require 'mutant/selector'
@@ -167,6 +184,7 @@ require 'mutant/selector/expression'
167
184
  require 'mutant/selector/null'
168
185
  require 'mutant/world'
169
186
  require 'mutant/config'
187
+ require 'mutant/config/coverage_criteria'
170
188
  require 'mutant/cli'
171
189
  require 'mutant/cli/command'
172
190
  require 'mutant/cli/command/subscription'
@@ -196,7 +214,6 @@ require 'mutant/reporter/cli/format'
196
214
  require 'mutant/repository'
197
215
  require 'mutant/repository/diff'
198
216
  require 'mutant/repository/diff/ranges'
199
- require 'mutant/warnings'
200
217
  require 'mutant/zombifier'
201
218
  require 'mutant/range'
202
219
  require 'mutant/license'
@@ -222,8 +239,7 @@ module Mutant
222
239
  stderr: $stderr,
223
240
  stdout: $stdout,
224
241
  thread: Thread,
225
- timer: Timer.new(Process),
226
- warnings: Warnings.new(Warning)
242
+ timer: Timer.new(Process)
227
243
  )
228
244
 
229
245
  # Reopen class to initialize constant to avoid dep circle
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module AST
5
+ # Regexp source mapper
6
+ module Regexp
7
+ # Parse regex string into expression
8
+ #
9
+ # @param regexp [String]
10
+ #
11
+ # @return [Regexp::Expression, nil]
12
+ def self.parse(regexp)
13
+ ::Regexp::Parser.parse(regexp)
14
+ end
15
+
16
+ # Convert expression into ast node
17
+ #
18
+ # @param expression [Regexp::Expression]
19
+ #
20
+ # @return [Parser::AST::Node]
21
+ def self.to_ast(expression)
22
+ ast_type = :"regexp_#{expression.token}_#{expression.type}"
23
+
24
+ Transformer.lookup(ast_type).to_ast(expression)
25
+ end
26
+
27
+ # Convert node into expression
28
+ #
29
+ # @param node [Parser::AST::Node]
30
+ #
31
+ # @return [Regexp::Expression]
32
+ def self.to_expression(node)
33
+ Transformer.lookup(node.type).to_expression(node)
34
+ end
35
+ end # Regexp
36
+ end # AST
37
+ end # Mutant
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module AST
5
+ module Regexp
6
+ # Regexp bijective mapper
7
+ #
8
+ # Transforms parsed regular expression representation from
9
+ # `Regexp::Expression` instances (provided by `regexp_parser`) into
10
+ # equivalent representations using `Parser::AST::Node`
11
+ class Transformer
12
+ include AbstractType
13
+
14
+ REGISTRY = Registry.new
15
+
16
+ # Lookup transformer class for regular expression node type
17
+ #
18
+ # @param type [Symbol]
19
+ #
20
+ # @return [Class<Transformer>]
21
+ def self.lookup(type)
22
+ REGISTRY.lookup(type)
23
+ end
24
+
25
+ def self.register(type)
26
+ REGISTRY.register(type, self)
27
+ end
28
+ private_class_method :register
29
+
30
+ # Transform expression
31
+ #
32
+ # @param expression [Regexp::Expression]
33
+ #
34
+ # @return [Parser::AST::Node]
35
+ def self.to_ast(expression)
36
+ self::ExpressionToAST.call(expression)
37
+ end
38
+
39
+ # Transform node
40
+ #
41
+ # @param node [Parser::AST::Node]
42
+ #
43
+ # @return [Regexp::Expression]
44
+ def self.to_expression(node)
45
+ self::ASTToExpression.call(node)
46
+ end
47
+
48
+ # Abstract expression transformer
49
+ class ExpressionToAST
50
+ PREFIX = :regexp
51
+
52
+ include Concord.new(:expression), Procto.call, AST::Sexp, AbstractType, Adamantium
53
+
54
+ private
55
+
56
+ def ast(*children)
57
+ s(type, *children)
58
+ end
59
+
60
+ def quantify(node)
61
+ return node unless expression.quantified?
62
+
63
+ Quantifier.to_ast(expression.quantifier).append(node)
64
+ end
65
+
66
+ def children
67
+ expression.map(&Regexp.public_method(:to_ast))
68
+ end
69
+
70
+ def type
71
+ :"#{PREFIX}_#{expression.token}_#{expression.type}"
72
+ end
73
+ end # ExpressionToAST
74
+
75
+ # Abstract node transformer
76
+ class ASTToExpression
77
+ include Concord.new(:node), Procto.call, AbstractType, Adamantium
78
+
79
+ # Call generic transform method and freeze result
80
+ #
81
+ # @return [Regexp::Expression]
82
+ def call
83
+ transform.freeze
84
+ end
85
+
86
+ private
87
+
88
+ abstract_method :transform
89
+
90
+ def subexpressions
91
+ node.children.map(&Regexp.public_method(:to_expression))
92
+ end
93
+ end # ASTToExpression
94
+
95
+ # Mixin for node transformers
96
+ #
97
+ # Helps construct a mapping from Parser::AST::Node domain to
98
+ # Regexp::Expression domain
99
+ module LookupTable
100
+ Mapping = Class.new.include(Concord::Public.new(:token, :regexp_class))
101
+
102
+ # Table mapping ast types to object information for regexp domain
103
+ class Table
104
+
105
+ # Coerce array of mapping information into structured table
106
+ #
107
+ # @param [Array(Symbol, Array, Class<Regexp::Expression>)]
108
+ #
109
+ # @return [Table]
110
+ def self.create(*rows)
111
+ table = rows.map do |ast_type, token, klass|
112
+ [ast_type, Mapping.new(::Regexp::Token.new(*token), klass)]
113
+ end.to_h
114
+
115
+ new(table)
116
+ end
117
+
118
+ include Concord.new(:table), Adamantium
119
+
120
+ # Types defined by the table
121
+ #
122
+ # @return [Array<Symbol>]
123
+ def types
124
+ table.keys
125
+ end
126
+
127
+ # Lookup mapping information given an ast node type
128
+ #
129
+ # @param type [Symbol]
130
+ #
131
+ # @return [Mapping]
132
+ def lookup(type)
133
+ table.fetch(type)
134
+ end
135
+ end # Table
136
+
137
+ private
138
+
139
+ def expression_token
140
+ self.class::TABLE.lookup(node.type).token
141
+ end
142
+
143
+ def expression_class
144
+ self.class::TABLE.lookup(node.type).regexp_class
145
+ end
146
+ end # LookupTable
147
+ end # Transformer
148
+ end # Regexp
149
+ end # AST
150
+ end # Mutant
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module AST
5
+ module Regexp
6
+ class Transformer
7
+ # Transformer for nodes which map directly to other domain
8
+ #
9
+ # A node maps "directly" to another domain if the node never
10
+ # has children or text which needs to be preserved for a mapping
11
+ #
12
+ # @example direct mapping
13
+ #
14
+ # input = /\d/
15
+ # expression = Regexp::Parser.parse(input).first
16
+ # node = Transformer::Direct.to_ast(expression)
17
+ #
18
+ # # the digit type always has the same text and no children
19
+ # expression.text # => "\\d"
20
+ # expression.terminal? # => true
21
+ #
22
+ # # therefore the `Parser::AST::Node` is always the same
23
+ # node # => s(:regexp_digit_type)
24
+ class Direct < self
25
+ # Mapper from `Regexp::Expression` to `Parser::AST::Node`
26
+ class ExpressionToAST < Transformer::ExpressionToAST
27
+ # Transform expression into node
28
+ #
29
+ # @return [Parser::AST::Node]
30
+ def call
31
+ quantify(ast)
32
+ end
33
+ end # ExpressionToAST
34
+
35
+ # Mapper from `Parser::AST::Node` to `Regexp::Expression`
36
+ class ASTToExpression < Transformer::ASTToExpression
37
+ include LookupTable
38
+
39
+ # rubocop:disable Layout/LineLength
40
+ TABLE = Table.create(
41
+ [:regexp_alnum_posixclass, [:posixclass, :alnum, '[:alnum:]'], ::Regexp::Expression::PosixClass],
42
+ [:regexp_alpha_posixclass, [:posixclass, :alpha, '[:alpha:]'], ::Regexp::Expression::PosixClass],
43
+ [:regexp_alpha_property, [:property, :alpha, '\p{Alpha}'], ::Regexp::Expression::UnicodeProperty::Alpha],
44
+ [:regexp_alternation_escape, [:escape, :alternation, '\|'], ::Regexp::Expression::EscapeSequence::Literal],
45
+ [:regexp_arabic_property, [:property, :arabic, '\p{Arabic}'], ::Regexp::Expression::UnicodeProperty::Script],
46
+ [:regexp_ascii_posixclass, [:posixclass, :ascii, '[:ascii:]'], ::Regexp::Expression::PosixClass],
47
+ [:regexp_backspace_escape, [:escape, :backspace, '\b'], ::Regexp::Expression::EscapeSequence::Backspace],
48
+ [:regexp_bell_escape, [:escape, :bell, '\a'], ::Regexp::Expression::EscapeSequence::Literal],
49
+ [:regexp_blank_posixclass, [:posixclass, :blank, '[:blank:]'], ::Regexp::Expression::PosixClass],
50
+ [:regexp_bol_anchor, [:anchor, :bol, '^'], ::Regexp::Expression::Anchor::BeginningOfLine],
51
+ [:regexp_bol_escape, [:escape, :bol, '\^'], ::Regexp::Expression::EscapeSequence::Literal],
52
+ [:regexp_bos_anchor, [:anchor, :bos, '\\A'], ::Regexp::Expression::Anchor::BeginningOfString],
53
+ [:regexp_carriage_escape, [:escape, :carriage, '\r'], ::Regexp::Expression::EscapeSequence::Literal],
54
+ [:regexp_cntrl_posixclass, [:posixclass, :cntrl, '[:cntrl:]'], ::Regexp::Expression::PosixClass],
55
+ [:regexp_digit_posixclass, [:posixclass, :digit, '[:digit:]'], ::Regexp::Expression::PosixClass],
56
+ [:regexp_digit_type, [:type, :digit, '\d'], ::Regexp::Expression::CharacterType::Digit],
57
+ [:regexp_dot_escape, [:escape, :dot, '\.'], ::Regexp::Expression::EscapeSequence::Literal],
58
+ [:regexp_dot_meta, [:meta, :dot, '.'], ::Regexp::Expression::CharacterType::Any],
59
+ [:regexp_eol_anchor, [:anchor, :eol, '$'], ::Regexp::Expression::Anchor::EndOfLine],
60
+ [:regexp_eol_escape, [:escape, :eol, '\$'], ::Regexp::Expression::EscapeSequence::Literal],
61
+ [:regexp_eos_anchor, [:anchor, :eos, '\\z'], ::Regexp::Expression::Anchor::EndOfString],
62
+ [:regexp_eos_ob_eol_anchor, [:anchor, :eos_ob_eol, '\\Z'], ::Regexp::Expression::Anchor::EndOfStringOrBeforeEndOfLine],
63
+ [:regexp_escape_escape, [:escape, :escape, '\e'], ::Regexp::Expression::EscapeSequence::AsciiEscape],
64
+ [:regexp_form_feed_escape, [:escape, :form_feed, '\f'], ::Regexp::Expression::EscapeSequence::FormFeed],
65
+ [:regexp_graph_posixclass, [:posixclass, :graph, '[:graph:]'], ::Regexp::Expression::PosixClass],
66
+ [:regexp_group_close_escape, [:escape, :group_close, '\)'], ::Regexp::Expression::EscapeSequence::Literal],
67
+ [:regexp_group_open_escape, [:escape, :group_open, '\('], ::Regexp::Expression::EscapeSequence::Literal],
68
+ [:regexp_han_property, [:property, :han, '\p{Han}'], ::Regexp::Expression::UnicodeProperty::Script],
69
+ [:regexp_hangul_property, [:property, :hangul, '\p{Hangul}'], ::Regexp::Expression::UnicodeProperty::Script],
70
+ [:regexp_hex_type, [:type, :hex, '\h'], ::Regexp::Expression::CharacterType::Hex],
71
+ [:regexp_hiragana_property, [:property, :hiragana, '\p{Hiragana}'], ::Regexp::Expression::UnicodeProperty::Script],
72
+ [:regexp_interval_close_escape, [:escape, :interval_close, '\}'], ::Regexp::Expression::EscapeSequence::Literal],
73
+ [:regexp_interval_open_escape, [:escape, :interval_open, '\{'], ::Regexp::Expression::EscapeSequence::Literal],
74
+ [:regexp_katakana_property, [:property, :katakana, '\p{Katakana}'], ::Regexp::Expression::UnicodeProperty::Script],
75
+ [:regexp_letter_property, [:property, :letter, '\p{L}'], ::Regexp::Expression::UnicodeProperty::Letter::Any],
76
+ [:regexp_linebreak_type, [:type, :linebreak, '\R'], ::Regexp::Expression::CharacterType::Linebreak],
77
+ [:regexp_lower_posixclass, [:posixclass, :lower, '[:lower:]'], ::Regexp::Expression::PosixClass],
78
+ [:regexp_mark_keep, [:keep, :mark, '\K'], ::Regexp::Expression::Keep::Mark],
79
+ [:regexp_match_start_anchor, [:anchor, :match_start, '\\G'], ::Regexp::Expression::Anchor::MatchStart],
80
+ [:regexp_newline_escape, [:escape, :newline, '\n'], ::Regexp::Expression::EscapeSequence::Literal],
81
+ [:regexp_nondigit_type, [:type, :nondigit, '\D'], ::Regexp::Expression::CharacterType::NonDigit],
82
+ [:regexp_nonhex_type, [:type, :nonhex, '\H'], ::Regexp::Expression::CharacterType::NonHex],
83
+ [:regexp_nonspace_type, [:type, :nonspace, '\S'], ::Regexp::Expression::CharacterType::NonSpace],
84
+ [:regexp_nonword_boundary_anchor, [:anchor, :nonword_boundary, '\\B'], ::Regexp::Expression::Anchor::NonWordBoundary],
85
+ [:regexp_nonword_type, [:type, :nonword, '\W'], ::Regexp::Expression::CharacterType::NonWord],
86
+ [:regexp_one_or_more_escape, [:escape, :one_or_more, '\+'], ::Regexp::Expression::EscapeSequence::Literal],
87
+ [:regexp_print_nonposixclass, [:nonposixclass, :print, '[:^print:]'], ::Regexp::Expression::PosixClass],
88
+ [:regexp_print_nonproperty, [:nonproperty, :print, '\P{Print}'], ::Regexp::Expression::UnicodeProperty::Print],
89
+ [:regexp_print_posixclass, [:posixclass, :print, '[:print:]'], ::Regexp::Expression::PosixClass],
90
+ [:regexp_print_posixclass, [:posixclass, :print, '[:print:]'], ::Regexp::Expression::PosixClass],
91
+ [:regexp_print_property, [:property, :print, '\p{Print}'], ::Regexp::Expression::UnicodeProperty::Print],
92
+ [:regexp_punct_posixclass, [:posixclass, :punct, '[:punct:]'], ::Regexp::Expression::PosixClass],
93
+ [:regexp_set_close_escape, [:escape, :set_close, '\]'], ::Regexp::Expression::EscapeSequence::Literal],
94
+ [:regexp_set_open_escape, [:escape, :set_open, '\['], ::Regexp::Expression::EscapeSequence::Literal],
95
+ [:regexp_space_posixclass, [:posixclass, :space, '[:space:]'], ::Regexp::Expression::PosixClass],
96
+ [:regexp_space_type, [:type, :space, '\s'], ::Regexp::Expression::CharacterType::Space],
97
+ [:regexp_upper_posixclass, [:posixclass, :upper, '[:upper:]'], ::Regexp::Expression::PosixClass],
98
+ [:regexp_vertical_tab_escape, [:escape, :vertical_tab, '\v'], ::Regexp::Expression::EscapeSequence::VerticalTab],
99
+ [:regexp_word_boundary_anchor, [:anchor, :word_boundary, '\b'], ::Regexp::Expression::Anchor::WordBoundary],
100
+ [:regexp_word_posixclass, [:posixclass, :word, '[:word:]'], ::Regexp::Expression::PosixClass],
101
+ [:regexp_word_type, [:type, :word, '\w'], ::Regexp::Expression::CharacterType::Word],
102
+ [:regexp_xdigit_posixclass, [:posixclass, :xdigit, '[:xdigit:]'], ::Regexp::Expression::PosixClass],
103
+ [:regexp_xgrapheme_type, [:type, :xgrapheme, '\X'], ::Regexp::Expression::CharacterType::ExtendedGrapheme],
104
+ [:regexp_zero_or_more_escape, [:escape, :zero_or_more, '\*'], ::Regexp::Expression::EscapeSequence::Literal],
105
+ [:regexp_zero_or_one_escape, [:escape, :zero_or_one, '\?'], ::Regexp::Expression::EscapeSequence::Literal]
106
+ )
107
+ # rubocop:enable Layout/LineLength
108
+
109
+ private
110
+
111
+ def transform
112
+ expression_class.new(expression_token)
113
+ end
114
+ end # ASTToExpression
115
+
116
+ ASTToExpression::TABLE.types.each(&method(:register))
117
+ end # Direct
118
+ end # Transformer
119
+ end # Regexp
120
+ end # AST
121
+ end # Mutant
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mutant
4
+ module AST
5
+ module Regexp
6
+ class Transformer
7
+ # Transformer for named groups
8
+ class NamedGroup < self
9
+ register :regexp_named_group
10
+
11
+ # Mapper from `Regexp::Expression` to `Parser::AST::Node`
12
+ class ExpressionToAST < Transformer::ExpressionToAST
13
+
14
+ # Transform named group into node
15
+ #
16
+ # @return [Parser::AST::Node]
17
+ def call
18
+ quantify(ast(expression.name, *children))
19
+ end
20
+ end # ExpressionToAST
21
+
22
+ # Mapper from `Parser::AST::Node` to `Regexp::Expression`
23
+ class ASTToExpression < Transformer::ASTToExpression
24
+ include NamedChildren
25
+
26
+ children :name
27
+
28
+ private
29
+
30
+ def transform
31
+ named_group.tap do |expression|
32
+ expression.expressions = subexpressions
33
+ end
34
+ end
35
+
36
+ def subexpressions
37
+ remaining_children.map(&Regexp.public_method(:to_expression))
38
+ end
39
+
40
+ def named_group
41
+ ::Regexp::Expression::Group::Named.new(
42
+ ::Regexp::Token.new(:group, :named, "(?<#{name}>")
43
+ )
44
+ end
45
+ end # ASTToExpression
46
+ end # NamedGroup
47
+ end # Transformer
48
+ end # Regexp
49
+ end # AST
50
+ end # Mutant