synvert-core 0.63.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +9 -1
- data/Guardfile +11 -2
- data/README.md +74 -34
- data/Rakefile +15 -1
- data/lib/synvert/core/array_ext.rb +41 -0
- data/lib/synvert/core/configuration.rb +12 -0
- data/lib/synvert/core/engine/erb.rb +9 -8
- data/lib/synvert/core/exceptions.rb +0 -4
- data/lib/synvert/core/node_ext.rb +232 -128
- data/lib/synvert/core/node_query/compiler/array.rb +34 -0
- data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
- data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
- data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
- data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
- data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
- data/lib/synvert/core/node_query/compiler/float.rb +23 -0
- data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
- data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
- data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
- data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
- data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
- data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
- data/lib/synvert/core/node_query/compiler/string.rb +34 -0
- data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
- data/lib/synvert/core/node_query/compiler.rb +24 -0
- data/lib/synvert/core/node_query/lexer.rex +96 -0
- data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
- data/lib/synvert/core/node_query/parser.racc.rb +518 -0
- data/lib/synvert/core/node_query/parser.y +84 -0
- data/lib/synvert/core/node_query.rb +36 -0
- data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
- data/lib/synvert/core/rewriter/action/delete_action.rb +17 -8
- data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
- data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
- data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
- data/lib/synvert/core/rewriter/action/replace_action.rb +15 -5
- data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
- data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
- data/lib/synvert/core/rewriter/action/wrap_action.rb +16 -7
- data/lib/synvert/core/rewriter/action.rb +22 -10
- data/lib/synvert/core/rewriter/any_value.rb +1 -0
- data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
- data/lib/synvert/core/rewriter/condition.rb +11 -3
- data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
- data/lib/synvert/core/rewriter/helper.rb +7 -4
- data/lib/synvert/core/rewriter/instance.rb +217 -104
- data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
- data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
- data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
- data/lib/synvert/core/rewriter/scope/within_scope.rb +10 -5
- data/lib/synvert/core/rewriter/scope.rb +8 -0
- data/lib/synvert/core/rewriter/warning.rb +1 -1
- data/lib/synvert/core/rewriter.rb +91 -43
- data/lib/synvert/core/version.rb +1 -1
- data/lib/synvert/core.rb +22 -6
- data/spec/synvert/core/engine/erb_spec.rb +2 -2
- data/spec/synvert/core/node_ext_spec.rb +36 -12
- data/spec/synvert/core/node_query/lexer_spec.rb +512 -0
- data/spec/synvert/core/node_query/parser_spec.rb +270 -0
- data/spec/synvert/core/rewriter/action_spec.rb +0 -4
- data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
- data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
- data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
- data/spec/synvert/core/rewriter/instance_spec.rb +31 -20
- data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
- data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
- data/spec/synvert/core/rewriter_spec.rb +4 -2
- data/synvert-core-ruby.gemspec +7 -2
- metadata +91 -4
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Attribute is a pair of key, value and operator,
|
5
|
+
class Attribute
|
6
|
+
# Initialize a Attribute.
|
7
|
+
# @param key [String] the key
|
8
|
+
# @param value the value can be any class implement {Synvert::Core::NodeQuery::Compiler::Comparable}
|
9
|
+
# @param operator [Symbol] the operator
|
10
|
+
def initialize(key:, value:, operator: :==)
|
11
|
+
@key = key
|
12
|
+
@value = value
|
13
|
+
@operator = operator
|
14
|
+
end
|
15
|
+
|
16
|
+
# Check if the node matches the attribute.
|
17
|
+
# @param node [Parser::AST::Node] the node
|
18
|
+
# @return [Boolean]
|
19
|
+
def match?(node)
|
20
|
+
@value.base_node = node if @value.is_a?(DynamicAttribute)
|
21
|
+
node && @value.match?(node.child_node_by_name(@key), @operator)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
case @operator
|
26
|
+
when :!=
|
27
|
+
"#{@key}!=#{@value}"
|
28
|
+
when :=~
|
29
|
+
"#{@key}=~#{@value}"
|
30
|
+
when :!~
|
31
|
+
"#{@key}!~#{@value}"
|
32
|
+
when :>
|
33
|
+
"#{@key}>#{@value}"
|
34
|
+
when :>=
|
35
|
+
"#{@key}>=#{@value}"
|
36
|
+
when :<
|
37
|
+
"#{@key}<#{@value}"
|
38
|
+
when :<=
|
39
|
+
"#{@key}<=#{@value}"
|
40
|
+
when :in
|
41
|
+
"#{@key} in (#{@value})"
|
42
|
+
when :not_in
|
43
|
+
"#{@key} not in (#{@value})"
|
44
|
+
when :includes
|
45
|
+
"#{@key} includes #{@value}"
|
46
|
+
else
|
47
|
+
"#{@key}=#{@value}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# AttributeList contains one or more {Synvert::Core::NodeQuery::Compiler::Attribute}.
|
5
|
+
class AttributeList
|
6
|
+
# Initialize a AttributeList.
|
7
|
+
# @param attribute [Synvert::Core::NodeQuery::Compiler::Attribute] the attribute
|
8
|
+
# @param rest [Synvert::Core::NodeQuery::Compiler::AttributeList] the rest attribute list
|
9
|
+
def initialize(attribute:, rest: nil)
|
10
|
+
@attribute = attribute
|
11
|
+
@rest = rest
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check if the node matches the attribute list.
|
15
|
+
# @return [Boolean]
|
16
|
+
def match?(node)
|
17
|
+
@attribute.match?(node) && (!@rest || @rest.match?(node))
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"[#{@attribute}]#{@rest}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Boolean represents a ruby boolean value.
|
5
|
+
class Boolean
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a Boolean.
|
9
|
+
# @param value [Boolean] the boolean value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
SIMPLE_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@value.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Compare acutal value with expected value.
|
5
|
+
module Comparable
|
6
|
+
SIMPLE_VALID_OPERATORS = [:==, :!=, :includes]
|
7
|
+
NUMBER_VALID_OPERATORS = [:==, :!=, :>, :>=, :<, :<=, :includes]
|
8
|
+
ARRAY_VALID_OPERATORS = [:==, :!=, :in, :not_in]
|
9
|
+
REGEXP_VALID_OPERATORS = [:=~, :!~]
|
10
|
+
|
11
|
+
# Check if the actual value matches the expected value.
|
12
|
+
#
|
13
|
+
# @param node [Parser::AST::Node] node to calculate actual value
|
14
|
+
# @param operator [Symbol] operator to compare with expected value, operator can be <code>:==</code>, <code>:!=</code>, <code>:></code>, <code>:>=</code>, <code>:<</code>, <code>:<=</code>, <code>:includes</code>, <code>:in</code>, <code>:not_in</code>, <code>:=~</code>, <code>!~</code>
|
15
|
+
# @return [Boolean] true if actual value matches the expected value
|
16
|
+
# @raise [Synvert::Core::NodeQuery::Compiler::InvalidOperatorError] if operator is invalid
|
17
|
+
def match?(node, operator)
|
18
|
+
raise InvalidOperatorError, "invalid operator #{operator}" unless valid_operator?(operator)
|
19
|
+
|
20
|
+
case operator
|
21
|
+
when :!=
|
22
|
+
if expected_value.is_a?(::Array)
|
23
|
+
actual = actual_value(node)
|
24
|
+
!actual.is_a?(::Array) || actual.size != expected_value.size ||
|
25
|
+
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, :!=) }
|
26
|
+
else
|
27
|
+
actual_value(node) != expected_value
|
28
|
+
end
|
29
|
+
when :=~
|
30
|
+
actual_value(node) =~ expected_value
|
31
|
+
when :!~
|
32
|
+
actual_value(node) !~ expected_value
|
33
|
+
when :>
|
34
|
+
actual_value(node) > expected_value
|
35
|
+
when :>=
|
36
|
+
actual_value(node) >= expected_value
|
37
|
+
when :<
|
38
|
+
actual_value(node) < expected_value
|
39
|
+
when :<=
|
40
|
+
actual_value(node) <= expected_value
|
41
|
+
when :in
|
42
|
+
expected_value.any? { |expected| expected.match?(node, :==) }
|
43
|
+
when :not_in
|
44
|
+
expected_value.all? { |expected| expected.match?(node, :!=) }
|
45
|
+
when :includes
|
46
|
+
actual_value(node).any? { |actual| actual == expected_value }
|
47
|
+
else
|
48
|
+
if expected_value.is_a?(::Array)
|
49
|
+
actual = actual_value(node)
|
50
|
+
actual.is_a?(::Array) && actual.size == expected_value.size &&
|
51
|
+
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, :==) }
|
52
|
+
else
|
53
|
+
actual_value(node) == expected_value
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get the actual value from ast node.
|
59
|
+
# @return if node is a {Parser::AST::Node}, return the node value, otherwise, return the node itself.
|
60
|
+
def actual_value(node)
|
61
|
+
if node.is_a?(::Parser::AST::Node)
|
62
|
+
node.to_value
|
63
|
+
else
|
64
|
+
node
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get the expected value
|
69
|
+
def expected_value
|
70
|
+
@value
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check if the operator is valid.
|
74
|
+
# @return [Boolean] true if the operator is valid
|
75
|
+
def valid_operator?(operator)
|
76
|
+
valid_operators.include?(operator)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# DynamicAttribute represents a ruby dynamic attribute.
|
5
|
+
# e.g. code is <code>{ a: a }</code>, query is <code>'.hash > .pair[key={{value}}]'</code>,
|
6
|
+
# <code>{{value}}</code> is the dynamic attribute.
|
7
|
+
class DynamicAttribute
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_accessor :base_node
|
11
|
+
|
12
|
+
# Initialize a DynamicAttribute.
|
13
|
+
# @param value [String] the dynamic attribute value
|
14
|
+
def initialize(value:)
|
15
|
+
@value = value
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the actual value of a node.
|
19
|
+
#
|
20
|
+
# @param node the node
|
21
|
+
# @return [String] if node is a {Parser::AST::Node}, return the node source code, otherwise return the node itself.
|
22
|
+
def actual_value(node)
|
23
|
+
if node.is_a?(::Parser::AST::Node)
|
24
|
+
node.to_source
|
25
|
+
else
|
26
|
+
node
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the expected value.
|
31
|
+
#
|
32
|
+
# @return [String] Query the node by @valud from base_node, if the node is a {Parser::AST::Node}, return the node source code, otherwise return the node itself.
|
33
|
+
def expected_value
|
34
|
+
node = base_node.child_node_by_name(@value)
|
35
|
+
if node.is_a?(::Parser::AST::Node)
|
36
|
+
node.to_source
|
37
|
+
else
|
38
|
+
node
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get valid operators.
|
43
|
+
def valid_operators
|
44
|
+
SIMPLE_VALID_OPERATORS
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_s
|
48
|
+
"{{#{@value}}}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Expression represents a node query expression.
|
5
|
+
class Expression
|
6
|
+
# Initialize a Expression.
|
7
|
+
# @param selector [Synvert::Core::NodeQuery::Compiler::Selector] the selector
|
8
|
+
# @param rest [Synvert::Core::NodeQuery::Compiler::Expression] the rest expression
|
9
|
+
# @param relationship [Symbol] the relationship between the selector and rest expression, it can be <code>:descendant</code>, <code>:child</code>, <code>:next_sibling</code>, <code>:subsequent_sibling</code> or <code>nil</code>.
|
10
|
+
def initialize(selector: nil, rest: nil, relationship: nil)
|
11
|
+
@selector = selector
|
12
|
+
@rest = rest
|
13
|
+
@relationship = relationship
|
14
|
+
end
|
15
|
+
|
16
|
+
# Check if the node matches the expression.
|
17
|
+
# @param node [Parser::AST::Node] the node
|
18
|
+
# @return [Boolean]
|
19
|
+
def match?(node)
|
20
|
+
!query_nodes(node).empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Query nodes by the expression.
|
24
|
+
#
|
25
|
+
# * If relationship is nil, it will match in all recursive child nodes and return matching nodes.
|
26
|
+
# * If relationship is :decendant, it will match in all recursive child nodes.
|
27
|
+
# * If relationship is :child, it will match in direct child nodes.
|
28
|
+
# * If relationship is :next_sibling, it try to match next sibling node.
|
29
|
+
# * If relationship is :subsequent_sibling, it will match in all sibling nodes.
|
30
|
+
# @param node [Parser::AST::Node] node to match
|
31
|
+
# @param descendant_match [Boolean] whether to match in descendant node
|
32
|
+
# @return [Array<Parser::AST::Node>] matching nodes.
|
33
|
+
def query_nodes(node, descendant_match: true)
|
34
|
+
matching_nodes = find_nodes_without_relationship(node, descendant_match: descendant_match)
|
35
|
+
if @relationship.nil?
|
36
|
+
return matching_nodes
|
37
|
+
end
|
38
|
+
|
39
|
+
expression_nodes = matching_nodes.map do |matching_node|
|
40
|
+
case @relationship
|
41
|
+
when :descendant
|
42
|
+
nodes = []
|
43
|
+
matching_node.recursive_children { |child_node|
|
44
|
+
nodes += @rest.query_nodes(child_node, descendant_match: false)
|
45
|
+
}
|
46
|
+
nodes
|
47
|
+
when :child
|
48
|
+
matching_node.children.map { |child_node| @rest.query_nodes(child_node, descendant_match: false) }
|
49
|
+
.flatten
|
50
|
+
when :next_sibling
|
51
|
+
@rest.query_nodes(matching_node.siblings.first, descendant_match: false)
|
52
|
+
when :subsequent_sibling
|
53
|
+
matching_node.siblings.map { |sibling_node| @rest.query_nodes(sibling_node, descendant_match: false) }
|
54
|
+
.flatten
|
55
|
+
end
|
56
|
+
end.flatten
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
return @selector.to_s unless @rest
|
61
|
+
|
62
|
+
result = []
|
63
|
+
result << @selector if @selector
|
64
|
+
case @relationship
|
65
|
+
when :child then result << '>'
|
66
|
+
when :subsequent_sibling then result << '~'
|
67
|
+
when :next_sibling then result << '+'
|
68
|
+
end
|
69
|
+
result << @rest
|
70
|
+
result.map(&:to_s).join(' ')
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def find_nodes_without_relationship(node, descendant_match: true)
|
76
|
+
return [node] unless @selector
|
77
|
+
|
78
|
+
nodes = []
|
79
|
+
nodes << node if @selector.match?(node)
|
80
|
+
if descendant_match
|
81
|
+
node.recursive_children do |child_node|
|
82
|
+
nodes << child_node if @selector.match?(child_node)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@selector.filter(nodes)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Float represents a ruby float value.
|
5
|
+
class Float
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a Float.
|
9
|
+
# @param value [Float] the float value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
NUMBER_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@value.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Identifier represents a ruby identifier value.
|
5
|
+
# e.g. code is `class Synvert; end`, `Synvert` is an identifier.
|
6
|
+
class Identifier
|
7
|
+
include Comparable
|
8
|
+
|
9
|
+
# Initialize an Identifier.
|
10
|
+
# @param value [String] the identifier value
|
11
|
+
def initialize(value:)
|
12
|
+
@value = value
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get the actual value.
|
16
|
+
#
|
17
|
+
# @param node the node
|
18
|
+
# @return [String|Array]
|
19
|
+
# If the node is a {Parser::AST::Node}, return the node source code,
|
20
|
+
# if the node is an Array, return the array of each element's actual value,
|
21
|
+
# otherwise, return the String value.
|
22
|
+
def actual_value(node)
|
23
|
+
if node.is_a?(::Parser::AST::Node)
|
24
|
+
node.to_source
|
25
|
+
elsif node.is_a?(::Array)
|
26
|
+
node.map { |n| actual_value(n) }
|
27
|
+
else
|
28
|
+
node.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get valid operators.
|
33
|
+
def valid_operators
|
34
|
+
SIMPLE_VALID_OPERATORS
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
@value
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Integer represents a ruby integer value.
|
5
|
+
class Integer
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a Integer.
|
9
|
+
# @param value [Integer] the integer value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
NUMBER_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
@value.to_s
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Nil represents a ruby nil value.
|
5
|
+
class Nil
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a Nil.
|
9
|
+
# @param value [nil] the nil value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
SIMPLE_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
'nil'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Regexp represents a ruby regexp value.
|
5
|
+
class Regexp
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a Regexp.
|
9
|
+
# @param value [Regexp] the regexp value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check if the regexp value matches the node value.
|
15
|
+
# @param node [Parser::AST::Node] the node
|
16
|
+
# @param operator [Symbol] the operator
|
17
|
+
# @return [Boolean] true if the regexp value matches the node value, otherwise, false.
|
18
|
+
def match?(node, operator = :=~)
|
19
|
+
match =
|
20
|
+
if node.is_a?(::Parser::AST::Node)
|
21
|
+
@value.match(node.to_source)
|
22
|
+
else
|
23
|
+
@value.match(node.to_s)
|
24
|
+
end
|
25
|
+
operator == :=~ ? match : !match
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get valid operators.
|
29
|
+
def valid_operators
|
30
|
+
REGEXP_VALID_OPERATORS
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
@value.to_s
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Selector used to match nodes, it combines by node type and/or attribute list, plus index or has expression.
|
5
|
+
class Selector
|
6
|
+
# Initialize a Selector.
|
7
|
+
# @param node_type [String] the node type
|
8
|
+
# @param attribute_list [Synvert::Core::NodeQuery::Compiler::AttributeList] the attribute list
|
9
|
+
# @param index [Integer] the index
|
10
|
+
# @param has_expression [Synvert::Core::NodeQuery::Compiler::Expression] the has expression
|
11
|
+
def initialize(node_type: nil, attribute_list: nil, index: nil, has_expression: nil)
|
12
|
+
@node_type = node_type
|
13
|
+
@attribute_list = attribute_list
|
14
|
+
@index = index
|
15
|
+
@has_expression = has_expression
|
16
|
+
end
|
17
|
+
|
18
|
+
# Filter nodes by index.
|
19
|
+
def filter(nodes)
|
20
|
+
return nodes if @index.nil?
|
21
|
+
|
22
|
+
nodes[@index] ? [nodes[@index]] : []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Check if node matches the selector.
|
26
|
+
# @param node [Parser::AST::Node] the node
|
27
|
+
def match?(node, _operator = :==)
|
28
|
+
(!@node_type || (node.is_a?(::Parser::AST::Node) && @node_type.to_sym == node.type)) &&
|
29
|
+
(!@attribute_list || @attribute_list.match?(node)) &&
|
30
|
+
(!@has_expression || @has_expression.match?(node))
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
str = ".#{@node_type}#{@attribute_list}"
|
35
|
+
return str if !@index && !@has_expression
|
36
|
+
|
37
|
+
return "#{str}:has(#{@has_expression})" if @has_expression
|
38
|
+
|
39
|
+
case @index
|
40
|
+
when 0
|
41
|
+
str + ':first-child'
|
42
|
+
when -1
|
43
|
+
str + ':last-child'
|
44
|
+
when (1..)
|
45
|
+
str + ":nth-child(#{@index + 1})"
|
46
|
+
else # ...-1
|
47
|
+
str + ":nth-last-child(#{-@index})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# String represents a ruby string value.
|
5
|
+
class String
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize a String.
|
9
|
+
# @param value [String] the string value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
SIMPLE_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get the actual value of a node.
|
20
|
+
# @param node [Parser::AST::Node] the node
|
21
|
+
# @return [String] if node is a Parser::AST::Node, return the node source code, otherwise, return the string value.
|
22
|
+
def actual_value(node)
|
23
|
+
if node.is_a?(::Parser::AST::Node)
|
24
|
+
node.to_source
|
25
|
+
else
|
26
|
+
node.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"\"#{@value}\""
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
# Symbol represents a ruby symbol value.
|
5
|
+
class Symbol
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initliaze a Symobol.
|
9
|
+
# @param value [Symbol] the symbol value
|
10
|
+
def initialize(value:)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
# Get valid operators.
|
15
|
+
def valid_operators
|
16
|
+
SIMPLE_VALID_OPERATORS
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
":#{@value}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Synvert::Core::NodeQuery::Compiler
|
4
|
+
autoload :InvalidOperatorError, 'synvert/core/node_query/compiler/invalid_operator_error'
|
5
|
+
autoload :ParseError, 'synvert/core/node_query/compiler/parse_error'
|
6
|
+
|
7
|
+
autoload :Comparable, 'synvert/core/node_query/compiler/comparable'
|
8
|
+
|
9
|
+
autoload :Expression, 'synvert/core/node_query/compiler/expression'
|
10
|
+
autoload :Selector, 'synvert/core/node_query/compiler/selector'
|
11
|
+
autoload :AttributeList, 'synvert/core/node_query/compiler/attribute_list'
|
12
|
+
autoload :Attribute, 'synvert/core/node_query/compiler/attribute'
|
13
|
+
|
14
|
+
autoload :Array, 'synvert/core/node_query/compiler/array'
|
15
|
+
autoload :Boolean, 'synvert/core/node_query/compiler/boolean'
|
16
|
+
autoload :DynamicAttribute, 'synvert/core/node_query/compiler/dynamic_attribute'
|
17
|
+
autoload :Float, 'synvert/core/node_query/compiler/float'
|
18
|
+
autoload :Identifier, 'synvert/core/node_query/compiler/identifier'
|
19
|
+
autoload :Integer, 'synvert/core/node_query/compiler/integer'
|
20
|
+
autoload :Nil, 'synvert/core/node_query/compiler/nil'
|
21
|
+
autoload :Regexp, 'synvert/core/node_query/compiler/regexp'
|
22
|
+
autoload :String, 'synvert/core/node_query/compiler/string'
|
23
|
+
autoload :Symbol, 'synvert/core/node_query/compiler/symbol'
|
24
|
+
end
|