travis-conditions 0.0.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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