hotdog 0.12.0 → 0.13.0

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