node_query 1.0.0
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.
- 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
|