hotdog 0.12.0 → 0.13.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.
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "parslet"
4
+
5
+ module Hotdog
6
+ module Expression
7
+ class ExpressionParser < Parslet::Parser
8
+ root(:expression)
9
+ rule(:expression) {
10
+ ( expression0 \
11
+ )
12
+ }
13
+ rule(:expression0) {
14
+ ( expression1.as(:left) >> spacing.maybe >> binary_op.as(:binary_op) >> spacing.maybe >> expression.as(:right) \
15
+ | expression1 \
16
+ )
17
+ }
18
+ rule(:expression1) {
19
+ ( unary_op.as(:unary_op) >> spacing >> expression.as(:expression) \
20
+ | unary_op.as(:unary_op) >> spacing.maybe >> str('(') >> spacing.maybe >> expression.as(:expression) >> spacing.maybe >> str(')') \
21
+ | expression2 \
22
+ )
23
+ }
24
+ rule(:expression2) {
25
+ ( expression3.as(:left) >> spacing.maybe.as(:binary_op) >> expression.as(:right) \
26
+ | expression3 \
27
+ )
28
+ }
29
+ rule(:expression3) {
30
+ ( expression4.as(:left) >> spacing.maybe >> str('&&').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
31
+ | expression4.as(:left) >> spacing.maybe >> str('||').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
32
+ | expression4.as(:left) >> spacing.maybe >> str('&').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
33
+ | expression4.as(:left) >> spacing.maybe >> str(',').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
34
+ | expression4.as(:left) >> spacing.maybe >> str('^').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
35
+ | expression4.as(:left) >> spacing.maybe >> str('|').as(:binary_op) >> spacing.maybe >> expression.as(:right) \
36
+ | expression4 \
37
+ )
38
+ }
39
+ rule(:expression4) {
40
+ ( str('!').as(:unary_op) >> spacing.maybe >> primary.as(:expression) >> spacing.maybe \
41
+ | str('~').as(:unary_op) >> spacing.maybe >> primary.as(:expression) >> spacing.maybe \
42
+ | str('!').as(:unary_op) >> spacing.maybe >> expression.as(:expression) \
43
+ | str('~').as(:unary_op) >> spacing.maybe >> expression.as(:expression) \
44
+ | spacing.maybe >> primary >> spacing.maybe \
45
+ )
46
+ }
47
+ rule(:binary_op) {
48
+ ( str('AND') \
49
+ | str('OR') \
50
+ | str('XOR') \
51
+ | str('and') \
52
+ | str('or') \
53
+ | str('xor') \
54
+ )
55
+ }
56
+ rule(:unary_op) {
57
+ ( str('NOT') \
58
+ | str('not') \
59
+ )
60
+ }
61
+ rule(:funcall) {
62
+ ( funcall_identifier.as(:funcall) >> spacing.maybe >> str('(') >> spacing.maybe >> str(')') \
63
+ | funcall_identifier.as(:funcall) >> spacing.maybe >> str('(') >> spacing.maybe >> funcall_args.as(:funcall_args) >> spacing.maybe >> str(')') \
64
+ )
65
+ }
66
+ rule(:funcall_identifier) {
67
+ ( binary_op.absent? >> unary_op.absent? >> match('[A-Z_a-z]') >> match('[0-9A-Z_a-z]').repeat(0) \
68
+ | binary_op >> match('[0-9A-Z_a-z]').repeat(1) \
69
+ | unary_op >> match('[0-9A-Z_a-z]').repeat(1) \
70
+ )
71
+ }
72
+ rule(:funcall_args) {
73
+ ( funcall_arg.as(:funcall_args_head) >> spacing.maybe >> str(',') >> spacing.maybe >> funcall_args.as(:funcall_args_tail) \
74
+ | funcall_arg.as(:funcall_args_head) \
75
+ )
76
+ }
77
+ rule(:funcall_arg) {
78
+ ( float.as(:float) \
79
+ | integer.as(:integer) \
80
+ | string.as(:string) \
81
+ | regexp.as(:regexp) \
82
+ | primary \
83
+ )
84
+ }
85
+ rule(:float) {
86
+ ( match('[0-9]').repeat(1) >> str('.') >> match('[0-9]').repeat(1) \
87
+ )
88
+ }
89
+ rule(:integer) {
90
+ ( match('[1-9]').repeat(1) >> match('[0-9]').repeat(0) \
91
+ )
92
+ }
93
+ rule(:string) {
94
+ ( str('"') >> (str('"').absent? >> any).repeat(0) >> str('"') \
95
+ | str("'") >> (str("'").absent? >> any).repeat(0) >> str("'") \
96
+ )
97
+ }
98
+ rule(:regexp) {
99
+ ( str('/') >> (str('/').absent? >> any).repeat(0) >> str('/') \
100
+ )
101
+ }
102
+ rule(:primary) {
103
+ ( str('(') >> expression >> str(')') \
104
+ | funcall \
105
+ | tag \
106
+ )
107
+ }
108
+ rule(:tag) {
109
+ ( regexp.as(:tag_name_regexp) >> separator.as(:separator) >> regexp.as(:tag_value_regexp) \
110
+ | regexp.as(:tag_name_regexp) >> separator.as(:separator) \
111
+ | regexp.as(:tag_name_regexp) \
112
+ | tag_name_glob.as(:tag_name_glob) >> separator.as(:separator) >> tag_value_glob.as(:tag_value_glob) \
113
+ | tag_name_glob.as(:tag_name_glob) >> separator.as(:separator) >> tag_value.as(:tag_value) \
114
+ | tag_name_glob.as(:tag_name_glob) >> separator.as(:separator) \
115
+ | tag_name_glob.as(:tag_name_glob) \
116
+ | tag_name.as(:tag_name) >> separator.as(:separator) >> tag_value_glob.as(:tag_value_glob) \
117
+ | tag_name.as(:tag_name) >> separator.as(:separator) >> tag_value.as(:tag_value) \
118
+ | tag_name.as(:tag_name) >> separator.as(:separator) \
119
+ | tag_name.as(:tag_name) \
120
+ | separator.as(:separator) >> regexp.as(:tag_value_regexp) \
121
+ | separator.as(:separator) >> tag_value_glob.as(:tag_value_glob) \
122
+ | separator.as(:separator) >> tag_value.as(:tag_value) \
123
+ | tag_value_regexp.as(:tag_value_regexp) \
124
+ | tag_value_glob.as(:tag_value_glob) \
125
+ | tag_value.as(:tag_value) \
126
+ )
127
+ }
128
+ rule(:tag_name_regexp) {
129
+ ( regexp \
130
+ )
131
+ }
132
+ rule(:tag_value_regexp) {
133
+ ( regexp \
134
+ )
135
+ }
136
+ rule(:tag_name_glob) {
137
+ ( binary_op.absent? >> unary_op.absent? >> tag_name.repeat(0) >> (glob_char >> tag_name.maybe).repeat(1) \
138
+ | binary_op >> (glob_char >> tag_name.maybe).repeat(1) \
139
+ | unary_op >> (glob_char >> tag_name.maybe).repeat(1) \
140
+ )
141
+ }
142
+ rule(:tag_value_glob) {
143
+ ( binary_op.absent? >> unary_op.absent? >> tag_value.repeat(0) >> (glob_char >> tag_value.maybe).repeat(1) \
144
+ | binary_op >> (glob_char >> tag_value.maybe).repeat(1) \
145
+ | unary_op >> (glob_char >> tag_value.maybe).repeat(1) \
146
+ )
147
+ }
148
+ rule(:tag_name) {
149
+ ( binary_op.absent? >> unary_op.absent? >> match('[A-Z_a-z]') >> match('[-./0-9A-Z_a-z]').repeat(0) \
150
+ | binary_op >> match('[-./0-9A-Z_a-z]').repeat(1) \
151
+ | unary_op >> match('[-./0-9A-Z_a-z]').repeat(1) \
152
+ )
153
+ }
154
+ rule(:tag_value) {
155
+ ( binary_op.absent? >> unary_op.absent? >> match('[-./0-9:A-Z_a-z]').repeat(1) \
156
+ | binary_op >> match('[-./0-9:A-Z_a-z]').repeat(1) \
157
+ | unary_op >> match('[-./0-9:A-Z_a-z]').repeat(1) \
158
+ )
159
+ }
160
+ rule(:separator) {
161
+ ( str(':') \
162
+ | str('=') \
163
+ )
164
+ }
165
+ rule(:glob_char) {
166
+ ( str('*') | str('?') | str('[') | str(']') )
167
+ }
168
+ rule(:spacing) {
169
+ ( match('[\t\n\r ]').repeat(1) \
170
+ )
171
+ }
172
+ end
173
+ end
174
+ end
175
+
176
+ # vim:set ft=ruby :
@@ -1,3 +1,3 @@
1
1
  module Hotdog
2
- VERSION = "0.12.0"
2
+ VERSION = "0.13.0"
3
3
  end
@@ -10,7 +10,7 @@ describe "tag glob expression" do
10
10
  }
11
11
 
12
12
  it "interprets tag glob with host" do
13
- expr = Hotdog::Commands::Search::GlobHostNode.new("foo*", ":")
13
+ expr = Hotdog::Expression::GlobHostNode.new("foo*", ":")
14
14
  q = [
15
15
  "SELECT hosts.id AS host_id FROM hosts",
16
16
  "WHERE LOWER(hosts.name) GLOB LOWER(?);",
@@ -19,11 +19,11 @@ describe "tag glob expression" do
19
19
  [[1], [2], [3]]
20
20
  }
21
21
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
22
- expect(expr.dump).to eq({identifier_glob: "host", separator: ":", attribute_glob: "foo*"})
22
+ expect(expr.dump).to eq({tag_name_glob: "host", separator: ":", tag_value_glob: "foo*"})
23
23
  end
24
24
 
25
- it "interprets tag glob with identifier and attribute" do
26
- expr = Hotdog::Commands::Search::GlobTagNode.new("foo*", "bar*", ":")
25
+ it "interprets tag glob with tag_name and tag_value" do
26
+ expr = Hotdog::Expression::GlobTagNode.new("foo*", "bar*", ":")
27
27
  q = [
28
28
  "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags",
29
29
  "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
@@ -33,11 +33,11 @@ describe "tag glob expression" do
33
33
  [[1], [2], [3]]
34
34
  }
35
35
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
36
- expect(expr.dump).to eq({identifier_glob: "foo*", separator: ":", attribute_glob: "bar*"})
36
+ expect(expr.dump).to eq({tag_name_glob: "foo*", separator: ":", tag_value_glob: "bar*"})
37
37
  end
38
38
 
39
- it "interprets tag glob with identifier with separator" do
40
- expr = Hotdog::Commands::Search::GlobTagNameNode.new("foo*", ":")
39
+ it "interprets tag glob with tag_name with separator" do
40
+ expr = Hotdog::Expression::GlobTagNameNode.new("foo*", ":")
41
41
  q = [
42
42
  "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags",
43
43
  "INNER JOIN tags ON hosts_tags.tag_id = tags.id",
@@ -47,11 +47,11 @@ describe "tag glob expression" do
47
47
  [[1], [2], [3]]
48
48
  }
49
49
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
50
- expect(expr.dump).to eq({identifier_glob: "foo*", separator: ":"})
50
+ expect(expr.dump).to eq({tag_name_glob: "foo*", separator: ":"})
51
51
  end
52
52
 
53
- it "interprets tag glob with identifier without separator" do
54
- expr = Hotdog::Commands::Search::GlobNode.new("foo*", nil)
53
+ it "interprets tag glob with tag_name without separator" do
54
+ expr = Hotdog::Expression::GlobNode.new("foo*", nil)
55
55
  q = [
56
56
  "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags",
57
57
  "INNER JOIN hosts ON hosts_tags.host_id = hosts.id",
@@ -62,11 +62,11 @@ describe "tag glob expression" do
62
62
  [[1], [2], [3]]
63
63
  }
64
64
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
65
- expect(expr.dump).to eq({identifier_glob: "foo*"})
65
+ expect(expr.dump).to eq({tag_name_glob: "foo*"})
66
66
  end
67
67
 
68
- it "interprets tag glob with attribute with separator" do
69
- expr = Hotdog::Commands::Search::GlobTagValueNode.new("foo*", ":")
68
+ it "interprets tag glob with tag_value with separator" do
69
+ expr = Hotdog::Expression::GlobTagValueNode.new("foo*", ":")
70
70
  q = [
71
71
  "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags",
72
72
  "INNER JOIN hosts ON hosts_tags.host_id = hosts.id",
@@ -77,11 +77,11 @@ describe "tag glob expression" do
77
77
  [[1], [2], [3]]
78
78
  }
79
79
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
80
- expect(expr.dump).to eq({separator: ":", attribute_glob: "foo*"})
80
+ expect(expr.dump).to eq({separator: ":", tag_value_glob: "foo*"})
81
81
  end
82
82
 
83
- it "interprets tag glob with attribute without separator" do
84
- expr = Hotdog::Commands::Search::GlobTagValueNode.new("foo*", nil)
83
+ it "interprets tag glob with tag_value without separator" do
84
+ expr = Hotdog::Expression::GlobTagValueNode.new("foo*", nil)
85
85
  q = [
86
86
  "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags",
87
87
  "INNER JOIN hosts ON hosts_tags.host_id = hosts.id",
@@ -92,11 +92,11 @@ describe "tag glob expression" do
92
92
  [[1], [2], [3]]
93
93
  }
94
94
  expect(expr.evaluate(cmd)).to eq([1, 2, 3])
95
- expect(expr.dump).to eq({attribute_glob: "foo*"})
95
+ expect(expr.dump).to eq({tag_value_glob: "foo*"})
96
96
  end
97
97
 
98
98
  it "empty tag glob" do
99
- expr = Hotdog::Commands::Search::GlobExpressionNode.new(nil, nil, nil)
99
+ expr = Hotdog::Expression::GlobExpressionNode.new(nil, nil, nil)
100
100
  expect {
101
101
  expr.evaluate(cmd)
102
102
  }.to raise_error(NotImplementedError)
@@ -15,275 +15,311 @@ describe "parser" do
15
15
  end
16
16
 
17
17
  it "parses ':foo'" do
18
- expect(cmd.parse(":foo")).to eq({separator: ":", attribute: "foo"})
18
+ expect(cmd.parse(":foo")).to eq({separator: ":", tag_value: "foo"})
19
19
  end
20
20
 
21
21
  it "parses ':foo*'" do
22
- expect(cmd.parse(":foo*")).to eq({separator: ":", attribute_glob: "foo*"})
22
+ expect(cmd.parse(":foo*")).to eq({separator: ":", tag_value_glob: "foo*"})
23
23
  end
24
24
 
25
25
  it "parses ':/foo/'" do
26
- expect(cmd.parse(":/foo/")).to eq({separator: ":", attribute_regexp: "foo"})
26
+ expect(cmd.parse(":/foo/")).to eq({separator: ":", tag_value_regexp: "/foo/"})
27
27
  end
28
28
 
29
29
  it "parses 'foo'" do
30
- expect(cmd.parse("foo")).to eq({identifier: "foo"})
30
+ expect(cmd.parse("foo")).to eq({tag_name: "foo"})
31
31
  end
32
32
 
33
33
  it "parses 'foo:bar'" do
34
- expect(cmd.parse("foo:bar")).to eq({identifier: "foo", separator: ":", attribute: "bar"})
34
+ expect(cmd.parse("foo:bar")).to eq({tag_name: "foo", separator: ":", tag_value: "bar"})
35
35
  end
36
36
 
37
37
  it "parses 'foo: bar'" do
38
- expect(cmd.parse("foo:bar")).to eq({identifier: "foo", separator: ":", attribute: "bar"})
38
+ expect(cmd.parse("foo:bar")).to eq({tag_name: "foo", separator: ":", tag_value: "bar"})
39
39
  end
40
40
 
41
41
  it "parses 'foo :bar'" do
42
- expect(cmd.parse("foo:bar")).to eq({identifier: "foo", separator: ":", attribute: "bar"})
42
+ expect(cmd.parse("foo:bar")).to eq({tag_name: "foo", separator: ":", tag_value: "bar"})
43
43
  end
44
44
 
45
45
  it "parses 'foo : bar'" do
46
- expect(cmd.parse("foo:bar")).to eq({identifier: "foo", separator: ":", attribute: "bar"})
46
+ expect(cmd.parse("foo:bar")).to eq({tag_name: "foo", separator: ":", tag_value: "bar"})
47
47
  end
48
48
 
49
49
  it "parses 'foo:bar*'" do
50
- expect(cmd.parse("foo:bar*")).to eq({identifier: "foo", separator: ":", attribute_glob: "bar*"})
50
+ expect(cmd.parse("foo:bar*")).to eq({tag_name: "foo", separator: ":", tag_value_glob: "bar*"})
51
51
  end
52
52
 
53
53
  it "parses 'foo*'" do
54
- expect(cmd.parse("foo*")).to eq({identifier_glob: "foo*"})
54
+ expect(cmd.parse("foo*")).to eq({tag_name_glob: "foo*"})
55
55
  end
56
56
 
57
57
  it "parses 'foo*:bar'" do
58
- expect(cmd.parse("foo*:bar")).to eq({identifier_glob: "foo*", separator: ":", attribute: "bar"})
58
+ expect(cmd.parse("foo*:bar")).to eq({tag_name_glob: "foo*", separator: ":", tag_value: "bar"})
59
59
  end
60
60
 
61
61
  it "parses 'foo*:bar*'" do
62
- expect(cmd.parse("foo*:bar*")).to eq({identifier_glob: "foo*", separator: ":", attribute_glob: "bar*"})
62
+ expect(cmd.parse("foo*:bar*")).to eq({tag_name_glob: "foo*", separator: ":", tag_value_glob: "bar*"})
63
63
  end
64
64
 
65
65
  it "parses '/foo/'" do
66
- expect(cmd.parse("/foo/")).to eq({identifier_regexp: "foo"})
66
+ expect(cmd.parse("/foo/")).to eq({tag_name_regexp: "/foo/"})
67
67
  end
68
68
 
69
69
  it "parses '/foo/:/bar/'" do
70
- expect(cmd.parse("/foo/:/bar/")).to eq({identifier_regexp: "foo", separator: ":", attribute_regexp: "bar"})
70
+ expect(cmd.parse("/foo/:/bar/")).to eq({tag_name_regexp: "/foo/", separator: ":", tag_value_regexp: "/bar/"})
71
71
  end
72
72
 
73
73
  it "parses '(foo)'" do
74
- expect(cmd.parse("(foo)")).to eq({identifier: "foo"})
74
+ expect(cmd.parse("(foo)")).to eq({tag_name: "foo"})
75
75
  end
76
76
 
77
77
  it "parses '( foo )'" do
78
- expect(cmd.parse("( foo )")).to eq({identifier: "foo"})
78
+ expect(cmd.parse("( foo )")).to eq({tag_name: "foo"})
79
79
  end
80
80
 
81
81
  it "parses ' ( foo ) '" do
82
- expect(cmd.parse(" ( foo ) ")).to eq({identifier: "foo"})
82
+ expect(cmd.parse(" ( foo ) ")).to eq({tag_name: "foo"})
83
83
  end
84
84
 
85
85
  it "parses '((foo))'" do
86
- expect(cmd.parse("((foo))")).to eq({identifier: "foo"})
86
+ expect(cmd.parse("((foo))")).to eq({tag_name: "foo"})
87
87
  end
88
88
 
89
89
  it "parses '(( foo ))'" do
90
- expect(cmd.parse("(( foo ))")).to eq({identifier: "foo"})
90
+ expect(cmd.parse("(( foo ))")).to eq({tag_name: "foo"})
91
91
  end
92
92
 
93
93
  it "parses ' ( ( foo ) ) '" do
94
- expect(cmd.parse("( ( foo ) )")).to eq({identifier: "foo"})
94
+ expect(cmd.parse("( ( foo ) )")).to eq({tag_name: "foo"})
95
95
  end
96
96
 
97
- it "parses 'identifier with prefix and'" do
98
- expect(cmd.parse("android")).to eq({identifier: "android"})
97
+ it "parses 'tag_name with prefix and'" do
98
+ expect(cmd.parse("android")).to eq({tag_name: "android"})
99
99
  end
100
100
 
101
- it "parses 'identifier with infix and'" do
102
- expect(cmd.parse("islander")).to eq({identifier: "islander"})
101
+ it "parses 'tag_name with infix and'" do
102
+ expect(cmd.parse("islander")).to eq({tag_name: "islander"})
103
103
  end
104
104
 
105
- it "parses 'identifier with suffix and'" do
106
- expect(cmd.parse("mainland")).to eq({identifier: "mainland"})
105
+ it "parses 'tag_name with suffix and'" do
106
+ expect(cmd.parse("mainland")).to eq({tag_name: "mainland"})
107
107
  end
108
108
 
109
- it "parses 'identifier with prefix or'" do
110
- expect(cmd.parse("oreo")).to eq({identifier: "oreo"})
109
+ it "parses 'tag_name with prefix or'" do
110
+ expect(cmd.parse("oreo")).to eq({tag_name: "oreo"})
111
111
  end
112
112
 
113
- it "parses 'identifier with infix or'" do
114
- expect(cmd.parse("category")).to eq({identifier: "category"})
113
+ it "parses 'tag_name with infix or'" do
114
+ expect(cmd.parse("category")).to eq({tag_name: "category"})
115
115
  end
116
116
 
117
- it "parses 'identifier with suffix or'" do
118
- expect(cmd.parse("imperator")).to eq({identifier: "imperator"})
117
+ it "parses 'tag_name with suffix or'" do
118
+ expect(cmd.parse("imperator")).to eq({tag_name: "imperator"})
119
119
  end
120
120
 
121
- it "parses 'identifier with prefix not'" do
122
- expect(cmd.parse("nothing")).to eq({identifier: "nothing"})
121
+ it "parses 'tag_name with prefix not'" do
122
+ expect(cmd.parse("nothing")).to eq({tag_name: "nothing"})
123
123
  end
124
124
 
125
- it "parses 'identifier with infix not'" do
126
- expect(cmd.parse("annotation")).to eq({identifier: "annotation"})
125
+ it "parses 'tag_name with infix not'" do
126
+ expect(cmd.parse("annotation")).to eq({tag_name: "annotation"})
127
127
  end
128
128
 
129
- it "parses 'identifier with suffix not'" do
130
- expect(cmd.parse("forgetmenot")).to eq({identifier: "forgetmenot"})
129
+ it "parses 'tag_name with suffix not'" do
130
+ expect(cmd.parse("forgetmenot")).to eq({tag_name: "forgetmenot"})
131
131
  end
132
132
 
133
133
  it "parses 'foo bar'" do
134
- expect(cmd.parse("foo bar")).to eq({left: {identifier: "foo"}, binary_op: nil, right: {identifier: "bar"}})
134
+ expect(cmd.parse("foo bar")).to eq({left: {tag_name: "foo"}, binary_op: nil, right: {tag_name: "bar"}})
135
135
  end
136
136
 
137
137
  it "parses 'foo bar baz'" do
138
- expect(cmd.parse("foo bar baz")).to eq({left: {identifier: "foo"}, binary_op: nil, right: {left: {identifier: "bar"}, binary_op: nil, right: {identifier: "baz"}}})
138
+ expect(cmd.parse("foo bar baz")).to eq({left: {tag_name: "foo"}, binary_op: nil, right: {left: {tag_name: "bar"}, binary_op: nil, right: {tag_name: "baz"}}})
139
139
  end
140
140
 
141
141
  it "parses 'not foo'" do
142
- expect(cmd.parse("not foo")).to eq({unary_op: "not", expression: {identifier: "foo"}})
142
+ expect(cmd.parse("not foo")).to eq({unary_op: "not", expression: {tag_name: "foo"}})
143
143
  end
144
144
 
145
145
  it "parses '! foo'" do
146
- expect(cmd.parse("! foo")).to eq({unary_op: "!", expression: {identifier: "foo"}})
146
+ expect(cmd.parse("! foo")).to eq({unary_op: "!", expression: {tag_name: "foo"}})
147
147
  end
148
148
 
149
149
  it "parses '~ foo'" do
150
- expect(cmd.parse("~ foo")).to eq({unary_op: "~", expression: {identifier: "foo"}})
150
+ expect(cmd.parse("~ foo")).to eq({unary_op: "~", expression: {tag_name: "foo"}})
151
151
  end
152
152
 
153
153
  it "parses 'not(not foo)'" do
154
- expect(cmd.parse("not(not foo)")).to eq({unary_op: "not", expression: {unary_op: "not", expression: {identifier: "foo"}}})
154
+ expect(cmd.parse("not(not foo)")).to eq({unary_op: "not", expression: {unary_op: "not", expression: {tag_name: "foo"}}})
155
155
  end
156
156
 
157
157
  it "parses '!(!foo)'" do
158
- expect(cmd.parse("!(!foo)")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {identifier: "foo"}}})
158
+ expect(cmd.parse("!(!foo)")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {tag_name: "foo"}}})
159
159
  end
160
160
 
161
161
  it "parses '~(~foo)'" do
162
- expect(cmd.parse("~(~foo)")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {identifier: "foo"}}})
162
+ expect(cmd.parse("~(~foo)")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {tag_name: "foo"}}})
163
163
  end
164
164
 
165
165
  it "parses 'not not foo'" do
166
- expect(cmd.parse("not not foo")).to eq({unary_op: "not", expression: {unary_op: "not", expression: {identifier: "foo"}}})
166
+ expect(cmd.parse("not not foo")).to eq({unary_op: "not", expression: {unary_op: "not", expression: {tag_name: "foo"}}})
167
167
  end
168
168
 
169
169
  it "parses '!!foo'" do
170
- expect(cmd.parse("!! foo")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {identifier: "foo"}}})
170
+ expect(cmd.parse("!! foo")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {tag_name: "foo"}}})
171
171
  end
172
172
 
173
173
  it "parses '! ! foo'" do
174
- expect(cmd.parse("!! foo")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {identifier: "foo"}}})
174
+ expect(cmd.parse("!! foo")).to eq({unary_op: "!", expression: {unary_op: "!", expression: {tag_name: "foo"}}})
175
175
  end
176
176
 
177
177
  it "parses '~~foo'" do
178
- expect(cmd.parse("~~ foo")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {identifier: "foo"}}})
178
+ expect(cmd.parse("~~ foo")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {tag_name: "foo"}}})
179
179
  end
180
180
 
181
181
  it "parses '~ ~ foo'" do
182
- expect(cmd.parse("~~ foo")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {identifier: "foo"}}})
182
+ expect(cmd.parse("~~ foo")).to eq({unary_op: "~", expression: {unary_op: "~", expression: {tag_name: "foo"}}})
183
183
  end
184
184
 
185
185
  it "parses 'foo and bar'" do
186
- expect(cmd.parse("foo and bar")).to eq({left: {identifier: "foo"}, binary_op: "and", right: {identifier: "bar"}})
186
+ expect(cmd.parse("foo and bar")).to eq({left: {tag_name: "foo"}, binary_op: "and", right: {tag_name: "bar"}})
187
187
  end
188
188
 
189
189
  it "parses 'foo and bar and baz'" do
190
- expect(cmd.parse("foo and bar and baz")).to eq({left: {identifier: "foo"}, binary_op: "and", right: {left: {identifier: "bar"}, binary_op: "and", right: {identifier: "baz"}}})
190
+ expect(cmd.parse("foo and bar and baz")).to eq({left: {tag_name: "foo"}, binary_op: "and", right: {left: {tag_name: "bar"}, binary_op: "and", right: {tag_name: "baz"}}})
191
191
  end
192
192
 
193
193
  it "parses 'foo&bar'" do
194
- expect(cmd.parse("foo&bar")).to eq({left: {identifier: "foo"}, binary_op: "&", right: {identifier: "bar"}})
194
+ expect(cmd.parse("foo&bar")).to eq({left: {tag_name: "foo"}, binary_op: "&", right: {tag_name: "bar"}})
195
195
  end
196
196
 
197
197
  it "parses 'foo & bar'" do
198
- expect(cmd.parse("foo & bar")).to eq({left: {identifier: "foo"}, binary_op: "&", right: {identifier: "bar"}})
198
+ expect(cmd.parse("foo & bar")).to eq({left: {tag_name: "foo"}, binary_op: "&", right: {tag_name: "bar"}})
199
199
  end
200
200
 
201
201
  it "parses 'foo&bar&baz'" do
202
- expect(cmd.parse("foo & bar & baz")).to eq({left: {identifier: "foo"}, binary_op: "&", right: {left: {identifier: "bar"}, binary_op: "&", right: {identifier: "baz"}}})
202
+ expect(cmd.parse("foo & bar & baz")).to eq({left: {tag_name: "foo"}, binary_op: "&", right: {left: {tag_name: "bar"}, binary_op: "&", right: {tag_name: "baz"}}})
203
203
  end
204
204
 
205
205
  it "parses 'foo & bar & baz'" do
206
- expect(cmd.parse("foo & bar & baz")).to eq({left: {identifier: "foo"}, binary_op: "&", right: {left: {identifier: "bar"}, binary_op: "&", right: {identifier: "baz"}}})
206
+ expect(cmd.parse("foo & bar & baz")).to eq({left: {tag_name: "foo"}, binary_op: "&", right: {left: {tag_name: "bar"}, binary_op: "&", right: {tag_name: "baz"}}})
207
207
  end
208
208
 
209
209
  it "parses 'foo&&bar'" do
210
- expect(cmd.parse("foo&&bar")).to eq({left: {identifier: "foo"}, binary_op: "&&", right: {identifier: "bar"}})
210
+ expect(cmd.parse("foo&&bar")).to eq({left: {tag_name: "foo"}, binary_op: "&&", right: {tag_name: "bar"}})
211
211
  end
212
212
 
213
213
  it "parses 'foo && bar'" do
214
- expect(cmd.parse("foo && bar")).to eq({left: {identifier: "foo"}, binary_op: "&&", right: {identifier: "bar"}})
214
+ expect(cmd.parse("foo && bar")).to eq({left: {tag_name: "foo"}, binary_op: "&&", right: {tag_name: "bar"}})
215
215
  end
216
216
 
217
217
  it "parses 'foo&&bar&&baz'" do
218
- expect(cmd.parse("foo&&bar&&baz")).to eq({left: {identifier: "foo"}, binary_op: "&&", right: {left: {identifier: "bar"}, binary_op: "&&", right: {identifier: "baz"}}})
218
+ expect(cmd.parse("foo&&bar&&baz")).to eq({left: {tag_name: "foo"}, binary_op: "&&", right: {left: {tag_name: "bar"}, binary_op: "&&", right: {tag_name: "baz"}}})
219
219
  end
220
220
 
221
221
  it "parses 'foo && bar && baz'" do
222
- expect(cmd.parse("foo && bar && baz")).to eq({left: {identifier: "foo"}, binary_op: "&&", right: {left: {identifier: "bar"}, binary_op: "&&", right: {identifier: "baz"}}})
222
+ expect(cmd.parse("foo && bar && baz")).to eq({left: {tag_name: "foo"}, binary_op: "&&", right: {left: {tag_name: "bar"}, binary_op: "&&", right: {tag_name: "baz"}}})
223
223
  end
224
224
 
225
225
  it "parses 'foo or bar'" do
226
- expect(cmd.parse("foo or bar")).to eq({left: {identifier: "foo"}, binary_op: "or", right: {identifier: "bar"}})
226
+ expect(cmd.parse("foo or bar")).to eq({left: {tag_name: "foo"}, binary_op: "or", right: {tag_name: "bar"}})
227
227
  end
228
228
 
229
229
  it "parses 'foo or bar or baz'" do
230
- expect(cmd.parse("foo or bar or baz")).to eq({left: {identifier: "foo"}, binary_op: "or", right: {left: {identifier: "bar"}, binary_op: "or", right: {identifier: "baz"}}})
230
+ expect(cmd.parse("foo or bar or baz")).to eq({left: {tag_name: "foo"}, binary_op: "or", right: {left: {tag_name: "bar"}, binary_op: "or", right: {tag_name: "baz"}}})
231
231
  end
232
232
 
233
233
  it "parses 'foo|bar'" do
234
- expect(cmd.parse("foo|bar")).to eq({left: {identifier: "foo"}, binary_op: "|", right: {identifier: "bar"}})
234
+ expect(cmd.parse("foo|bar")).to eq({left: {tag_name: "foo"}, binary_op: "|", right: {tag_name: "bar"}})
235
235
  end
236
236
 
237
237
  it "parses 'foo | bar'" do
238
- expect(cmd.parse("foo | bar")).to eq({left: {identifier: "foo"}, binary_op: "|", right: {identifier: "bar"}})
238
+ expect(cmd.parse("foo | bar")).to eq({left: {tag_name: "foo"}, binary_op: "|", right: {tag_name: "bar"}})
239
239
  end
240
240
 
241
241
  it "parses 'foo|bar|baz'" do
242
- expect(cmd.parse("foo|bar|baz")).to eq({left: {identifier: "foo"}, binary_op: "|", right: {left: {identifier: "bar"}, binary_op: "|", right: {identifier: "baz"}}})
242
+ expect(cmd.parse("foo|bar|baz")).to eq({left: {tag_name: "foo"}, binary_op: "|", right: {left: {tag_name: "bar"}, binary_op: "|", right: {tag_name: "baz"}}})
243
243
  end
244
244
 
245
245
  it "parses 'foo | bar | baz'" do
246
- expect(cmd.parse("foo | bar | baz")).to eq({left: {identifier: "foo"}, binary_op: "|", right: {left: {identifier: "bar"}, binary_op: "|", right: {identifier: "baz"}}})
246
+ expect(cmd.parse("foo | bar | baz")).to eq({left: {tag_name: "foo"}, binary_op: "|", right: {left: {tag_name: "bar"}, binary_op: "|", right: {tag_name: "baz"}}})
247
247
  end
248
248
 
249
249
  it "parses 'foo||bar'" do
250
- expect(cmd.parse("foo||bar")).to eq({left: {identifier: "foo"}, binary_op: "||", right: {identifier: "bar"}})
250
+ expect(cmd.parse("foo||bar")).to eq({left: {tag_name: "foo"}, binary_op: "||", right: {tag_name: "bar"}})
251
251
  end
252
252
 
253
253
  it "parses 'foo || bar'" do
254
- expect(cmd.parse("foo || bar")).to eq({left: {identifier: "foo"}, binary_op: "||", right: {identifier: "bar"}})
254
+ expect(cmd.parse("foo || bar")).to eq({left: {tag_name: "foo"}, binary_op: "||", right: {tag_name: "bar"}})
255
255
  end
256
256
 
257
257
  it "parses 'foo||bar||baz'" do
258
- expect(cmd.parse("foo||bar||baz")).to eq({left: {identifier: "foo"}, binary_op: "||", right: {left: {identifier: "bar"}, binary_op: "||", right: {identifier: "baz"}}})
258
+ expect(cmd.parse("foo||bar||baz")).to eq({left: {tag_name: "foo"}, binary_op: "||", right: {left: {tag_name: "bar"}, binary_op: "||", right: {tag_name: "baz"}}})
259
259
  end
260
260
 
261
261
  it "parses 'foo || bar || baz'" do
262
- expect(cmd.parse("foo || bar || baz")).to eq({left: {identifier: "foo"}, binary_op: "||", right: {left: {identifier: "bar"}, binary_op: "||", right: {identifier: "baz"}}})
262
+ expect(cmd.parse("foo || bar || baz")).to eq({left: {tag_name: "foo"}, binary_op: "||", right: {left: {tag_name: "bar"}, binary_op: "||", right: {tag_name: "baz"}}})
263
263
  end
264
264
 
265
265
  it "parses '(foo and bar) or baz'" do
266
- expect(cmd.parse("(foo and bar) or baz")).to eq({left: {left: {identifier: "foo"}, binary_op: "and", right: {identifier: "bar"}}, binary_op: "or", right: {identifier: "baz"}})
266
+ expect(cmd.parse("(foo and bar) or baz")).to eq({left: {left: {tag_name: "foo"}, binary_op: "and", right: {tag_name: "bar"}}, binary_op: "or", right: {tag_name: "baz"}})
267
267
  end
268
268
 
269
269
  it "parses 'foo and (bar or baz)'" do
270
- expect(cmd.parse("foo and (bar or baz)")).to eq({left: {identifier: "foo"}, binary_op: "and", right: {left: {identifier: "bar"}, binary_op: "or", right: {identifier: "baz"}}})
270
+ expect(cmd.parse("foo and (bar or baz)")).to eq({left: {tag_name: "foo"}, binary_op: "and", right: {left: {tag_name: "bar"}, binary_op: "or", right: {tag_name: "baz"}}})
271
271
  end
272
272
 
273
273
  it "parses 'not foo and bar'" do
274
- expect(cmd.parse("not foo and bar")).to eq({unary_op: "not", expression: {left: {identifier: "foo"}, binary_op: "and", right: {identifier: "bar"}}})
274
+ expect(cmd.parse("not foo and bar")).to eq({unary_op: "not", expression: {left: {tag_name: "foo"}, binary_op: "and", right: {tag_name: "bar"}}})
275
275
  end
276
276
 
277
277
  it "parses '! foo and bar'" do
278
- expect(cmd.parse("! foo and bar")).to eq({left: {unary_op: "!", expression: {identifier: "foo"}}, binary_op: "and", right: {identifier: "bar"}})
278
+ expect(cmd.parse("! foo and bar")).to eq({left: {unary_op: "!", expression: {tag_name: "foo"}}, binary_op: "and", right: {tag_name: "bar"}})
279
279
  end
280
280
 
281
281
  it "parses 'not foo && bar'" do
282
- expect(cmd.parse("not foo && bar")).to eq({unary_op: "not", expression: {left: {identifier: "foo"}, binary_op: "&&", right: {identifier: "bar"}}})
282
+ expect(cmd.parse("not foo && bar")).to eq({unary_op: "not", expression: {left: {tag_name: "foo"}, binary_op: "&&", right: {tag_name: "bar"}}})
283
283
  end
284
284
 
285
285
  it "parses '! foo && bar'" do
286
- expect(cmd.parse("! foo && bar")).to eq({left: {unary_op: "!", expression: {identifier: "foo"}}, binary_op: "&&", right: {identifier: "bar"}})
286
+ expect(cmd.parse("! foo && bar")).to eq({left: {unary_op: "!", expression: {tag_name: "foo"}}, binary_op: "&&", right: {tag_name: "bar"}})
287
+ end
288
+
289
+ it "parses 'f(x)'" do
290
+ expect(cmd.parse("f(x)")).to eq({funcall: "f", funcall_args: {funcall_args_head: {tag_name: "x"}}})
291
+ end
292
+
293
+ it "parses 'f(x, \"y\")'" do
294
+ expect(cmd.parse("f(x, \"y\")")).to eq({funcall: "f", funcall_args: {funcall_args_head: {tag_name: "x"}, funcall_args_tail: {funcall_args_head: {string: "\"y\""}}}})
295
+ end
296
+
297
+ it "parses 'f(x, \"y\", /z/)'" do
298
+ expect(cmd.parse("f(x, \"y\", /z/)")).to eq({funcall: "f", funcall_args: {funcall_args_head: {tag_name: "x"}, funcall_args_tail: {funcall_args_head: {string: "\"y\""}, funcall_args_tail: {funcall_args_head: {regexp: "/z/"}}}}})
299
+ end
300
+
301
+ it "parses 'g ( 12345 )'" do
302
+ expect(cmd.parse("g ( 12345 )")).to eq({funcall: "g", funcall_args: {funcall_args_head: {integer: "12345"}}})
303
+ end
304
+
305
+ it "parses 'g ( 12345 , 3.1415 )'" do
306
+ expect(cmd.parse("g ( 12345 , 3.1415 )")).to eq({funcall: "g", funcall_args: {funcall_args_head: {integer: "12345"}, funcall_args_tail: {funcall_args_head: {float: "3.1415"}}}})
307
+ end
308
+
309
+ it "parses 'f()'" do
310
+ expect(cmd.parse("f()")).to eq({funcall: "f"})
311
+ end
312
+
313
+ it "parses 'g(f())'" do
314
+ expect(cmd.parse("g(f())")).to eq({funcall: "g", funcall_args: {funcall_args_head: {funcall: "f"}}})
315
+ end
316
+
317
+ it "parses 'foo and bar(y)'" do
318
+ expect(cmd.parse("foo and bar(y)")).to eq({binary_op: "and", left: {tag_name: "foo"}, right: {funcall: "bar", funcall_args: {funcall_args_head: {tag_name: "y"}}}})
319
+ end
320
+
321
+ it "parses 'foo(x) and bar(y)'" do
322
+ expect(cmd.parse("foo(x) and bar(y)")).to eq({binary_op: "and", left: {funcall: "foo", funcall_args: {funcall_args_head: {tag_name: "x"}}}, right: {funcall: "bar", funcall_args: {funcall_args_head: {tag_name: "y"}}}})
287
323
  end
288
324
 
289
325
  it "is unable to parse ' '" do