travis-conditions 0.0.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -0
  3. data/Gemfile.lock +1 -1
  4. data/NOTES.md +107 -0
  5. data/README.md +261 -13
  6. data/bin/travis-conditions +34 -0
  7. data/lib/travis/conditions.rb +10 -16
  8. data/lib/travis/conditions/v0.rb +30 -0
  9. data/lib/travis/conditions/v0/data.rb +57 -0
  10. data/lib/travis/conditions/v0/eval.rb +70 -0
  11. data/lib/travis/conditions/v0/parser.rb +204 -0
  12. data/lib/travis/conditions/v1.rb +19 -0
  13. data/lib/travis/conditions/v1/boolean.rb +71 -0
  14. data/lib/travis/conditions/v1/data.rb +75 -0
  15. data/lib/travis/conditions/v1/eval.rb +114 -0
  16. data/lib/travis/conditions/v1/helper.rb +30 -0
  17. data/lib/travis/conditions/v1/parser.rb +214 -0
  18. data/lib/travis/conditions/version.rb +1 -1
  19. data/spec/conditions_spec.rb +15 -0
  20. data/spec/v0/conditions_spec.rb +15 -0
  21. data/spec/{data_spec.rb → v0/data_spec.rb} +6 -1
  22. data/spec/{eval_spec.rb → v0/eval_spec.rb} +1 -1
  23. data/spec/v0/fixtures/failures.txt +342 -0
  24. data/spec/v0/fixtures/passes.txt +1685 -0
  25. data/spec/{parser_spec.rb → v0/parser_spec.rb} +1 -1
  26. data/spec/v1/conditions_spec.rb +44 -0
  27. data/spec/v1/data_spec.rb +30 -0
  28. data/spec/v1/eval_spec.rb +349 -0
  29. data/spec/v1/fixtures/failures.txt +336 -0
  30. data/spec/v1/fixtures/passes.txt +1634 -0
  31. data/spec/v1/parser/boolean_spec.rb +215 -0
  32. data/spec/v1/parser/call_spec.rb +68 -0
  33. data/spec/v1/parser/comma_spec.rb +28 -0
  34. data/spec/v1/parser/cont_spec.rb +41 -0
  35. data/spec/v1/parser/eq_spec.rb +16 -0
  36. data/spec/v1/parser/in_list_spec.rb +60 -0
  37. data/spec/v1/parser/is_pred_spec.rb +24 -0
  38. data/spec/v1/parser/list_spec.rb +36 -0
  39. data/spec/v1/parser/operand_spec.rb +16 -0
  40. data/spec/v1/parser/parens_spec.rb +28 -0
  41. data/spec/v1/parser/quoted_spec.rb +24 -0
  42. data/spec/v1/parser/re_spec.rb +16 -0
  43. data/spec/v1/parser/regex_spec.rb +12 -0
  44. data/spec/v1/parser/space_spec.rb +40 -0
  45. data/spec/v1/parser/term_spec.rb +84 -0
  46. data/spec/v1/parser/val_spec.rb +24 -0
  47. data/spec/v1/parser/var_spec.rb +16 -0
  48. data/spec/v1/parser_spec.rb +486 -0
  49. data/spec/v1/user_spec.rb +223 -0
  50. metadata +48 -9
  51. data/lib/travis/conditions/data.rb +0 -45
  52. data/lib/travis/conditions/eval.rb +0 -68
  53. data/lib/travis/conditions/parser.rb +0 -202
@@ -0,0 +1,223 @@
1
+ # notes
2
+ #
3
+ # people seem to be confusing `=` (equality) with `IS` (predicate) a lot
4
+ # reject builds that fail to parse the condition, instead of ignoring it
5
+ #
6
+ # docs: change docs to recommend quotes around values in `= "string"`
7
+ # docs: change docs to recommend quotes around values in `IN ("list")`
8
+ # docs: mention NO SHELL CODE PLEASE
9
+ # docs: mention reqexp limitations, and recommend using `/.../` first
10
+ # docs: make the warning about `env` only having the .travis.yml vars bigger and fatter
11
+
12
+ describe Travis::Conditions::V1, 'real conditions' do
13
+ broken = <<~strs.split("\n")
14
+ branch =~ ^(!:release\/) or tag =~ ^version_code\/
15
+ tag =~ (^version_code\/)|(^$)
16
+ branch = develop/pipes=travis
17
+ strs
18
+
19
+ used = <<~strs.split("\n")
20
+ ((NOT branch =~ ^develop/.*$) OR (branch = develop/travis)) AND (tag IS blank)
21
+ (NOT branch =~ ^develop/.*$) AND (tag IS blank)
22
+ (branch != master OR type = cron OR type = api)
23
+ (branch = develop/travis) AND (tag IS blank)
24
+ (branch = master) AND (NOT (type IN (api, pull_request)))
25
+ (branch = master) AND (NOT (type IN (api,pull_request)))
26
+ (branch = master) AND (NOT (type IN (push, pull_request)))
27
+ (branch = master) AND (NOT (type IN (push,pull_request)))
28
+ (branch = master) AND (NOT(type IN (api, pull_request)))
29
+ (branch = master) AND (NOT(type IN (api,pull_request)))
30
+ (branch = master) AND (NOT(type IN (push ,pull_request)))
31
+ (branch = master) AND (NOT(type IN (push, pull_request)))
32
+ (branch = master) AND (NOT(type IN (push,pull_request)))
33
+ (type = pull_request) OR (tag IS present)
34
+ (type = push AND NOT (branch = develop)) OR (type = cron)
35
+ NOT branch = master
36
+ NOT branch =~ ^develop/.*$
37
+ NOT tag = "stable"
38
+ NOT tag = stable
39
+ NOT tag =~ \+d2$
40
+ branch = develop/hardening
41
+ branch = develop/pipes-travis
42
+ branch = develop/travis
43
+ branch = master
44
+ branch = master AND NOT(type IN (push,pull_request))
45
+ branch = master AND type = pull_request
46
+ branch = master AND type = push
47
+ branch = master OR tag =~ ^v\d+\.\d+$
48
+ branch = master OR tag IS present
49
+ branch = master OR type = pull_request OR branch =~ ^rc_.*
50
+ branch = staging
51
+ branch = task/ASI-93-auto-update-campaign-performance-summary-for-periscope
52
+ branch = travis
53
+ branch =~ /^release.*$/
54
+ branch =~ /release/
55
+ branch =~ ^develop
56
+ branch =~ ^develop OR branch =~ ^master OR branch =~ ^release
57
+ branch =~ ^release
58
+ branch =~ ^release.*$
59
+ branch =~ ^release/ or tag =~ ^version_code\/
60
+ branch IN (master, ^develop, ^release)
61
+ branch IN (master, develop) OR branch =~ ^release\/.+$ OR branch =~ ^hotfix\/.+$ OR branch =~ ^bugfix\/.+$ OR branch =~ ^support\/.+$ ORtag IS present OR type IN (pull_request, api)
62
+ branch IN (master, development, features)
63
+ branch is blank
64
+ env(TRAVIS_SECURE_ENV_VARS) = true
65
+ fork = false
66
+ not tag =~ ^autobuild
67
+ tag =~ ''
68
+ tag =~ /(^version_code\/)|(^$)/
69
+ tag =~ /^$|\s+/
70
+ tag =~ [0-9]+\.[0-9]+\.[0-9]+
71
+ tag =~ ^\d+(\.\d+)+(-.*)?
72
+ tag =~ ^\d+(\.\d+)+(-.*)? OR branch = travisbuilds-selenium
73
+ tag =~ ^travis*
74
+ tag =~ ^v
75
+ tag =~ ^v
76
+ tag =~ ^version_code/ OR (tag IS NOT present)
77
+ tag =~ ^version_code/ OR tag IS NOT present
78
+ tag =~ ^version_code\/ OR tag =~ ^$
79
+ tag =~ ^version_code\/|^$
80
+ tag IS blank
81
+ tag IS blank
82
+ tag IS present
83
+ tag is blank
84
+ type = push
85
+ type IN (api, cron)
86
+ type IN (push, pull_request)
87
+ strs
88
+
89
+ fixed = <<~strs.split("\n").reject(&:empty?)
90
+ true
91
+ 1=1
92
+ ( branch NOT IN (master) OR fork = true ) AND type IN (push, api, pull_request)
93
+ (((type != pull_request) AND (branch = master)) OR (tag =~ ^\\d+\\.\\d+\\.\\d+$))
94
+ ((branch = master) OR (tag =~ ^\\d+\\.\\d+\\.\\d+$))
95
+ (branch = master AND type != pull_request) AND (NOT tag =~ ^v)
96
+ (branch = master AND type != pull_request) OR (tag =~ ^v)
97
+ (branch = master OR branch =~ ^infra/$) AND type = push
98
+ (branch = master OR tag =~ ^deploy/) AND (type != pull_request)
99
+ (branch = master) AND (NOT (tag =~prod_))
100
+ (branch = master) AND (tag =~ ^v\\d+\\.\\d+\\.\\d+.*$) AND (type IN (push, pull_request))
101
+ (branch = master) AND (tag =~ ^v\\d+\\.\\d+\\.\\d+.*$) AND type IN (push, pull_request)
102
+ (tag =~ ^prod_)
103
+ (tag =~ ^v) AND (branch = master)
104
+ (tag =~ ^v) AND (branch = release)
105
+ (tag =~ ^v) AND (repo = meniga/mobile-sdk-android)
106
+ (type IN (cron, api)) OR ((tag IS present) AND (tag =~ ^v))
107
+ (type IN (cron, api)) OR (tag =~ ^v)
108
+ (type IN (cron, api)) OR (tag =~ ^v?([0-9]+)\\.([0-9]+)\\.([0-9]+)(?:(?:\\-|\\+)([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)){0,2}$)
109
+ NOT type IN (pull_request) AND (branch = master OR tag =~ ^v[1-9])
110
+ TRAVIS_PULL_REQUEST = false
111
+ branch = master AND tag is blank AND fork is false AND repo = head_repo AND type != pull_request
112
+ branch = master AND tag is blank AND fork is false AND type != pull_request
113
+ branch = master AND type = push
114
+ branch = master AND type == pull_request
115
+ branch IN (master, dev_package)
116
+ branch NOT IN (master) OR fork = true
117
+ branch in (master, dev) OR type == pull
118
+ env(FOO) = env(TRAVIS_SECURE_ENV_VARS)
119
+ env(IS_TAGS_ONLY) = env(NO)
120
+ env(IS_TAGS_ONLY) = env(NO) AND env(YES) IN (env(IS_FULL_REGRESSION), env(IS_READY_TO_BUILD))
121
+ env(PRIOR_VERSION) != env(RELEASE_VERSION)
122
+ env(PRIOR_VERSION) IS present AND env(PRIOR_VERSION) != env(RELEASE_VERSION) AND branch = master AND type = push AND repo = cotsog/travis_ci_prod_regular
123
+ env(PRIOR_VERSION) IS present AND env(PRIOR_VERSION) != env(RELEASE_VERSION) AND branch = master AND type = push AND repo = justien/travis_conditional_stages
124
+ env(PRIOR_VERSION) IS present AND env(PRIOR_VERSION) != env(RELEASE_VERSION) AND branch = master AND type = push AND repo = plus3it/terraform-aws-watchmaker
125
+ env(TRAVIS_SECURE_ENV_VARS) IS true
126
+ env(env_name) IS blank
127
+ env(env_name) IS present
128
+ tag =~ ^version_code/ OR tag IS NOT present
129
+ tag IS NOT blank
130
+ tag IS NOT present
131
+ tag IS blank
132
+ tag IS present
133
+ tag IS present OR branch == master
134
+ true = false
135
+ type = pull_request
136
+ type IN (api, cron)
137
+ type IN (push, cron) AND (NOT branch =~ [0-9]{8,})
138
+ type IN (push, cron) AND (tag =~ ^$)
139
+ type IN (push, cron) AND NOT (branch =~ [0-9]{8,})
140
+ type NOT IN (api, cron)
141
+ type NOT IN (cron)
142
+
143
+ env(FORCE_REBUILD) ~= ^true$
144
+ env(REBUILD) ~= ^true$ OR env(FORCE_REBUILD) ~= ^true$ OR (branch = master AND type = push)
145
+ type = cron OR env(TEST_SQLITE) ~= ^true$
146
+
147
+ branch = master AND tag is blank AND fork is false AND repo = head_repo AND type != pull_request
148
+ tags IS blank
149
+ type = cron OR env(TEST_SQLITE) IS true
150
+ tag !~ \\\n ^v-
151
+
152
+ env(REBUILD) OR env(FORCE_REBUILD) OR branch = master
153
+ env(DOCKER_BUILD)
154
+ env(DOCKER_PASSWORD)
155
+ type = cron OR env(TEST_SQLITE)
156
+
157
+ type != pull_request AND ( branch =~ ^release$ OR branch =~ ^release-(\d).(\d).(\d)$ )
158
+ type != pull_request AND (branch =~ ^release$ OR branch =~ ^release-(\d).(\d).(\d)$)
159
+ (branch = master) OR (branch =~ ^release/) OR (tag is present)
160
+ strs
161
+
162
+ kaputt = <<~strs.split("\n")
163
+ # IS used for comparison
164
+ (branch = dev) AND (type IS cron)
165
+ (repo IS 2m/sssio) AND (tag =~ ^v)
166
+ branch IS master
167
+ type IS push
168
+
169
+ # broken IN list
170
+ (type = pull_request) AND (branch IN dev)
171
+
172
+ # = followed by IN
173
+ branch = IN (master, dev_package)
174
+
175
+ # missing boolean operand
176
+ branch = master AND ( tag =~ ^deploy/ OR )
177
+
178
+ # NON instead of NOT
179
+ branch = master AND type NON IN (pull_request)
180
+
181
+ # missing AND or OR
182
+ branch = master NOT type = pull_request
183
+
184
+ # triple operator
185
+ branch = type = pull_request
186
+
187
+ # missing IS
188
+ tag NOT present
189
+
190
+ # bash code
191
+ env(FORCE_JOBS) IS blank && (branch = ${MASTER_BRANCH} OR type = pull_request)
192
+
193
+ # operator !=~
194
+ branch = master or branch !=~ ^renovate/
195
+
196
+ # needs /.../ around the pattern
197
+ env(TRAVIS_COMMIT_MESSAGE) =~ ^Replace Conditionalaaa$
198
+
199
+ # missing quotes
200
+ env(TRAVIS_COMMIT_MESSAGE) IN (Replace Conditional)
201
+ env(TRAVIS_COMMIT_MESSAGE) = Replace conditional
202
+ strs
203
+
204
+ strs = used + fixed
205
+
206
+ strs.each do |str|
207
+ context do
208
+ let(:subject) { described_class.parse(str) }
209
+ it(str) { expect { subject }.to_not raise_error }
210
+ end
211
+ end
212
+
213
+ context do
214
+ let(:str) do
215
+ %(
216
+ repo = iribeyri/travis-experiments
217
+ AND type != pull_request
218
+ AND (branch = master OR tag =~ ^v[0-9]+\.[0-9]+\.[0-9]+.*$)
219
+ )
220
+ end
221
+ it { expect { described_class.parse(str) }.to_not raise_error }
222
+ end
223
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: travis-conditions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Travis CI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-17 00:00:00.000000000 Z
11
+ date: 2018-06-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parslet
@@ -26,23 +26,62 @@ dependencies:
26
26
  version: '1.8'
27
27
  description:
28
28
  email: contact@travis-ci.org
29
- executables: []
29
+ executables:
30
+ - travis-conditions
30
31
  extensions: []
31
32
  extra_rdoc_files: []
32
33
  files:
34
+ - CHANGELOG.md
33
35
  - Gemfile
34
36
  - Gemfile.lock
35
37
  - LICENSE
38
+ - NOTES.md
36
39
  - README.md
40
+ - bin/travis-conditions
37
41
  - lib/travis/conditions.rb
38
- - lib/travis/conditions/data.rb
39
- - lib/travis/conditions/eval.rb
40
- - lib/travis/conditions/parser.rb
42
+ - lib/travis/conditions/v0.rb
43
+ - lib/travis/conditions/v0/data.rb
44
+ - lib/travis/conditions/v0/eval.rb
45
+ - lib/travis/conditions/v0/parser.rb
46
+ - lib/travis/conditions/v1.rb
47
+ - lib/travis/conditions/v1/boolean.rb
48
+ - lib/travis/conditions/v1/data.rb
49
+ - lib/travis/conditions/v1/eval.rb
50
+ - lib/travis/conditions/v1/helper.rb
51
+ - lib/travis/conditions/v1/parser.rb
41
52
  - lib/travis/conditions/version.rb
42
- - spec/data_spec.rb
43
- - spec/eval_spec.rb
44
- - spec/parser_spec.rb
53
+ - spec/conditions_spec.rb
45
54
  - spec/spec_helper.rb
55
+ - spec/v0/conditions_spec.rb
56
+ - spec/v0/data_spec.rb
57
+ - spec/v0/eval_spec.rb
58
+ - spec/v0/fixtures/failures.txt
59
+ - spec/v0/fixtures/passes.txt
60
+ - spec/v0/parser_spec.rb
61
+ - spec/v1/conditions_spec.rb
62
+ - spec/v1/data_spec.rb
63
+ - spec/v1/eval_spec.rb
64
+ - spec/v1/fixtures/failures.txt
65
+ - spec/v1/fixtures/passes.txt
66
+ - spec/v1/parser/boolean_spec.rb
67
+ - spec/v1/parser/call_spec.rb
68
+ - spec/v1/parser/comma_spec.rb
69
+ - spec/v1/parser/cont_spec.rb
70
+ - spec/v1/parser/eq_spec.rb
71
+ - spec/v1/parser/in_list_spec.rb
72
+ - spec/v1/parser/is_pred_spec.rb
73
+ - spec/v1/parser/list_spec.rb
74
+ - spec/v1/parser/operand_spec.rb
75
+ - spec/v1/parser/parens_spec.rb
76
+ - spec/v1/parser/quoted_spec.rb
77
+ - spec/v1/parser/re_spec.rb
78
+ - spec/v1/parser/regex_spec.rb
79
+ - spec/v1/parser/space_spec.rb
80
+ - spec/v1/parser/term_spec.rb
81
+ - spec/v1/parser/val_spec.rb
82
+ - spec/v1/parser/var_spec.rb
83
+ - spec/v1/parser_spec.rb
84
+ - spec/v1/user_spec.rb
46
85
  homepage: https://github.com/travis-ci/travis-conditions
47
86
  licenses:
48
87
  - MIT
@@ -1,45 +0,0 @@
1
- module Travis
2
- module Conditions
3
- class Data < Struct.new(:data)
4
- def initialize(data)
5
- data = symbolize(data)
6
- data[:env] = symbolize(to_h(data[:env] || {}))
7
- super(data)
8
- end
9
-
10
- def [](key)
11
- data[key.to_sym]
12
- end
13
-
14
- def env(key)
15
- data.fetch(:env, {})[key.to_sym]
16
- end
17
-
18
- private
19
-
20
- def to_h(obj)
21
- case obj
22
- when Hash
23
- obj
24
- else
25
- Array(obj).map { |value| split(value.to_s) }.to_h
26
- end
27
- end
28
-
29
- def split(str)
30
- str.split('=', 2)
31
- end
32
-
33
- def symbolize(value)
34
- case value
35
- when Hash
36
- value.map { |key, value| [key.to_sym, symbolize(value)] }.to_h
37
- when Array
38
- value.map { |value| symbolize(value) }
39
- else
40
- value
41
- end
42
- end
43
- end
44
- end
45
- end
@@ -1,68 +0,0 @@
1
- module Travis
2
- module Conditions
3
- class Eval < Struct.new(:sexp, :data)
4
- def apply
5
- !!evl(sexp)
6
- end
7
-
8
- private
9
-
10
- def evl(value)
11
- case value
12
- when Array
13
- send(*value)
14
- else
15
- data[value]
16
- end
17
- end
18
-
19
- def not(lft)
20
- !evl(lft)
21
- end
22
-
23
- def or(lft, rgt)
24
- evl(lft) || evl(rgt)
25
- end
26
-
27
- def and(lft, rgt)
28
- evl(lft) && evl(rgt)
29
- end
30
-
31
- def eq(lft, rgt)
32
- evl(lft) == rgt
33
- end
34
-
35
- def not_eq(lft, rgt)
36
- not eq(lft, rgt)
37
- end
38
-
39
- def match(lft, rgt)
40
- evl(lft) =~ Regexp.new(rgt)
41
- end
42
-
43
- def not_match(lft, rgt)
44
- not match(lft, rgt)
45
- end
46
-
47
- def in(lft, rgt)
48
- rgt.include?(evl(lft))
49
- end
50
-
51
- def is(lft, rgt)
52
- send(rgt, evl(lft))
53
- end
54
-
55
- def env(key)
56
- data.env(key)
57
- end
58
-
59
- def present(value)
60
- value.respond_to?(:empty?) && !value.empty?
61
- end
62
-
63
- def blank(value)
64
- !present(value)
65
- end
66
- end
67
- end
68
- end
@@ -1,202 +0,0 @@
1
- require 'parslet'
2
-
3
- module Travis
4
- module Conditions
5
- class Parser < Struct.new(:opts)
6
- FUNCS = %w(env)
7
- PRESENCE = %w(present blank)
8
-
9
- def parse(str)
10
- parser.parse(str)
11
- end
12
-
13
- private
14
-
15
- def parser
16
- @parser ||= define_parser(opts[:keys]).new
17
- end
18
-
19
- def define_parser(keywords)
20
- Class.new(Parslet::Parser) do
21
- root :expr_or
22
-
23
- rule :expr_or do
24
- spaced(expr_and.as(:lft), op_or, expr_or.as(:rgt)).as(:or) | expr_and
25
- end
26
-
27
- rule :expr_and do
28
- spaced(expr_inner.as(:lft), op_and, expr_and.as(:rgt)).as(:and) | expr_inner
29
- end
30
-
31
- rule :expr_inner do
32
- lnot(parens(expr_or) | expr_incl | expr_is | expr_regex | expr_cmp)
33
- end
34
-
35
- rule :expr_cmp do
36
- spaced(lhs.as(:lft), op_cmp.as(:op), value.as(:rgt)).as(:cmp)
37
- end
38
-
39
- rule :expr_regex do
40
- spaced(lhs.as(:lft), op_regex.as(:op), regex.as(:rgt)).as(:cmp)
41
- end
42
-
43
- rule :expr_incl do
44
- spaced(lhs.as(:lft), op_incl, parens(list).as(:rgt)).as(:incl)
45
- end
46
-
47
- rule :expr_is do
48
- spaced(lhs.as(:lft), op_is, presence.as(:rgt)).as(:is)
49
- end
50
-
51
- def lnot(node)
52
- (stri('not').maybe >> space >> node.as(:rgt)).as(:not) | node
53
- end
54
-
55
- rule :list do
56
- (value >> (ts(str(',')) >> value).repeat)
57
- end
58
-
59
- rule :lhs do
60
- func | keyword
61
- end
62
-
63
- rule :keyword do
64
- stris(*keywords)
65
- end
66
-
67
- rule :func do
68
- (stris(*FUNCS).as(:name) >> parens(word)).as(:func)
69
- end
70
-
71
- rule :word do
72
- match('[\w_\-]').repeat(1).as(:str)
73
- end
74
-
75
- rule :regex do
76
- quoted('/') | match('[\S]').repeat(1).as(:str)
77
- end
78
-
79
- rule :value do
80
- unquoted | quoted('"') | quoted("'")
81
- end
82
-
83
- rule :unquoted do
84
- match('[^\s\"\'\(\),]').repeat(1).as(:str)
85
- end
86
-
87
- def quoted(chr)
88
- str(chr) >> match("[^#{chr}]").repeat.as(:str) >> str(chr)
89
- end
90
-
91
- rule :presence do
92
- (stris(*PRESENCE)).as(:str)
93
- end
94
-
95
- rule :op_is do
96
- stri('is')
97
- end
98
-
99
- rule :op_cmp do
100
- str('=') | str('!=')
101
- end
102
-
103
- rule :op_regex do
104
- str('=~') | str('!~')
105
- end
106
-
107
- rule :op_incl do
108
- stri('in')
109
- end
110
-
111
- rule :op_or do
112
- stri('or')
113
- end
114
-
115
- rule :op_and do
116
- stri('and')
117
- end
118
-
119
- rule :space do
120
- match('\s').repeat(1)
121
- end
122
-
123
- rule :space? do
124
- space.maybe
125
- end
126
-
127
- def stris(*strs)
128
- strs.inject(stri(strs.shift)) { |node, str| node | stri(str) }
129
- end
130
-
131
- def stri(str)
132
- str(str) | str(str.upcase)
133
- end
134
-
135
- def parens?(node)
136
- spaced?(str('(').maybe, node, str(')').maybe)
137
- end
138
-
139
- def parens(node)
140
- spaced?(str('('), node, str(')'))
141
- end
142
-
143
- def spaced?(*nodes)
144
- nodes.zip([space?] * nodes.size).flatten[0..-2].inject(&:>>)
145
- end
146
-
147
- def spaced(*nodes)
148
- # nodes.zip([space] * nodes.size).flatten[0..-2].inject(&:>>)
149
- nodes.zip([space?] * nodes.size).flatten[0..-2].inject(&:>>)
150
- end
151
-
152
- def ls(node)
153
- space? >> node
154
- end
155
-
156
- def ts(node)
157
- node >> space?
158
- end
159
- end
160
- end
161
- end
162
-
163
- class Transform < Parslet::Transform
164
- OP = {
165
- '=' => :eq,
166
- '!=' => :not_eq,
167
- '=~' => :match,
168
- '!~' => :not_match,
169
- }
170
-
171
- str = ->(node) { node.is_a?(Hash) ? node[:str].to_s : node.to_s }
172
- sym = ->(node) { str.(node).downcase.to_sym }
173
- func = ->(node) { [sym.(node[:func][:name]), str.(node[:func])] }
174
- list = ->(node) { node.is_a?(Array) ? node.map { |v| str.(v) } : [str.(node)] }
175
- lhs = ->(node) { node.is_a?(Hash) && node.key?(:func) ? func.(node) : str.(node) }
176
-
177
- rule not: { rgt: subtree(:rgt) } do
178
- [:not, rgt]
179
- end
180
-
181
- rule or: { lft: subtree(:lft), rgt: subtree(:rgt) } do
182
- [:or, lft, rgt]
183
- end
184
-
185
- rule and: { lft: subtree(:lft), rgt: subtree(:rgt) } do
186
- [:and, lft, rgt]
187
- end
188
-
189
- rule cmp: { op: simple(:op), lft: subtree(:lft), rgt: subtree(:rgt) } do
190
- [OP[op.to_s], lhs.(lft), str.(rgt)]
191
- end
192
-
193
- rule incl: { lft: subtree(:lft), rgt: subtree(:rgt) } do
194
- [:in, lhs.(lft), list.(rgt)]
195
- end
196
-
197
- rule is: { lft: subtree(:lft), rgt: subtree(:rgt) } do
198
- [:is, lhs.(lft), sym.(rgt)]
199
- end
200
- end
201
- end
202
- end