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.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +18 -0
  5. data/Gemfile.lock +98 -0
  6. data/Guardfile +16 -0
  7. data/README.md +37 -0
  8. data/Rakefile +22 -0
  9. data/lib/node_query/adapter.rb +32 -0
  10. data/lib/node_query/compiler/array_value.rb +35 -0
  11. data/lib/node_query/compiler/attribute.rb +39 -0
  12. data/lib/node_query/compiler/attribute_list.rb +25 -0
  13. data/lib/node_query/compiler/basic_selector.rb +29 -0
  14. data/lib/node_query/compiler/boolean.rb +24 -0
  15. data/lib/node_query/compiler/comparable.rb +107 -0
  16. data/lib/node_query/compiler/evaluated_value.rb +50 -0
  17. data/lib/node_query/compiler/expression.rb +40 -0
  18. data/lib/node_query/compiler/expression_list.rb +28 -0
  19. data/lib/node_query/compiler/float.rb +24 -0
  20. data/lib/node_query/compiler/identifier.rb +41 -0
  21. data/lib/node_query/compiler/integer.rb +24 -0
  22. data/lib/node_query/compiler/invalid_operator_error.rb +7 -0
  23. data/lib/node_query/compiler/nil.rb +24 -0
  24. data/lib/node_query/compiler/parse_error.rb +7 -0
  25. data/lib/node_query/compiler/regexp.rb +38 -0
  26. data/lib/node_query/compiler/selector.rb +117 -0
  27. data/lib/node_query/compiler/string.rb +24 -0
  28. data/lib/node_query/compiler/symbol.rb +24 -0
  29. data/lib/node_query/compiler.rb +26 -0
  30. data/lib/node_query/helper.rb +35 -0
  31. data/lib/node_query/parser_adapter.rb +20 -0
  32. data/lib/node_query/version.rb +5 -0
  33. data/lib/node_query.rb +38 -0
  34. data/lib/node_query_lexer.rex +98 -0
  35. data/lib/node_query_parser.y +63 -0
  36. data/node_query.gemspec +36 -0
  37. data/sig/node_query.rbs +11 -0
  38. metadata +95 -0
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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
+ # @param node [Node] the node
17
+ # @return [String|Array]
18
+ # If the node is a {Node}, return the node source code,
19
+ # if the node is an Array, return the array of each element's actual value,
20
+ # otherwise, return the String value.
21
+ def actual_value(node)
22
+ if node.is_a?(::Parser::AST::Node)
23
+ NodeQuery.get_adapter.get_source(node)
24
+ elsif node.is_a?(::Array)
25
+ node.map { |n| actual_value(n) }
26
+ else
27
+ node.to_s
28
+ end
29
+ end
30
+
31
+ # Get valid operators.
32
+ # @return [Array] valid operators
33
+ def valid_operators
34
+ STRING_VALID_OPERATORS
35
+ end
36
+
37
+ def to_s
38
+ @value
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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
+ # @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
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NodeQuery::Compiler
4
+ # Operator is invalid error.
5
+ class InvalidOperatorError < StandardError
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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
+ # @return [Array] valid operators
16
+ def valid_operators
17
+ SIMPLE_VALID_OPERATORS
18
+ end
19
+
20
+ def to_s
21
+ 'nil'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NodeQuery::Compiler
4
+ # The parser error.
5
+ class ParseError < StandardError
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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 [Node] the node
16
+ # @param operator [String] 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(NodeQuery.get_adapter.get_source(node))
22
+ else
23
+ @value.match(node.to_s)
24
+ end
25
+ operator == '=~' ? match : !match
26
+ end
27
+
28
+ # Get valid operators.
29
+ # @return [Array] valid operators
30
+ def valid_operators
31
+ REGEXP_VALID_OPERATORS
32
+ end
33
+
34
+ def to_s
35
+ @value.to_s
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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 goto_scope [String] goto scope
8
+ # @param relationship [String] the relationship between the selectors, it can be descendant <code>nil</code>, child <code>></code>, next sibling <code>+</code> or subsequent sibing <code>~</code>.
9
+ # @param rest [NodeQuery::Compiler::Selector] the rest selector
10
+ # @param basic_selector [NodeQuery::Compiler::BasicSelector] the simple selector
11
+ # @param attribute_list [NodeQuery::Compiler::AttributeList] the attribute list
12
+ # @param pseudo_class [String] the pseudo class, can be <code>has</code> or <code>not_has</code>
13
+ # @param pseudo_selector [NodeQuery::Compiler::Expression] the pseudo selector
14
+ def initialize(goto_scope: nil, relationship: nil, rest: nil, basic_selector: nil, pseudo_class: nil, pseudo_selector: nil)
15
+ @goto_scope = goto_scope
16
+ @relationship = relationship
17
+ @rest = rest
18
+ @basic_selector = basic_selector
19
+ @pseudo_class = pseudo_class
20
+ @pseudo_selector = pseudo_selector
21
+ end
22
+
23
+ # Check if node matches the selector.
24
+ # @param node [Parser::AST::Node] the node
25
+ def match?(node)
26
+ node.is_a?(::Parser::AST::Node) && (!@basic_selector || @basic_selector.match?(node)) && match_pseudo_class?(node)
27
+ end
28
+
29
+ # Query nodes by the selector.
30
+ # * If relationship is nil, it will match in all recursive child nodes and return matching nodes.
31
+ # * If relationship is decendant, it will match in all recursive child nodes.
32
+ # * If relationship is child, it will match in direct child nodes.
33
+ # * If relationship is next sibling, it try to match next sibling node.
34
+ # * If relationship is subsequent sibling, it will match in all sibling nodes.
35
+ # @param node [Node] node to match
36
+ # @return [Array<Node>] matching nodes.
37
+ def query_nodes(node)
38
+ return find_nodes_by_relationship(node) if @relationship
39
+
40
+ if node.is_a?(::Array)
41
+ return node.flat_map { |child_node| query_nodes(child_node) }
42
+ end
43
+
44
+ return find_nodes_by_goto_scope(node) if @goto_scope
45
+
46
+ nodes = []
47
+ nodes << node if match?(node)
48
+ if @basic_selector
49
+ NodeQuery::Helper.handle_recursive_child(node) do |child_node|
50
+ nodes << child_node if match?(child_node)
51
+ end
52
+ end
53
+ nodes
54
+ end
55
+
56
+ def to_s
57
+ result = []
58
+ result << "#{@goto_scope} " if @goto_scope
59
+ result << "#{@relationship} " if @relationship
60
+ result << @rest.to_s if @rest
61
+ result << @basic_selector.to_s if @basic_selector
62
+ result << ":#{@pseudo_class}(#{@pseudo_selector})" if @pseudo_class
63
+ result.join('')
64
+ end
65
+
66
+ private
67
+
68
+ # Find nodes by @goto_scope
69
+ # @param node [Node] node to match
70
+ # @return [Array<Node>] matching nodes
71
+ def find_nodes_by_goto_scope(node)
72
+ @goto_scope.split('.').each { |scope| node = node.send(scope) }
73
+ @rest.query_nodes(node)
74
+ end
75
+
76
+ # Find nodes by @relationship
77
+ # @param node [Node] node to match
78
+ # @return [Array<Node>] matching nodes
79
+ def find_nodes_by_relationship(node)
80
+ nodes = []
81
+ case @relationship
82
+ when '>'
83
+ if node.is_a?(::Array)
84
+ node.each do |child_node|
85
+ nodes << child_node if @rest.match?(child_node)
86
+ end
87
+ else
88
+ node.children.each do |child_node|
89
+ nodes << child_node if @rest.match?(child_node)
90
+ end
91
+ end
92
+ when '+'
93
+ next_sibling = node.siblings.first
94
+ nodes << next_sibling if @rest.match?(next_sibling)
95
+ when '~'
96
+ node.siblings.each do |sibling_node|
97
+ nodes << sibling_node if @rest.match?(sibling_node)
98
+ end
99
+ end
100
+ nodes
101
+ end
102
+
103
+ # Check if it matches pseudo class.
104
+ # @param node [Node] node to match
105
+ # @return [Boolean]
106
+ def match_pseudo_class?(node)
107
+ case @pseudo_class
108
+ when 'has'
109
+ !@pseudo_selector.query_nodes(node).empty?
110
+ when 'not_has'
111
+ @pseudo_selector.query_nodes(node).empty?
112
+ else
113
+ true
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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
+ # @return [Array] valid operators
16
+ def valid_operators
17
+ STRING_VALID_OPERATORS
18
+ end
19
+
20
+ def to_s
21
+ "\"#{@value}\""
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module 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
+ # @return [Array] valid operators
16
+ def valid_operators
17
+ SIMPLE_VALID_OPERATORS
18
+ end
19
+
20
+ def to_s
21
+ ":#{@value}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NodeQuery::Compiler
4
+ autoload :InvalidOperatorError, 'node_query/compiler/invalid_operator_error'
5
+ autoload :ParseError, 'node_query/compiler/parse_error'
6
+
7
+ autoload :Comparable, 'node_query/compiler/comparable'
8
+
9
+ autoload :ExpressionList, 'node_query/compiler/expression_list'
10
+ autoload :Expression, 'node_query/compiler/expression'
11
+ autoload :Selector, 'node_query/compiler/selector'
12
+ autoload :BasicSelector, 'node_query/compiler/basic_selector'
13
+ autoload :AttributeList, 'node_query/compiler/attribute_list'
14
+ autoload :Attribute, 'node_query/compiler/attribute'
15
+
16
+ autoload :ArrayValue, 'node_query/compiler/array_value'
17
+ autoload :Boolean, 'node_query/compiler/boolean'
18
+ autoload :EvaluatedValue, 'node_query/compiler/evaluated_value'
19
+ autoload :Float, 'node_query/compiler/float'
20
+ autoload :Identifier, 'node_query/compiler/identifier'
21
+ autoload :Integer, 'node_query/compiler/integer'
22
+ autoload :Nil, 'node_query/compiler/nil'
23
+ autoload :Regexp, 'node_query/compiler/regexp'
24
+ autoload :String, 'node_query/compiler/string'
25
+ autoload :Symbol, 'node_query/compiler/symbol'
26
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NodeQuery::Helper
4
+ # Get target node by the keys.
5
+ # @param node [Node] ast node
6
+ # @param keys [String] keys of child node.
7
+ # @return [Node|] the target node.
8
+ def self.get_target_node(node, keys)
9
+ return unless node
10
+
11
+ first_key, rest_keys = keys.to_s.split('.', 2)
12
+ if (node.is_a?(Array) && first_key === "*")
13
+ return node.map { |child_node| get_target_node(child_node, rest_keys) }
14
+ end
15
+
16
+ if node.respond_to?(first_key)
17
+ child_node = node.send(first_key)
18
+ end
19
+
20
+ return child_node unless rest_keys
21
+
22
+ return get_target_node(child_node, rest_keys)
23
+ end
24
+
25
+ # Recursively handle child nodes.
26
+ # @param node [Node] ast node
27
+ # @yield [child] Gives a child node.
28
+ # @yieldparam child [Parser::AST::Node] child node
29
+ def self.handle_recursive_child(node, &block)
30
+ NodeQuery.get_adapter.get_children(node).each do |child|
31
+ block.call(child)
32
+ handle_recursive_child(child, &block)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NodeQuery::ParserAdapter
4
+ def get_node_type(node)
5
+ node.type
6
+ end
7
+
8
+ def get_source(node)
9
+ node.loc.expression.source
10
+ end
11
+
12
+ def get_children(node)
13
+ node.is_a?(Parser::AST::Node) ? node.children : []
14
+ end
15
+
16
+ def get_siblings(node)
17
+ index = node.parent.children.index(node)
18
+ node.parent.children[index + 1..]
19
+ end
20
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class NodeQuery
4
+ VERSION = "1.0.0"
5
+ end
data/lib/node_query.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/array'
4
+
5
+ require_relative "node_query/version"
6
+ require_relative "node_query/parser_adapter"
7
+ require_relative "node_query/compiler"
8
+ require_relative "node_query/helper"
9
+ require_relative "./node_query_lexer.rex"
10
+ require_relative "./node_query_parser.racc"
11
+
12
+ class NodeQuery
13
+ # Configure NodeQuery
14
+ # @param [Hash] options options to configure
15
+ # @option options [NodeQuery::Adapter] :adapter the adpater
16
+ def self.configure(options)
17
+ @adapter = options.adapter
18
+ end
19
+
20
+ # Get the adapter
21
+ # @return [NodeQuery::Adapter] current adapter, by default is {NodeQuery::ParserAdapter}
22
+ def self.get_adapter
23
+ @adapter ||= ParserAdapter.new
24
+ end
25
+
26
+ # Initialize a NodeQuery.
27
+ # @param nql [String] node query language
28
+ def initialize(nql)
29
+ @expression = NodeQueryParser.new.parse(nql)
30
+ end
31
+
32
+ # Parse ast node.
33
+ # @param node [Node] ast node
34
+ # @return [Array<Node>] matching child nodes
35
+ def parse(node)
36
+ @expression.query_nodes(node)
37
+ end
38
+ end
@@ -0,0 +1,98 @@
1
+ class NodeQueryLexer
2
+
3
+ macros
4
+ OPEN_ATTRIBUTE /\[/
5
+ CLOSE_ATTRIBUTE /\]/
6
+ OPEN_ARRAY /\(/
7
+ CLOSE_ARRAY /\)/
8
+ OPEN_SELECTOR /\(/
9
+ CLOSE_SELECTOR /\)/
10
+ OPEN_DYNAMIC_ATTRIBUTE /{{/
11
+ CLOSE_DYNAMIC_ATTRIBUTE /}}/
12
+ NODE_TYPE /\.[a-z]+/
13
+ IDENTIFIER /[\.\w]+/
14
+ IDENTIFIER_VALUE /[\.\w!&:\?<>=]+/
15
+ FALSE /false/
16
+ FLOAT /\d+\.\d+/
17
+ INTEGER /\d+/
18
+ NIL /nil/
19
+ REGEXP_BODY /(?:[^\/]|\\\/)*/
20
+ REGEXP /\/(#{REGEXP_BODY})(?<!\\)\/([imxo]*)/
21
+ SYMBOL /:[\w!\?<>=]+/
22
+ TRUE /true/
23
+ SINGLE_QUOTE_STRING /'.*?'/
24
+ DOUBLE_QUOTE_STRING /".*?"/
25
+
26
+ rules
27
+
28
+ # [:state] pattern [actions]
29
+ /\s+/
30
+ /,/ { [:tCOMMA, text] }
31
+ /:has/ { [:tPSEUDO_CLASS, text[1..-1]] }
32
+ /:not_has/ { [:tPSEUDO_CLASS, text[1..-1]] }
33
+ /#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
34
+ /#{IDENTIFIER}/ { [:tGOTO_SCOPE, text] }
35
+ />/ { [:tRELATIONSHIP, text] }
36
+ /~/ { [:tRELATIONSHIP, text] }
37
+ /\+/ { [:tRELATIONSHIP, text] }
38
+ /#{OPEN_SELECTOR}/ { [:tOPEN_SELECTOR, text] }
39
+ /#{CLOSE_SELECTOR}/ { [:tCLOSE_SELECTOR, text] }
40
+ /#{OPEN_ATTRIBUTE}/ { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
41
+ :KEY /\s+/
42
+ :KEY /\^=/ { @state = :VALUE; [:tOPERATOR, '^='] }
43
+ :KEY /\$=/ { @state = :VALUE; [:tOPERATOR, '$='] }
44
+ :KEY /\*=/ { @state = :VALUE; [:tOPERATOR, '*='] }
45
+ :KEY /!=/ { @state = :VALUE; [:tOPERATOR, '!='] }
46
+ :KEY /=~/ { @state = :VALUE; [:tOPERATOR, '=~'] }
47
+ :KEY /!~/ { @state = :VALUE; [:tOPERATOR, '!~'] }
48
+ :KEY />=/ { @state = :VALUE; [:tOPERATOR, '>='] }
49
+ :KEY /<=/ { @state = :VALUE; [:tOPERATOR, '<='] }
50
+ :KEY />/ { @state = :VALUE; [:tOPERATOR, '>'] }
51
+ :KEY /</ { @state = :VALUE; [:tOPERATOR, '<'] }
52
+ :KEY /=/ { @state = :VALUE; [:tOPERATOR, '=='] }
53
+ :KEY /includes/i { @state = :VALUE; [:tOPERATOR, 'includes'] }
54
+ :KEY /not in/i { @state = :VALUE; [:tOPERATOR, 'not_in'] }
55
+ :KEY /in/i { @state = :VALUE; [:tOPERATOR, 'in'] }
56
+ :KEY /#{IDENTIFIER}/ { [:tKEY, text] }
57
+ :VALUE /\s+/
58
+ :VALUE /\[\]=/ { [:tIDENTIFIER_VALUE, text] }
59
+ :VALUE /\[\]/ { [:tIDENTIFIER_VALUE, text] }
60
+ :VALUE /:\[\]=/ { [:tSYMBOL, text[1..-1].to_sym] }
61
+ :VALUE /:\[\]/ { [:tSYMBOL, text[1..-1].to_sym] }
62
+ :VALUE /{{#{IDENTIFIER}}}/ { [:tDYNAMIC_ATTRIBUTE, text[2..-3]] }
63
+ :VALUE /#{OPEN_ARRAY}/ { @state = :ARRAY_VALUE; [:tOPEN_ARRAY, text] }
64
+ :VALUE /#{CLOSE_ATTRIBUTE}/ { @nested_count -= 1; @state = @nested_count == 0 ? nil : :VALUE; [:tCLOSE_ATTRIBUTE, text] }
65
+ :VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
66
+ :VALUE /#{NIL}/ { [:tNIL, nil] }
67
+ :VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
68
+ :VALUE /#{FALSE}/ { [:tBOOLEAN, false] }
69
+ :VALUE /#{SYMBOL}/ { [:tSYMBOL, text[1..-1].to_sym] }
70
+ :VALUE /#{FLOAT}/ { [:tFLOAT, text.to_f] }
71
+ :VALUE /#{INTEGER}/ { [:tINTEGER, text.to_i] }
72
+ :VALUE /#{REGEXP}/ { [:tREGEXP, eval(text)] }
73
+ :VALUE /#{DOUBLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
74
+ :VALUE /#{SINGLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
75
+ :VALUE /#{NODE_TYPE}/ { [:tNODE_TYPE, text[1..]] }
76
+ :VALUE /#{OPEN_ATTRIBUTE}/ { @nested_count += 1; @state = :KEY; [:tOPEN_ATTRIBUTE, text] }
77
+ :VALUE /#{IDENTIFIER_VALUE}/ { [:tIDENTIFIER_VALUE, text] }
78
+ :ARRAY_VALUE /\s+/
79
+ :ARRAY_VALUE /#{CLOSE_ARRAY}/ { @state = :VALUE; [:tCLOSE_ARRAY, text] }
80
+ :ARRAY_VALUE /#{NIL}\?/ { [:tIDENTIFIER_VALUE, text] }
81
+ :ARRAY_VALUE /#{NIL}/ { [:tNIL, nil] }
82
+ :ARRAY_VALUE /#{TRUE}/ { [:tBOOLEAN, true] }
83
+ :ARRAY_VALUE /#{FALSE}/ { [:tBOOLEAN, false] }
84
+ :ARRAY_VALUE /#{SYMBOL}/ { [:tSYMBOL, text[1..-1].to_sym] }
85
+ :ARRAY_VALUE /#{FLOAT}/ { [:tFLOAT, text.to_f] }
86
+ :ARRAY_VALUE /#{INTEGER}/ { [:tINTEGER, text.to_i] }
87
+ :ARRAY_VALUE /#{REGEXP}/ { [:tREGEXP, eval(text)] }
88
+ :ARRAY_VALUE /#{DOUBLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
89
+ :ARRAY_VALUE /#{SINGLE_QUOTE_STRING}/ { [:tSTRING, text[1...-1]] }
90
+ :ARRAY_VALUE /#{IDENTIFIER_VALUE}/ { [:tIDENTIFIER_VALUE, text] }
91
+
92
+ inner
93
+ def initialize
94
+ @nested_count = 0
95
+ end
96
+
97
+ def do_parse; end
98
+ end
@@ -0,0 +1,63 @@
1
+ class NodeQueryParser
2
+ options no_result_var
3
+ token tCOMMA tNODE_TYPE tGOTO_SCOPE tATTRIBUTE tKEY tIDENTIFIER tIDENTIFIER_VALUE tPSEUDO_CLASS tRELATIONSHIP
4
+ tOPEN_ATTRIBUTE tCLOSE_ATTRIBUTE tOPEN_ARRAY tCLOSE_ARRAY tOPEN_SELECTOR tCLOSE_SELECTOR
5
+ tOPERATOR tARRAY_VALUE tDYNAMIC_ATTRIBUTE tBOOLEAN tFLOAT tINTEGER tNIL tREGEXP tSTRING tSYMBOL
6
+ rule
7
+ expression_list
8
+ : expression tCOMMA expression_list { NodeQuery::Compiler::ExpressionList.new(expression: val[0], rest: val[2]) }
9
+ | expression { NodeQuery::Compiler::ExpressionList.new(expression: val[0]) }
10
+
11
+ expression
12
+ : selector expression { NodeQuery::Compiler::Expression.new(selector: val[0], rest: val[1]) }
13
+ | selector { NodeQuery::Compiler::Expression.new(selector: val[0]) }
14
+
15
+ selector
16
+ : basic_selector { NodeQuery::Compiler::Selector.new(basic_selector: val[0]) }
17
+ | tPSEUDO_CLASS tOPEN_SELECTOR selector tCLOSE_SELECTOR { NodeQuery::Compiler::Selector.new(pseudo_class: val[0], pseudo_selector: val[2]) }
18
+ | tRELATIONSHIP selector { NodeQuery::Compiler::Selector.new(relationship: val[0], rest: val[1]) }
19
+ | tGOTO_SCOPE selector { NodeQuery::Compiler::Selector.new(goto_scope: val[0], rest: val[1]) }
20
+
21
+ basic_selector
22
+ : tNODE_TYPE { NodeQuery::Compiler::BasicSelector.new(node_type: val[0]) }
23
+ | tNODE_TYPE attribute_list { NodeQuery::Compiler::BasicSelector.new(node_type: val[0], attribute_list: val[1]) }
24
+
25
+ attribute_list
26
+ : tOPEN_ATTRIBUTE attribute tCLOSE_ATTRIBUTE attribute_list { NodeQuery::Compiler::AttributeList.new(attribute: val[1], rest: val[3]) }
27
+ | tOPEN_ATTRIBUTE attribute tCLOSE_ATTRIBUTE { NodeQuery::Compiler::AttributeList.new(attribute: val[1]) }
28
+
29
+ attribute
30
+ : tKEY tOPERATOR value { NodeQuery::Compiler::Attribute.new(key: val[0], value: val[2], operator: val[1]) }
31
+ | tKEY tOPERATOR tOPEN_ARRAY tCLOSE_ARRAY { NodeQuery::Compiler::Attribute.new(key: val[0], value: NodeQuery::Compiler::Array.new, operator: val[1]) }
32
+ | tKEY tOPERATOR tOPEN_ARRAY array_value tCLOSE_ARRAY { NodeQuery::Compiler::Attribute.new(key: val[0], value: val[3], operator: val[1]) }
33
+
34
+ array_value
35
+ : value array_value { NodeQuery::Compiler::ArrayValue.new(value: val[0], rest: val[1]) }
36
+ | value { NodeQuery::Compiler::ArrayValue.new(value: val[0]) }
37
+
38
+ value
39
+ : basic_selector
40
+ | tDYNAMIC_ATTRIBUTE { NodeQuery::Compiler::EvaluatedValue.new(value: val[0]) }
41
+ | tBOOLEAN { NodeQuery::Compiler::Boolean.new(value: val[0]) }
42
+ | tFLOAT { NodeQuery::Compiler::Float.new(value: val[0]) }
43
+ | tINTEGER { NodeQuery::Compiler::Integer.new(value: val[0])}
44
+ | tNIL { NodeQuery::Compiler::Nil.new(value: val[0]) }
45
+ | tREGEXP { NodeQuery::Compiler::Regexp.new(value: val[0]) }
46
+ | tSTRING { NodeQuery::Compiler::String.new(value: val[0]) }
47
+ | tSYMBOL { NodeQuery::Compiler::Symbol.new(value: val[0]) }
48
+ | tIDENTIFIER_VALUE { NodeQuery::Compiler::Identifier.new(value: val[0]) }
49
+ end
50
+
51
+ ---- inner
52
+ def initialize
53
+ @lexer = NodeQueryLexer.new
54
+ end
55
+
56
+ def parse string
57
+ @lexer.parse string
58
+ do_parse
59
+ end
60
+
61
+ def next_token
62
+ @lexer.next_token
63
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/node_query/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "node_query"
7
+ spec.version = NodeQuery::VERSION
8
+ spec.authors = ["Richard Huang"]
9
+ spec.email = ["flyerhzm@gmail.com"]
10
+
11
+ spec.summary = "ast node query language"
12
+ spec.description = "ast node query language"
13
+ spec.homepage = "https://github.com/xinminlabs/node-query-ruby"
14
+ spec.required_ruby_version = ">= 2.6.0"
15
+
16
+ spec.metadata["homepage_uri"] = spec.homepage
17
+ spec.metadata["source_code_uri"] = "https://github.com/xinminlabs/node-query-ruby"
18
+ spec.metadata["changelog_uri"] = "https://github.com/xinminlabs/node-query-ruby/CHANGELOG.md"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
+ `git ls-files -z`.split("\x0").reject do |f|
24
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
25
+ end
26
+ end
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ # Uncomment to register a new dependency of your gem
32
+ spec.add_dependency "activesupport"
33
+
34
+ # For more information and examples about making a new gem, check out our
35
+ # guide at: https://bundler.io/guides/creating_gem.html
36
+ end
@@ -0,0 +1,11 @@
1
+ class NodeQuery
2
+ VERSION: String
3
+
4
+ def self.configure: (options: { adapter: NodeQuery::Adapter }) -> void
5
+
6
+ def self.get_adapter: () -> NodeQuery::Adapter
7
+
8
+ def initialize: (nql: String) -> NodeQuery
9
+
10
+ def parse: (node: Node) -> Array[Node]
11
+ end