node_query 1.0.0

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