mutant 0.11.17 → 0.11.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -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