node_query 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +98 -0
- data/Guardfile +16 -0
- data/README.md +37 -0
- data/Rakefile +22 -0
- data/lib/node_query/adapter.rb +32 -0
- data/lib/node_query/compiler/array_value.rb +35 -0
- data/lib/node_query/compiler/attribute.rb +39 -0
- data/lib/node_query/compiler/attribute_list.rb +25 -0
- data/lib/node_query/compiler/basic_selector.rb +29 -0
- data/lib/node_query/compiler/boolean.rb +24 -0
- data/lib/node_query/compiler/comparable.rb +107 -0
- data/lib/node_query/compiler/evaluated_value.rb +50 -0
- data/lib/node_query/compiler/expression.rb +40 -0
- data/lib/node_query/compiler/expression_list.rb +28 -0
- data/lib/node_query/compiler/float.rb +24 -0
- data/lib/node_query/compiler/identifier.rb +41 -0
- data/lib/node_query/compiler/integer.rb +24 -0
- data/lib/node_query/compiler/invalid_operator_error.rb +7 -0
- data/lib/node_query/compiler/nil.rb +24 -0
- data/lib/node_query/compiler/parse_error.rb +7 -0
- data/lib/node_query/compiler/regexp.rb +38 -0
- data/lib/node_query/compiler/selector.rb +117 -0
- data/lib/node_query/compiler/string.rb +24 -0
- data/lib/node_query/compiler/symbol.rb +24 -0
- data/lib/node_query/compiler.rb +26 -0
- data/lib/node_query/helper.rb +35 -0
- data/lib/node_query/parser_adapter.rb +20 -0
- data/lib/node_query/version.rb +5 -0
- data/lib/node_query.rb +38 -0
- data/lib/node_query_lexer.rex +98 -0
- data/lib/node_query_parser.y +63 -0
- data/node_query.gemspec +36 -0
- data/sig/node_query.rbs +11 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 133039b40806dc003715b5d04f34bbb78d6f3cfe3969e1d10dc920063e8ebe11
|
4
|
+
data.tar.gz: 43d5557d76103476d90c98f2575f0869ed8809abe639ec62d38695cd1131eca7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0cb0b449357c4238ea7d123e16985a82e8fb4a1d2271359616720d0dd2833eac3a07963ff612c7a71a8afdb88ec5014e5f9f35fb22a51acd188e46295d86cae1
|
7
|
+
data.tar.gz: 48ff7474de2c811103c71df2789d81818691e11e8c10303544d15dde4a9362933984ea60b353182fc4b9d16720532756e21c593a15b6a685466275bc880f7de3
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in node_query.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
|
10
|
+
gem "rspec", "~> 3.0"
|
11
|
+
|
12
|
+
gem "guard"
|
13
|
+
gem "guard-rspec"
|
14
|
+
gem "guard-rake"
|
15
|
+
gem "oedipus_lex"
|
16
|
+
gem "racc"
|
17
|
+
gem "parser"
|
18
|
+
gem "parser_node_ext"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
node_query (1.0.0)
|
5
|
+
activesupport
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (7.0.3)
|
11
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
12
|
+
i18n (>= 1.6, < 2)
|
13
|
+
minitest (>= 5.1)
|
14
|
+
tzinfo (~> 2.0)
|
15
|
+
ast (2.4.2)
|
16
|
+
coderay (1.1.3)
|
17
|
+
concurrent-ruby (1.1.10)
|
18
|
+
diff-lcs (1.5.0)
|
19
|
+
ffi (1.15.5)
|
20
|
+
formatador (1.1.0)
|
21
|
+
guard (2.18.0)
|
22
|
+
formatador (>= 0.2.4)
|
23
|
+
listen (>= 2.7, < 4.0)
|
24
|
+
lumberjack (>= 1.0.12, < 2.0)
|
25
|
+
nenv (~> 0.1)
|
26
|
+
notiffany (~> 0.0)
|
27
|
+
pry (>= 0.13.0)
|
28
|
+
shellany (~> 0.0)
|
29
|
+
thor (>= 0.18.1)
|
30
|
+
guard-compat (1.2.1)
|
31
|
+
guard-rake (1.0.0)
|
32
|
+
guard
|
33
|
+
rake
|
34
|
+
guard-rspec (4.7.3)
|
35
|
+
guard (~> 2.1)
|
36
|
+
guard-compat (~> 1.1)
|
37
|
+
rspec (>= 2.99.0, < 4.0)
|
38
|
+
i18n (1.10.0)
|
39
|
+
concurrent-ruby (~> 1.0)
|
40
|
+
listen (3.7.1)
|
41
|
+
rb-fsevent (~> 0.10, >= 0.10.3)
|
42
|
+
rb-inotify (~> 0.9, >= 0.9.10)
|
43
|
+
lumberjack (1.2.8)
|
44
|
+
method_source (1.0.0)
|
45
|
+
minitest (5.16.1)
|
46
|
+
nenv (0.3.0)
|
47
|
+
notiffany (0.1.3)
|
48
|
+
nenv (~> 0.1)
|
49
|
+
shellany (~> 0.0)
|
50
|
+
oedipus_lex (2.6.0)
|
51
|
+
parser (3.1.2.0)
|
52
|
+
ast (~> 2.4.1)
|
53
|
+
parser_node_ext (0.1.0)
|
54
|
+
parser
|
55
|
+
pry (0.14.1)
|
56
|
+
coderay (~> 1.1)
|
57
|
+
method_source (~> 1.0)
|
58
|
+
racc (1.6.0)
|
59
|
+
rake (13.0.6)
|
60
|
+
rb-fsevent (0.11.1)
|
61
|
+
rb-inotify (0.10.1)
|
62
|
+
ffi (~> 1.0)
|
63
|
+
rspec (3.11.0)
|
64
|
+
rspec-core (~> 3.11.0)
|
65
|
+
rspec-expectations (~> 3.11.0)
|
66
|
+
rspec-mocks (~> 3.11.0)
|
67
|
+
rspec-core (3.11.0)
|
68
|
+
rspec-support (~> 3.11.0)
|
69
|
+
rspec-expectations (3.11.0)
|
70
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
71
|
+
rspec-support (~> 3.11.0)
|
72
|
+
rspec-mocks (3.11.1)
|
73
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
74
|
+
rspec-support (~> 3.11.0)
|
75
|
+
rspec-support (3.11.0)
|
76
|
+
shellany (0.0.1)
|
77
|
+
thor (1.2.1)
|
78
|
+
tzinfo (2.0.4)
|
79
|
+
concurrent-ruby (~> 1.0)
|
80
|
+
|
81
|
+
PLATFORMS
|
82
|
+
x86_64-darwin-21
|
83
|
+
x86_64-linux
|
84
|
+
|
85
|
+
DEPENDENCIES
|
86
|
+
guard
|
87
|
+
guard-rake
|
88
|
+
guard-rspec
|
89
|
+
node_query!
|
90
|
+
oedipus_lex
|
91
|
+
parser
|
92
|
+
parser_node_ext
|
93
|
+
racc
|
94
|
+
rake (~> 13.0)
|
95
|
+
rspec (~> 3.0)
|
96
|
+
|
97
|
+
BUNDLED WITH
|
98
|
+
2.3.7
|
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
4
|
+
watch(%r{^spec/.+_spec\.rb$})
|
5
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
6
|
+
watch('lib/node_query_lexer.rex.rb') { 'spec/node_query_lexer_spec.rb' }
|
7
|
+
watch('lib/node_query_compiler.rb') { 'spec/node_query_parser_spec.rb' }
|
8
|
+
watch(%r{^lib/node_query/compiler/.*\.rb$}) { 'spec/node_query_parser_spec.rb' }
|
9
|
+
watch('lib/node_query/parser.racc.rb') { 'spec/node_query_parser_spec.rb' }
|
10
|
+
watch('spec/spec_helper.rb') { "spec" }
|
11
|
+
end
|
12
|
+
|
13
|
+
guard :rake, task: 'generate' do
|
14
|
+
watch('lib/node_query_lexer.rex')
|
15
|
+
watch('lib/node_query_parser.y')
|
16
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# NodeQuery
|
2
|
+
|
3
|
+
NodeQuery defines an AST node query language, which is a css like syntax for matching nodes.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'node_query'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install node_query
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
It provides only one api:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
NodeQuery.new(nodeQueryString).parse(node)
|
27
|
+
```
|
28
|
+
|
29
|
+
## Development
|
30
|
+
|
31
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
32
|
+
|
33
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/xinminlabs/node-query-ruby.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
require 'oedipus_lex'
|
7
|
+
Rake.application.rake_require "oedipus_lex"
|
8
|
+
|
9
|
+
file "lib/node_query_lexer.rex.rb" => "lib/node_query_lexer.rex"
|
10
|
+
file "lib/node_query_parser.racc.rb" => "lib/node_query_parser.y"
|
11
|
+
|
12
|
+
task :lexer => "lib/node_query_lexer.rex.rb"
|
13
|
+
task :parser => "lib/node_query_parser.racc.rb"
|
14
|
+
task :generate => [:lexer, :parser]
|
15
|
+
|
16
|
+
rule '.racc.rb' => '.y' do |t|
|
17
|
+
cmd = "bundle exec racc -l -v -o #{t.name} #{t.source}"
|
18
|
+
sh cmd
|
19
|
+
end
|
20
|
+
|
21
|
+
task :default => :spec
|
22
|
+
task :spec => :generate
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Abstract Adapter class
|
4
|
+
class NodeQuery::Adapter
|
5
|
+
# Get the type of node
|
6
|
+
# @param node [Node] ast node
|
7
|
+
# @return [Symbol] node type
|
8
|
+
def get_node_type(node)
|
9
|
+
raise NotImplementedError, 'get_node_type is not implemented'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get the source code of node
|
13
|
+
# @param node [Node] ast node
|
14
|
+
# @return [String] node source code
|
15
|
+
def get_source(node)
|
16
|
+
raise NotImplementedError, 'get_source is not implemented'
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get the children of node
|
20
|
+
# @param node [Node] ast node
|
21
|
+
# @return [Array<Node>] node children
|
22
|
+
def get_children(node)
|
23
|
+
raise NotImplementedError, 'get_children is not implemented'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get the siblings of node
|
27
|
+
# @param node [Node] ast node
|
28
|
+
# @return [Array<Node>] node siblings
|
29
|
+
def get_siblings(node)
|
30
|
+
raise NotImplementedError, 'get_siblings is not implemented'
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# ArrayValue represents a ruby array value.
|
5
|
+
class ArrayValue
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
# Initialize an Array.
|
9
|
+
# @param value the first value of the array
|
10
|
+
# @param rest the rest value of the array
|
11
|
+
def initialize(value: nil, rest: nil)
|
12
|
+
@value = value
|
13
|
+
@rest = rest
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get the expected value.
|
17
|
+
# @return [Array]
|
18
|
+
def expected_value
|
19
|
+
expected = []
|
20
|
+
expected.push(@value) if @value
|
21
|
+
expected += @rest.expected_value if @rest
|
22
|
+
expected
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get valid operators.
|
26
|
+
# @return [Array] valid operators
|
27
|
+
def valid_operators
|
28
|
+
ARRAY_VALID_OPERATORS
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
[@value, @rest].compact.join(' ')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module 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 {NodeQuery::Compiler::Comparable}
|
9
|
+
# @param operator [String] 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 [Node] the node
|
18
|
+
# @return [Boolean]
|
19
|
+
def match?(node)
|
20
|
+
@value.base_node = node if @value.is_a?(EvaluatedValue)
|
21
|
+
node && @value.match?(NodeQuery::Helper.get_target_node(node, @key), @operator)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
case @operator
|
26
|
+
when '^=', '$=', '*=', '!=', '=~', '!~', '>=', '>', '<=', '<'
|
27
|
+
"#{@key}#{@operator}#{@value}"
|
28
|
+
when 'in'
|
29
|
+
"#{@key} in (#{@value})"
|
30
|
+
when 'not_in'
|
31
|
+
"#{@key} not in (#{@value})"
|
32
|
+
when 'includes'
|
33
|
+
"#{@key} includes #{@value}"
|
34
|
+
else
|
35
|
+
"#{@key}=#{@value}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# AttributeList contains one or more {NodeQuery::Compiler::Attribute}.
|
5
|
+
class AttributeList
|
6
|
+
# Initialize an AttributeList.
|
7
|
+
# @param attribute [NodeQuery::Compiler::Attribute] the attribute
|
8
|
+
# @param rest [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
|
+
# @param node [Node] the node
|
16
|
+
# @return [Boolean]
|
17
|
+
def match?(node)
|
18
|
+
@attribute.match?(node) && (!@rest || @rest.match?(node))
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
"[#{@attribute}]#{@rest}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# BasicSelector used to match nodes, it combines by node type and/or attribute list.
|
5
|
+
class BasicSelector
|
6
|
+
# Initialize a BasicSelector.
|
7
|
+
# @param node_type [String] the node type
|
8
|
+
# @param attribute_list [NodeQuery::Compiler::AttributeList] the attribute list
|
9
|
+
def initialize(node_type:, attribute_list: nil)
|
10
|
+
@node_type = node_type
|
11
|
+
@attribute_list = attribute_list
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check if node matches the selector.
|
15
|
+
# @param node [Node] the node
|
16
|
+
# @return [Boolean]
|
17
|
+
def match?(node, _operator = '==')
|
18
|
+
return false unless node
|
19
|
+
|
20
|
+
@node_type.to_sym == NodeQuery.get_adapter.get_node_type(node) && (!@attribute_list || @attribute_list.match?(node))
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
result = [".#{@node_type}"]
|
25
|
+
result << @attribute_list.to_s if @attribute_list
|
26
|
+
result.join('')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module 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
|
+
# @return [Array] valid operators
|
16
|
+
def valid_operators
|
17
|
+
SIMPLE_VALID_OPERATORS
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@value.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# Compare acutal value with expected value.
|
5
|
+
module Comparable
|
6
|
+
SIMPLE_VALID_OPERATORS = ['==', '!=', 'includes']
|
7
|
+
STRING_VALID_OPERATORS = ['==', '!=', '^=', '$=', '*=', 'includes']
|
8
|
+
NUMBER_VALID_OPERATORS = ['==', '!=', '>', '>=', '<', '<=', 'includes']
|
9
|
+
ARRAY_VALID_OPERATORS = ['==', '!=', 'in', 'not_in']
|
10
|
+
REGEXP_VALID_OPERATORS = ['=~', '!~']
|
11
|
+
|
12
|
+
# Check if the actual value matches the expected value.
|
13
|
+
#
|
14
|
+
# @param node [Node] node to calculate actual value
|
15
|
+
# @param operator [String] 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>
|
16
|
+
# @return [Boolean] true if actual value matches the expected value
|
17
|
+
# @raise [NodeQuery::Compiler::InvalidOperatorError] if operator is invalid
|
18
|
+
def match?(node, operator)
|
19
|
+
raise InvalidOperatorError, "invalid operator #{operator}" unless valid_operator?(operator)
|
20
|
+
|
21
|
+
case operator
|
22
|
+
when '!='
|
23
|
+
if expected_value.is_a?(::Array)
|
24
|
+
actual = actual_value(node)
|
25
|
+
!actual.is_a?(::Array) || actual.size != expected_value.size ||
|
26
|
+
actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, '!=') }
|
27
|
+
else
|
28
|
+
actual_value(node) != expected_value
|
29
|
+
end
|
30
|
+
when '=~'
|
31
|
+
actual_value(node) =~ expected_value
|
32
|
+
when '!~'
|
33
|
+
actual_value(node) !~ expected_value
|
34
|
+
when '^='
|
35
|
+
actual_value(node).start_with?(expected_value)
|
36
|
+
when '$='
|
37
|
+
actual_value(node).end_with?(expected_value)
|
38
|
+
when '*='
|
39
|
+
actual_value(node).include?(expected_value)
|
40
|
+
when '>'
|
41
|
+
actual_value(node) > expected_value
|
42
|
+
when '>='
|
43
|
+
actual_value(node) >= expected_value
|
44
|
+
when '<'
|
45
|
+
actual_value(node) < expected_value
|
46
|
+
when '<='
|
47
|
+
actual_value(node) <= expected_value
|
48
|
+
when 'in'
|
49
|
+
expected_value.any? { |expected| expected.match?(node, '==') }
|
50
|
+
when 'not_in'
|
51
|
+
expected_value.all? { |expected| expected.match?(node, '!=') }
|
52
|
+
when 'includes'
|
53
|
+
actual_value(node).any? { |actual| actual == expected_value }
|
54
|
+
else
|
55
|
+
if expected_value.is_a?(::Array)
|
56
|
+
actual = actual_value(node)
|
57
|
+
actual.is_a?(::Array) && actual.size == expected_value.size &&
|
58
|
+
actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, '==') }
|
59
|
+
else
|
60
|
+
actual_value(node) == expected_value
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get the actual value from ast node.
|
66
|
+
# @param node [Node] ast node
|
67
|
+
# @return the node value, could be integer, float, string, boolean, nil, range, and etc.
|
68
|
+
def actual_value(node)
|
69
|
+
if node.is_a?(::Parser::AST::Node)
|
70
|
+
case NodeQuery.get_adapter.get_node_type(node)
|
71
|
+
when :int, :float, :str, :sym
|
72
|
+
NodeQuery.get_adapter.get_children(node).last
|
73
|
+
when :true
|
74
|
+
true
|
75
|
+
when :false
|
76
|
+
false
|
77
|
+
when :nil
|
78
|
+
nil
|
79
|
+
when :array
|
80
|
+
NodeQuery.get_adapter.get_children(node).map { |child_node| actual_value(child_node) }
|
81
|
+
when :irange
|
82
|
+
actual_value(NodeQuery.get_adapter.get_children(node).first)..actual_value(NodeQuery.get_adapter.get_children(node).last)
|
83
|
+
when :erange
|
84
|
+
actual_value(NodeQuery.get_adapter.get_children(node).first)...actual_value(NodeQuery.get_adapter.get_children(node).last)
|
85
|
+
when :begin
|
86
|
+
actual_value(NodeQuery.get_adapter.get_children(node).first)
|
87
|
+
else
|
88
|
+
node
|
89
|
+
end
|
90
|
+
else
|
91
|
+
node
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Get the expected value
|
96
|
+
# @return expected value, could be integer, float, string, boolean, nil, range, and etc.
|
97
|
+
def expected_value
|
98
|
+
@value
|
99
|
+
end
|
100
|
+
|
101
|
+
# Check if the operator is valid.
|
102
|
+
# @return [Boolean] true if the operator is valid
|
103
|
+
def valid_operator?(operator)
|
104
|
+
valid_operators.include?(operator)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# EvaluatedValue 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 EvaluatedValue
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_accessor :base_node
|
11
|
+
|
12
|
+
# Initialize an EvaluatedValue.
|
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
|
+
# @param node [Node] the node
|
20
|
+
# @return [String] if node is a {Node}, return the node source code, otherwise return the node itself.
|
21
|
+
def actual_value(node)
|
22
|
+
if node.is_a?(::Parser::AST::Node)
|
23
|
+
NodeQuery.get_adapter.get_source(node)
|
24
|
+
else
|
25
|
+
node
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Get the expected value.
|
30
|
+
# @return [String] Query the node by @value from base_node, if the node is a {Node}, return the node source code, otherwise return the node itself.
|
31
|
+
def expected_value
|
32
|
+
node = NodeQuery::Helper.get_target_node(base_node, @value)
|
33
|
+
if node.is_a?(::Parser::AST::Node)
|
34
|
+
NodeQuery.get_adapter.get_source(node)
|
35
|
+
else
|
36
|
+
node
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Get valid operators.
|
41
|
+
# @return [Array] valid operators
|
42
|
+
def valid_operators
|
43
|
+
SIMPLE_VALID_OPERATORS
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
"{{#{@value}}}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# Expression represents a node query expression.
|
5
|
+
class Expression
|
6
|
+
# Initialize a Expression.
|
7
|
+
# @param selector [NodeQuery::Compiler::Selector] the selector
|
8
|
+
# @param rest [NodeQuery::Compiler::Expression] the rest expression
|
9
|
+
def initialize(selector: nil, rest: nil)
|
10
|
+
@selector = selector
|
11
|
+
@rest = rest
|
12
|
+
end
|
13
|
+
|
14
|
+
# Check if the node matches the expression.
|
15
|
+
# @param node [Node] the node
|
16
|
+
# @return [Boolean]
|
17
|
+
def match?(node)
|
18
|
+
!query_nodes(node).empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Query nodes by the selector and the rest expression.
|
22
|
+
# @param node [Node] node to match
|
23
|
+
# @return [Array<Node>] matching nodes.
|
24
|
+
def query_nodes(node)
|
25
|
+
matching_nodes = @selector.query_nodes(node)
|
26
|
+
return matching_nodes if @rest.nil?
|
27
|
+
|
28
|
+
matching_nodes.flat_map do |matching_node|
|
29
|
+
@rest.query_nodes(matching_node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
result = []
|
35
|
+
result << @selector.to_s if @selector
|
36
|
+
result << @rest.to_s if @rest
|
37
|
+
result.join(' ')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NodeQuery::Compiler
|
4
|
+
# ExpressionList contains one or more {NodeQuery::Compiler::Expression}.
|
5
|
+
class ExpressionList
|
6
|
+
# Initialize an ExpressionList.
|
7
|
+
# @param expression [NodeQuery::Compiler::Expression] the expression
|
8
|
+
# @param rest [NodeQuery::Compiler::ExpressionList] the rest expression list
|
9
|
+
def initialize(expression:, rest: nil)
|
10
|
+
@expression = expression
|
11
|
+
@rest = rest
|
12
|
+
end
|
13
|
+
|
14
|
+
# Query nodes by the current and the rest expression.
|
15
|
+
# @param node [Node] node to match
|
16
|
+
# @return [Array<Node>] matching nodes.
|
17
|
+
def query_nodes(node)
|
18
|
+
matching_nodes = @expression.query_nodes(node)
|
19
|
+
return matching_nodes if @rest.nil?
|
20
|
+
|
21
|
+
matching_nodes + @rest.query_nodes(node)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
[@expression, @rest].compact.join(', ')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module 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
|
+
# @return [Array] valid operators
|
16
|
+
def valid_operators
|
17
|
+
NUMBER_VALID_OPERATORS
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
@value.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|