poe-css 0.0.1

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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +186 -0
  4. data/.ruby-version +1 -0
  5. data/CONTRIBUTING.md +85 -0
  6. data/Gemfile +5 -0
  7. data/Gemfile.lock +105 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +275 -0
  10. data/Rakefile +10 -0
  11. data/bin/poe-css +14 -0
  12. data/demo/Gemfile +11 -0
  13. data/demo/Gemfile.lock +45 -0
  14. data/demo/app.rb +21 -0
  15. data/demo/views/index.haml +105 -0
  16. data/lib/dependencies.rb +11 -0
  17. data/lib/poe-css.rb +253 -0
  18. data/lib/poe-css/generator.rb +108 -0
  19. data/lib/poe-css/parser.rb +177 -0
  20. data/lib/poe-css/preprocessor.rb +186 -0
  21. data/lib/poe-css/simplifier.rb +201 -0
  22. data/lib/poe-css/version.rb +5 -0
  23. data/poe-css.gemspec +32 -0
  24. data/tests/poecss/all-clauses-1.output +23 -0
  25. data/tests/poecss/all-clauses-1.poecss +24 -0
  26. data/tests/poecss/alternation-1.output +8 -0
  27. data/tests/poecss/alternation-1.poecss +4 -0
  28. data/tests/poecss/nesting-1.output +8 -0
  29. data/tests/poecss/nesting-1.poecss +8 -0
  30. data/tests/poecss/nesting-2.output +30 -0
  31. data/tests/poecss/nesting-2.poecss +23 -0
  32. data/tests/poecss/nesting-3.output +15 -0
  33. data/tests/poecss/nesting-3.poecss +15 -0
  34. data/tests/poecss/nesting-4.output +14 -0
  35. data/tests/poecss/nesting-4.poecss +13 -0
  36. data/tests/poecss/performance-1.output +3 -0
  37. data/tests/poecss/performance-1.poecss +59 -0
  38. data/tests/poecss/performance-2.output +60 -0
  39. data/tests/poecss/performance-2.poecss +75 -0
  40. data/tests/poecss/rule-ordering-1.output +25 -0
  41. data/tests/poecss/rule-ordering-1.poecss +30 -0
  42. data/tests/poecss/rule-ordering-2.output +3 -0
  43. data/tests/poecss/rule-ordering-2.poecss +29 -0
  44. data/tests/poecss/rule-ordering-3.output +30 -0
  45. data/tests/poecss/rule-ordering-3.poecss +29 -0
  46. data/tests/poecss/rule-ordering-4.output +20 -0
  47. data/tests/poecss/rule-ordering-4.poecss +30 -0
  48. data/tests/poecss/rule-ordering-5.output +8 -0
  49. data/tests/poecss/rule-ordering-5.poecss +9 -0
  50. data/tests/poecss/rule-ordering-6.output +7 -0
  51. data/tests/poecss/rule-ordering-6.poecss +14 -0
  52. data/tests/poecss/simple-1.output +7 -0
  53. data/tests/poecss/simple-1.poecss +8 -0
  54. data/tests/poecss/simplification-1.output +6 -0
  55. data/tests/poecss/simplification-1.poecss +7 -0
  56. data/tests/poecss/simplification-2.output +6 -0
  57. data/tests/poecss/simplification-2.poecss +8 -0
  58. data/tests/poecss/simplification-3.output +2 -0
  59. data/tests/poecss/simplification-3.poecss +3 -0
  60. data/tests/poecss/simplification-4.output +3 -0
  61. data/tests/poecss/simplification-4.poecss +4 -0
  62. data/tests/poecss/simplification-5.output +3 -0
  63. data/tests/poecss/simplification-5.poecss +8 -0
  64. data/tests/poecss/simplification-6.output +2 -0
  65. data/tests/poecss/simplification-6.poecss +4 -0
  66. data/tests/preprocessor/arity-mismatch-1.error.preprocessor +4 -0
  67. data/tests/preprocessor/arity-mismatch-2.error.preprocessor +4 -0
  68. data/tests/preprocessor/complex.output +20 -0
  69. data/tests/preprocessor/complex.preprocessor +19 -0
  70. data/tests/preprocessor/empty.output +0 -0
  71. data/tests/preprocessor/empty.preprocessor +0 -0
  72. data/tests/preprocessor/macro-newlines.output +5 -0
  73. data/tests/preprocessor/macro-newlines.preprocessor +9 -0
  74. data/tests/preprocessor/macro.output +3 -0
  75. data/tests/preprocessor/macro.preprocessor +7 -0
  76. data/tests/preprocessor/missing-identifier-1.error.preprocessor +1 -0
  77. data/tests/preprocessor/missing-identifier-2.error.preprocessor +1 -0
  78. data/tests/preprocessor/normal-newlines.output +2 -0
  79. data/tests/preprocessor/normal-newlines.preprocessor +2 -0
  80. data/tests/preprocessor/parse-error-1.error.preprocessor +1 -0
  81. data/tests/preprocessor/parse-error-2.error.preprocessor +1 -0
  82. data/tests/preprocessor/parse-error-3.error.preprocessor +4 -0
  83. data/tests/preprocessor/simple.output +1 -0
  84. data/tests/preprocessor/simple.preprocessor +3 -0
  85. data/tests/run-all-tests.rb +60 -0
  86. data/tests/unit/simplify-bounds-test.rb +57 -0
  87. data/tests/unit/stricter-query-test.rb +24 -0
  88. metadata +292 -0
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module POECSS
4
+ module Simplifier
5
+ RARITY_TO_NUMBER = { 'Normal' => 0, 'Magic' => 1, 'Rare' => 2, 'Unique' => 3 }
6
+ NUMBER_TO_RARITY = RARITY_TO_NUMBER.invert
7
+
8
+ class << self
9
+ # Removes impossible matches, duplicate matches, and duplicate commands.
10
+ def simplify_clause(clause)
11
+ simplified_matches = clause.match_clauses.group_by(&:match_key).flat_map { |match_key, matches|
12
+ case match_key
13
+ when 'itemlevel', 'droplevel', 'quality', 'height', 'width', 'sockets', 'linkedsockets'
14
+ simplify_numeric_matches(match_key, matches)
15
+ when 'rarity'
16
+ simple_bounds = simplify_bounds(
17
+ matches.map { |m| [ m.match_arguments[:operator] || '=', RARITY_TO_NUMBER[m.match_arguments[:rarity]] ] }
18
+ )
19
+
20
+ simple_bounds&.map { |op, limit| MatchClause.new(match_key, { operator: op, rarity: NUMBER_TO_RARITY[limit] }) }
21
+ when 'basetype', 'class', 'socketgroup'
22
+ key =
23
+ case match_key
24
+ when 'basetype', 'class'
25
+ :substrings
26
+ when 'socketgroup'
27
+ :sub_socket_groups
28
+ else
29
+ raise ArgumentError, key
30
+ end
31
+
32
+ # Matches a QUERY if for all match in matches, match.substrings.any? { |substring| QUERY[substring] }.
33
+ matches_with_simplified_substrings = matches.uniq.map { |match|
34
+ # We're in an OR-ing context now, so get rid of any substring
35
+ # which is already subsumed by a different substring. If A is a
36
+ # substring of B, then if QUERY[B] is true, then QUERY[A] must
37
+ # also be true, so B can be eliminated from QUERY[A] || QUERY[B].
38
+ queries = []
39
+ match.match_arguments[key].sort_by(&:length).each do |s|
40
+ if queries.none? { |q| s[q] }
41
+ queries << s
42
+ end
43
+ end
44
+
45
+ MatchClause.new(match_key, { key => queries })
46
+ }
47
+
48
+ # Now look for cases where a match can be subsumed by another
49
+ # match. If match A's queries are a subset of match B's, then
50
+ # match B is more restrictive than match A and match A can be
51
+ # removed.
52
+
53
+ matches = []
54
+ matches_with_simplified_substrings.sort_by { |m| m.match_arguments[key].length }.each do |s|
55
+ if matches.none? { |q| stricter_query?(q.match_arguments[key], s.match_arguments[key]) }
56
+ matches << s
57
+ end
58
+ end
59
+
60
+ matches
61
+ when 'corrupted', 'elderitem', 'identified', 'shapedmap', 'shaperitem'
62
+ key =
63
+ case match_key
64
+ when 'corrupted'
65
+ :corrupted
66
+ when 'elderitem'
67
+ :elder_item
68
+ when 'identified'
69
+ :identified
70
+ when 'shapedmap'
71
+ :shaped_map
72
+ when 'shaperitem'
73
+ :shaper_item
74
+ else
75
+ raise ArgumentError, key
76
+ end
77
+
78
+ values = matches.map { |m| m.match_arguments[key] }.uniq
79
+
80
+ if values.length == 1
81
+ MatchClause.new(match_key, { key => values.first })
82
+ end
83
+ else
84
+ raise ArgumentError, match_key
85
+ end
86
+ }
87
+
88
+ # Some match reported that the match was impossible.
89
+ return nil if simplified_matches.any?(&:nil?)
90
+
91
+ # Only the last command of a particular key is used.
92
+ simplified_commands = clause.command_clauses
93
+ .group_by { |c|
94
+ case c.command_key
95
+ when 'show', 'hide' # Show and Hide need to be grouped together.
96
+ 'showhide'
97
+ else
98
+ c.command_key
99
+ end
100
+ }
101
+ .map { |_, cs| cs.last }
102
+
103
+ SimpleClause.new(simplified_matches, simplified_commands)
104
+ end
105
+
106
+ private
107
+
108
+ # A and B are lists of strings intended to be used in substring tests.
109
+ # QUERY(string, query) = query.any? { |q| string[q] }
110
+ # Returns true if for all strings to QUERY(string, A) that return true, QUERY(string, B) also returns true.
111
+ def stricter_query?(a_substrings, b_substrings)
112
+ a_substrings.all? { |a_query| b_substrings.any? { |b_query| a_query[b_query] } }
113
+ end
114
+
115
+ def simplify_numeric_matches(match_key, matches)
116
+ key =
117
+ case match_key
118
+ when 'itemlevel'
119
+ :level
120
+ when 'droplevel'
121
+ :level
122
+ when 'quality'
123
+ :quality
124
+ when 'rarity'
125
+ :rarity
126
+ when 'height'
127
+ :height
128
+ when 'sockets'
129
+ :sockets
130
+ when 'linkedsockets'
131
+ :sockets
132
+ when 'width'
133
+ :width
134
+ else
135
+ raise ArgumentError, match_key
136
+ end
137
+ simple_bounds = simplify_bounds(matches.map { |m| [ m.match_arguments[:operator] || '=', m.match_arguments[key] ] })
138
+
139
+ simple_bounds&.map { |op, limit| MatchClause.new(match_key, { operator: op, key => limit }) }
140
+ end
141
+
142
+ def simplify_bounds(bounds)
143
+ lower = -Float::INFINITY
144
+ lower_inclusive = nil
145
+ upper = Float::INFINITY
146
+ upper_inclusive = nil
147
+
148
+ bounds.each do |op, limit|
149
+ case op
150
+ when '>='
151
+ if limit > lower
152
+ lower = limit
153
+ lower_inclusive = true
154
+ end
155
+ when '>'
156
+ if limit >= lower
157
+ lower = limit
158
+ lower_inclusive = false
159
+ end
160
+ when '='
161
+ if limit > lower
162
+ lower = limit
163
+ lower_inclusive = true
164
+ end
165
+ if limit < upper
166
+ upper = limit
167
+ upper_inclusive = true
168
+ end
169
+ when '<'
170
+ if limit <= upper
171
+ upper = limit
172
+ upper_inclusive = false
173
+ end
174
+ when '<='
175
+ if limit < upper
176
+ upper = limit
177
+ upper_inclusive = true
178
+ end
179
+ else
180
+ raise ArgumentError, op
181
+ end
182
+ end
183
+
184
+ if lower == upper && upper_inclusive && lower_inclusive
185
+ [
186
+ [ '=', lower ]
187
+ ]
188
+ elsif lower < upper
189
+ [
190
+ if lower != -Float::INFINITY
191
+ [ lower_inclusive ? '>=' : '>', lower ]
192
+ end,
193
+ if upper != Float::INFINITY
194
+ [ upper_inclusive ? '<=' : '<', upper ]
195
+ end
196
+ ].compact
197
+ end
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module POECSS
4
+ VERSION = '0.0.1'
5
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+
5
+ require 'poe-css/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'poe-css'
9
+ s.version = POECSS::VERSION
10
+ s.authors = [ 'Alex Quach' ]
11
+ s.email = [ 'alexhquach@gmail.com' ]
12
+ s.homepage = ''
13
+ s.summary = 'A better way to write item filters for Path of Exile.'
14
+ s.description = 'A better way to write item filters for Path of Exile.'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+
18
+ s.test_files = `git ls-files -- tests/*`.split("\n")
19
+
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
21
+
22
+ s.require_paths = [ 'lib' ]
23
+
24
+ s.add_runtime_dependency 'parslet'
25
+
26
+ s.add_development_dependency 'bundler', '~> 1.14'
27
+ s.add_development_dependency 'irbtools'
28
+ s.add_development_dependency 'minitest'
29
+ s.add_development_dependency 'minitest-reporters'
30
+ s.add_development_dependency 'rake', '~> 10.0'
31
+ s.add_development_dependency 'rubocop'
32
+ end
@@ -0,0 +1,23 @@
1
+ Show
2
+ BaseType "b"
3
+ Class "asdf" "in quotes" "other" "thing"
4
+ Corrupted True
5
+ DropLevel > 25
6
+ ElderItem False
7
+ Height > 1
8
+ Identified True
9
+ ItemLevel >= 25
10
+ LinkedSockets <= 5
11
+ Quality = 20
12
+ Rarity = Unique
13
+ ShapedMap True
14
+ ShaperItem False
15
+ SocketGroup "RR" "B" "G" "W"
16
+ Sockets < 2
17
+ Width = 5
18
+ PlayAlertSound 1
19
+ PlayAlertSoundPositional 1 300
20
+ SetBackgroundColor 255 2 3 255
21
+ SetBorderColor 255 2 2 255
22
+ SetFontSize 13
23
+ SetTextColor 255 2 3 2
@@ -0,0 +1,24 @@
1
+ ItemLevel >= 25,
2
+ DropLevel > 25,
3
+ Quality = 20,
4
+ Rarity Unique,
5
+ Class "asdf" other thing "in quotes",
6
+ BaseType "b",
7
+ Sockets < 2,
8
+ LinkedSockets <= 5,
9
+ SocketGroup RR GG B W rr g b w,
10
+ Height > 1,
11
+ Width 5,
12
+ Identified,
13
+ Corrupted True,
14
+ ElderItem False,
15
+ ShapedMap True,
16
+ ShaperItem False {
17
+ Show
18
+ SetBorderColor rgb(255, 2, 2)
19
+ SetTextColor rgb(255, 2, 3, 2)
20
+ SetBackgroundColor rgba(255, 2, 3)
21
+ PlayAlertSound 1
22
+ PlayAlertSoundPositional 1 300
23
+ SetFontSize 13
24
+ }
@@ -0,0 +1,8 @@
1
+ Show
2
+ Rarity = Unique
3
+ SetFontSize 15
4
+
5
+ Show
6
+ ItemLevel >= 25
7
+ SetFontSize 15
8
+
@@ -0,0 +1,4 @@
1
+ ItemLevel >= 25 | Rarity Unique {
2
+ Show
3
+ SetFontSize 15
4
+ }
@@ -0,0 +1,8 @@
1
+ Show
2
+ ItemLevel >= 25
3
+ Rarity = Unique
4
+ SetFontSize 15
5
+
6
+ Show
7
+ ItemLevel >= 25
8
+ SetFontSize 13
@@ -0,0 +1,8 @@
1
+ ItemLevel >= 25 {
2
+ Show
3
+ SetFontSize 13
4
+
5
+ Rarity Unique {
6
+ SetFontSize 15
7
+ }
8
+ }
@@ -0,0 +1,30 @@
1
+ Show
2
+ ItemLevel >= 25
3
+ Quality < 5
4
+ Rarity = Magic
5
+ PlayAlertSound 13
6
+ SetFontSize 17
7
+
8
+ Show
9
+ ItemLevel >= 25
10
+ Rarity = Magic
11
+ PlayAlertSound 13
12
+ SetFontSize 16
13
+
14
+ Show
15
+ ItemLevel >= 25
16
+ Quality < 5
17
+ Rarity = Unique
18
+ PlayAlertSound 13
19
+ SetFontSize 15
20
+
21
+ Show
22
+ ItemLevel >= 25
23
+ Rarity = Unique
24
+ PlayAlertSound 13
25
+ SetFontSize 14
26
+
27
+ Show
28
+ ItemLevel >= 25
29
+ PlayAlertSound 13
30
+ SetFontSize 13
@@ -0,0 +1,23 @@
1
+ ItemLevel >= 25 {
2
+ Show
3
+ SetFontSize 13
4
+
5
+ Rarity Unique {
6
+ SetFontSize 14
7
+
8
+ Quality < 5 {
9
+ SetFontSize 15
10
+ }
11
+ }
12
+
13
+ Rarity Magic {
14
+ SetFontSize 16
15
+
16
+ Quality < 5 {
17
+ SetFontSize 17
18
+ }
19
+ }
20
+
21
+ PlayAlertSound 13
22
+ }
23
+
@@ -0,0 +1,15 @@
1
+ Show
2
+ ItemLevel >= 25
3
+ Quality >= 20
4
+ Rarity = Unique
5
+ SetFontSize 14
6
+
7
+ Show
8
+ ItemLevel >= 25
9
+ Quality >= 19
10
+ Rarity = Unique
11
+ SetFontSize 13
12
+
13
+ Show
14
+ ItemLevel >= 25
15
+
@@ -0,0 +1,15 @@
1
+ ItemLevel >= 25 {
2
+ Show
3
+
4
+ Rarity Unique {
5
+ Quality >= 19 {
6
+ SetFontSize 13
7
+ }
8
+ }
9
+
10
+ Rarity Unique {
11
+ Quality >= 20 {
12
+ SetFontSize 14
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,14 @@
1
+ Show
2
+ ItemLevel >= 25
3
+ Quality >= 20
4
+ Rarity = Unique
5
+ SetFontSize 14
6
+
7
+ Show
8
+ ItemLevel >= 25
9
+ Quality >= 19
10
+ Rarity = Unique
11
+ SetFontSize 13
12
+
13
+ Show
14
+ ItemLevel >= 25
@@ -0,0 +1,13 @@
1
+ ItemLevel >= 25 {
2
+ Show
3
+
4
+ Rarity Unique {
5
+ Quality >= 19 {
6
+ SetFontSize 13
7
+ }
8
+
9
+ Quality >= 20 {
10
+ SetFontSize 14
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ Show
2
+ ItemLevel >= 25
3
+