mutant 0.11.17 → 0.11.18

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.
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class 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
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class AST
5
- module Regexp
6
- class Transformer
7
- # Transformer for option groups
8
- class OptionsGroup < self
9
- register :regexp_options_group
10
- register :regexp_options_switch_group
11
-
12
- # Mapper from `Regexp::Expression` to `Parser::AST::Node`
13
- class ExpressionToAST < Transformer::ExpressionToAST
14
-
15
- # Transform options group into node
16
- #
17
- # @return [Parser::AST::Node]
18
- def call
19
- quantify(ast(expression.option_changes, *children))
20
- end
21
- end # ExpressionToAST
22
-
23
- # Mapper from `Parser::AST::Node` to `Regexp::Expression`
24
- class ASTToExpression < Transformer::ASTToExpression
25
- include NamedChildren
26
-
27
- children :option_changes
28
-
29
- private
30
-
31
- def transform
32
- options_group.tap do |expression|
33
- expression.expressions = subexpressions
34
- end
35
- end
36
-
37
- def subexpressions
38
- remaining_children.map(&Regexp.public_method(:to_expression))
39
- end
40
-
41
- def options_group
42
- ::Regexp::Expression::Group::Options.new(
43
- ::Regexp::Token.new(:group, type, text)
44
- )
45
- end
46
-
47
- def type
48
- {
49
- regexp_options_group: :options,
50
- regexp_options_switch_group: :options_switch
51
- }.fetch(node.type)
52
- end
53
-
54
- def text
55
- pos, neg = option_changes.partition { |_opt, val| val }.map do |arr|
56
- arr.map(&:first).join
57
- end
58
- neg_opt_sep = '-' unless neg.empty?
59
- content_sep = ':' unless type.equal?(:options_switch)
60
-
61
- "(?#{pos}#{neg_opt_sep}#{neg}#{content_sep}"
62
- end
63
- end # ASTToExpression
64
- end # OptionsGroup
65
- end # Transformer
66
- end # Regexp
67
- end # AST
68
- end # Mutant
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class AST
5
- module Regexp
6
- class Transformer
7
- # Transformer for regexp quantifiers
8
- class Quantifier < self
9
- # Mapper from `Regexp::Expression` to `Parser::AST::Node`
10
- class ExpressionToAST < Transformer::ExpressionToAST
11
- # Transform quantifier into node
12
- #
13
- # @return [Parser::AST::Node]
14
- def call
15
- ast(expression.min, expression.max)
16
- end
17
-
18
- private
19
-
20
- def type
21
- :"regexp_#{expression.mode}_#{expression.token}"
22
- end
23
- end # ExpressionToAST
24
-
25
- # Mapper from `Parser::AST::Node` to `Regexp::Expression`
26
- class ASTToExpression < Transformer::ASTToExpression
27
- include NamedChildren
28
-
29
- children :min, :max, :subject
30
-
31
- Quantifier = Class.new.include(Concord::Public.new(:type, :suffix, :mode))
32
-
33
- QUANTIFIER_MAP = {
34
- regexp_greedy_zero_or_more: [:zero_or_more, '*', :greedy],
35
- regexp_greedy_one_or_more: [:one_or_more, '+', :greedy],
36
- regexp_greedy_zero_or_one: [:zero_or_one, '?', :greedy],
37
- regexp_possessive_zero_or_one: [:zero_or_one, '?+', :possessive],
38
- regexp_reluctant_zero_or_more: [:zero_or_more, '*?', :reluctant],
39
- regexp_reluctant_one_or_more: [:one_or_more, '+?', :reluctant],
40
- regexp_possessive_zero_or_more: [:zero_or_more, '*+', :possessive],
41
- regexp_possessive_one_or_more: [:one_or_more, '++', :possessive],
42
- regexp_greedy_interval: [:interval, '', :greedy],
43
- regexp_reluctant_interval: [:interval, '?', :reluctant],
44
- regexp_possessive_interval: [:interval, '+', :possessive]
45
- }.transform_values { |arguments| Quantifier.new(*arguments) }
46
- .to_h
47
- .freeze
48
-
49
- private
50
-
51
- def transform
52
- Regexp.to_expression(subject).dup.tap do |expression|
53
- expression.quantify(type, text, min, max, mode)
54
- end
55
- end
56
-
57
- def text
58
- if type.equal?(:interval)
59
- interval_text + suffix
60
- else
61
- suffix
62
- end
63
- end
64
-
65
- def type
66
- quantifier.type
67
- end
68
-
69
- def suffix
70
- quantifier.suffix
71
- end
72
-
73
- def mode
74
- quantifier.mode
75
- end
76
-
77
- def quantifier
78
- QUANTIFIER_MAP.fetch(node.type)
79
- end
80
-
81
- def interval_text
82
- interval = [min, max].map { |num| num if num.positive? }.uniq
83
- "{#{interval.join(',')}}"
84
- end
85
- end # ASTToExpression
86
-
87
- ASTToExpression::QUANTIFIER_MAP.keys.each(&method(:register))
88
- end # Quantifier
89
- end # Transformer
90
- end # Regexp
91
- end # AST
92
- end # Mutant
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class AST
5
- module Regexp
6
- class Transformer
7
- # Transformer for nodes with children
8
- class Recursive < self
9
- # Mapper from `Regexp::Expression` to `Parser::AST::Node`
10
- class ExpressionToAST < Transformer::ExpressionToAST
11
- # Transform expression and children into nodes
12
- #
13
- # @return [Parser::AST::Node]
14
- def call
15
- quantify(ast(*children))
16
- end
17
- end # ExpressionToAST
18
-
19
- # Mapper from `Parser::AST::Node` to `Regexp::Expression`
20
- class ASTToExpression < Transformer::ASTToExpression
21
- include LookupTable
22
-
23
- # Expression::Sequence represents conditional branches, alternation branches, and intersection branches
24
- # rubocop:disable Layout/LineLength
25
- TABLE = Table.create(
26
- [:regexp_alternation_meta, [:meta, :alternation, '|'], ::Regexp::Expression::Alternation],
27
- [:regexp_atomic_group, [:group, :atomic, '(?>'], ::Regexp::Expression::Group::Atomic],
28
- [:regexp_capture_group, [:group, :capture, '('], ::Regexp::Expression::Group::Capture],
29
- [:regexp_character_set, [:set, :character, '['], ::Regexp::Expression::CharacterSet],
30
- [:regexp_intersection_set, [:set, :intersection, '&&'], ::Regexp::Expression::CharacterSet::Intersection],
31
- [:regexp_lookahead_assertion, [:assertion, :lookahead, '(?='], ::Regexp::Expression::Assertion::Lookahead],
32
- [:regexp_lookbehind_assertion, [:assertion, :lookbehind, '(?<='], ::Regexp::Expression::Assertion::Lookbehind],
33
- [:regexp_nlookahead_assertion, [:assertion, :nlookahead, '(?!'], ::Regexp::Expression::Assertion::NegativeLookahead],
34
- [:regexp_nlookbehind_assertion, [:assertion, :nlookbehind, '(?<!'], ::Regexp::Expression::Assertion::NegativeLookbehind],
35
- [:regexp_open_conditional, [:conditional, :open, '(?'], ::Regexp::Expression::Conditional::Expression],
36
- [:regexp_passive_group, [:group, :passive, '(?:'], ::Regexp::Expression::Group::Passive],
37
- [:regexp_range_set, [:set, :range, '-'], ::Regexp::Expression::CharacterSet::Range],
38
- [:regexp_sequence_expression, [:expression, :sequence, ''], ::Regexp::Expression::Sequence]
39
- )
40
- # rubocop:enable Layout/LineLength
41
-
42
- private
43
-
44
- def transform
45
- expression_class.new(expression_token).tap do |expression|
46
- expression.expressions = subexpressions
47
- end
48
- end
49
- end # ASTToExpression
50
-
51
- ASTToExpression::TABLE.types.each(&method(:register))
52
- end # Recursive
53
- end # Transformer
54
- end # Regexp
55
- end # AST
56
- end # Mutant
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class AST
5
- module Regexp
6
- class Transformer
7
- # Transformer for root nodes
8
- class Root < self
9
- register :regexp_root_expression
10
-
11
- ExpressionToAST = Class.new(Recursive::ExpressionToAST)
12
-
13
- # Mapper from `Parser::AST::Node` to `Regexp::Expression`
14
- class ASTToExpression < Transformer::ASTToExpression
15
-
16
- private
17
-
18
- def transform
19
- ::Regexp::Expression::Root.build.tap do |root|
20
- root.expressions = subexpressions
21
- end
22
- end
23
- end # ASTToExpression
24
- end # Root
25
- end # Transformer
26
- end # Regexp
27
- end # AST
28
- end # Mutant
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class AST
5
- module Regexp
6
- class Transformer
7
- # Regexp AST transformer for nodes that encode a text value
8
- class Text < self
9
- # Mapper from `Regexp::Expression` to `Parser::AST::Node`
10
- class ExpressionToAST < Transformer::ExpressionToAST
11
- # Transform expression into node preserving text value
12
- #
13
- # @return [Parser::AST::Node]
14
- def call
15
- quantify(ast(expression.text))
16
- end
17
- end # ExpressionToAST
18
-
19
- # Mapper from `Parser::AST::Node` to `Regexp::Expression`
20
- class ASTToExpression < Transformer::ASTToExpression
21
- include LookupTable
22
-
23
- # rubocop:disable Layout/LineLength
24
- TABLE = Table.create(
25
- [:regexp_literal_literal, %i[literal literal], ::Regexp::Expression::Literal],
26
- [:regexp_comment_group, %i[group comment], ::Regexp::Expression::Group::Comment],
27
- [:regexp_number_backref, %i[backref number], ::Regexp::Expression::Backreference::Number],
28
- [:regexp_name_call_backref, %i[backref name_call], ::Regexp::Expression::Backreference::NameCall],
29
- [:regexp_whitespace_free_space, %i[free_space whitespace], ::Regexp::Expression::WhiteSpace],
30
- [:regexp_comment_free_space, %i[free_space comment], ::Regexp::Expression::WhiteSpace],
31
- [:regexp_hex_escape, %i[escape hex], ::Regexp::Expression::EscapeSequence::Hex],
32
- [:regexp_octal_escape, %i[escape octal], ::Regexp::Expression::EscapeSequence::Octal],
33
- [:regexp_literal_escape, %i[escape literal], ::Regexp::Expression::EscapeSequence::Literal],
34
- [:regexp_backslash_escape, %i[escape backslash], ::Regexp::Expression::EscapeSequence::Literal],
35
- [:regexp_tab_escape, %i[escape tab], ::Regexp::Expression::EscapeSequence::Literal],
36
- [:regexp_codepoint_list_escape, %i[escape codepoint_list], ::Regexp::Expression::EscapeSequence::CodepointList],
37
- [:regexp_codepoint_escape, %i[escape codepoint], ::Regexp::Expression::EscapeSequence::Codepoint],
38
- [:regexp_control_escape, %i[escape control], ::Regexp::Expression::EscapeSequence::Control],
39
- [:regexp_meta_sequence_escape, %i[escape meta_sequence], ::Regexp::Expression::EscapeSequence::Control],
40
- [:regexp_condition_conditional, %i[conditional condition], ::Regexp::Expression::Conditional::Condition]
41
- )
42
- # rubocop:enable Layout/LineLength
43
-
44
- private
45
-
46
- def transform
47
- token = expression_token.dup
48
- token.text = Util.one(node.children)
49
- expression_class.new(token)
50
- end
51
- end # ASTToExpression
52
-
53
- ASTToExpression::TABLE.types.each(&method(:register))
54
- end # Text
55
- end # Transformer
56
- end # Regexp
57
- end # AST
58
- end # Mutant
@@ -1,152 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class 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
- ->(type) { fail "No regexp transformer registered for: #{type}" }
16
- )
17
-
18
- # Lookup transformer class for regular expression node type
19
- #
20
- # @param type [Symbol]
21
- #
22
- # @return [Class<Transformer>]
23
- def self.lookup(type)
24
- REGISTRY.lookup(type)
25
- end
26
-
27
- def self.register(type)
28
- REGISTRY.register(type, self)
29
- end
30
- private_class_method :register
31
-
32
- # Transform expression
33
- #
34
- # @param expression [Regexp::Expression]
35
- #
36
- # @return [Parser::AST::Node]
37
- def self.to_ast(expression)
38
- self::ExpressionToAST.call(expression)
39
- end
40
-
41
- # Transform node
42
- #
43
- # @param node [Parser::AST::Node]
44
- #
45
- # @return [Regexp::Expression]
46
- def self.to_expression(node)
47
- self::ASTToExpression.call(node)
48
- end
49
-
50
- # Abstract expression transformer
51
- class ExpressionToAST
52
- PREFIX = :regexp
53
-
54
- include Concord.new(:expression), Procto, AST::Sexp, AbstractType, Adamantium
55
-
56
- private
57
-
58
- def ast(*children)
59
- s(type, *children)
60
- end
61
-
62
- def quantify(node)
63
- return node unless expression.quantified?
64
-
65
- Quantifier.to_ast(expression.quantifier).append(node)
66
- end
67
-
68
- def children
69
- expression.map(&Regexp.public_method(:to_ast))
70
- end
71
-
72
- def type
73
- :"#{PREFIX}_#{expression.token}_#{expression.type}"
74
- end
75
- end # ExpressionToAST
76
-
77
- # Abstract node transformer
78
- class ASTToExpression
79
- include Concord.new(:node), Procto, AbstractType, Adamantium
80
-
81
- # Call generic transform method and freeze result
82
- #
83
- # @return [Regexp::Expression]
84
- def call
85
- transform.freeze
86
- end
87
-
88
- private
89
-
90
- abstract_method :transform
91
-
92
- def subexpressions
93
- node.children.map(&Regexp.public_method(:to_expression))
94
- end
95
- end # ASTToExpression
96
-
97
- # Mixin for node transformers
98
- #
99
- # Helps construct a mapping from Parser::AST::Node domain to
100
- # Regexp::Expression domain
101
- module LookupTable
102
- Mapping = Class.new.include(Concord::Public.new(:token, :regexp_class))
103
-
104
- # Table mapping ast types to object information for regexp domain
105
- class Table
106
-
107
- # Coerce array of mapping information into structured table
108
- #
109
- # @param [Array(Symbol, Array, Class<Regexp::Expression>)]
110
- #
111
- # @return [Table]
112
- def self.create(*rows)
113
- table = rows.to_h do |ast_type, token, klass|
114
- [ast_type, Mapping.new(::Regexp::Token.new(*token), klass)]
115
- end
116
-
117
- new(table)
118
- end
119
-
120
- include Concord.new(:table), Adamantium
121
-
122
- # Types defined by the table
123
- #
124
- # @return [Array<Symbol>]
125
- def types
126
- table.keys
127
- end
128
-
129
- # Lookup mapping information given an ast node type
130
- #
131
- # @param type [Symbol]
132
- #
133
- # @return [Mapping]
134
- def lookup(type)
135
- table.fetch(type)
136
- end
137
- end # Table
138
-
139
- private
140
-
141
- def expression_token
142
- self.class::TABLE.lookup(node.type).token
143
- end
144
-
145
- def expression_class
146
- self.class::TABLE.lookup(node.type).regexp_class
147
- end
148
- end # LookupTable
149
- end # Transformer
150
- end # Regexp
151
- end # AST
152
- end # Mutant
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class 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
-
36
- # Convert's a `parser` `regexp` node into more fine-grained AST nodes.
37
- #
38
- # @param node [Parser::AST::Node]
39
- #
40
- # @return [Parser::AST::Node]
41
- def self.expand_regexp_ast(node)
42
- *body, _opts = node.children
43
-
44
- # NOTE: We only mutate parts of regexp body if the body is composed of
45
- # only strings. Regular expressions with interpolation are skipped
46
- return unless body.all? { |child| child.type.equal?(:str) }
47
-
48
- body_expression = parse(body.map(&:children).join)
49
-
50
- to_ast(body_expression)
51
- end
52
- end # Regexp
53
- end # AST
54
- end # Mutant
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class Mutator
5
- class Node
6
- module Regexp
7
- # Mutator for pipe in `/foo|bar/` regexp
8
- class AlternationMeta < Node
9
- handle(:regexp_alternation_meta)
10
-
11
- private
12
-
13
- def dispatch
14
- children.each_index(&method(:delete_child))
15
- end
16
- end # AlternationMeta
17
- end # Regexp
18
- end # Node
19
- end # Mutator
20
- end # Mutant
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class Mutator
5
- class Node
6
- module Regexp
7
- # Mutator for beginning of line anchor `^`
8
- class BeginningOfLineAnchor < Node
9
- handle(:regexp_bol_anchor)
10
-
11
- private
12
-
13
- def dispatch
14
- emit(s(:regexp_bos_anchor))
15
- end
16
- end # BeginningOfLineAnchor
17
- end # Regexp
18
- end # Node
19
- end # Mutator
20
- end # Mutant
@@ -1,23 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class Mutator
5
- class Node
6
- module Regexp
7
- # Mutator for regexp capture groups, such as `/(foo)/`
8
- class CaptureGroup < Node
9
- handle(:regexp_capture_group)
10
-
11
- private
12
-
13
- def dispatch
14
- return if children.empty?
15
-
16
- emit(s(:regexp_passive_group, *children))
17
- children.each_index(&method(:mutate_child))
18
- end
19
- end # EndOfLineAnchor
20
- end # Regexp
21
- end # Node
22
- end # Mutator
23
- end # Mutant
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class Mutator
5
- class Node
6
- module Regexp
7
- # Character type mutator
8
- class CharacterType < Node
9
- map = {
10
- regexp_digit_type: :regexp_nondigit_type,
11
- regexp_hex_type: :regexp_nonhex_type,
12
- regexp_space_type: :regexp_nonspace_type,
13
- regexp_word_boundary_anchor: :regexp_nonword_boundary_anchor,
14
- regexp_word_type: :regexp_nonword_type,
15
- regexp_xgrapheme_type: :regexp_linebreak_type
16
- }
17
-
18
- MAP = map.merge(map.invert)
19
-
20
- handle(*MAP.keys)
21
-
22
- private
23
-
24
- def dispatch
25
- emit(s(MAP.fetch(node.type)))
26
- end
27
- end # CharacterType
28
- end # Regexp
29
- end # Node
30
- end # Mutator
31
- end # Mutant
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Mutant
4
- class Mutator
5
- class Node
6
- module Regexp
7
- # Mutator for end of line anchor `$`
8
- class EndOfLineAnchor < Node
9
- handle(:regexp_eol_anchor)
10
-
11
- private
12
-
13
- def dispatch
14
- emit(s(:regexp_eos_anchor))
15
- end
16
- end # EndOfLineAnchor
17
- end # Regexp
18
- end # Node
19
- end # Mutator
20
- end # Mutant