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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +186 -0
- data/.ruby-version +1 -0
- data/CONTRIBUTING.md +85 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +105 -0
- data/LICENSE.txt +22 -0
- data/README.md +275 -0
- data/Rakefile +10 -0
- data/bin/poe-css +14 -0
- data/demo/Gemfile +11 -0
- data/demo/Gemfile.lock +45 -0
- data/demo/app.rb +21 -0
- data/demo/views/index.haml +105 -0
- data/lib/dependencies.rb +11 -0
- data/lib/poe-css.rb +253 -0
- data/lib/poe-css/generator.rb +108 -0
- data/lib/poe-css/parser.rb +177 -0
- data/lib/poe-css/preprocessor.rb +186 -0
- data/lib/poe-css/simplifier.rb +201 -0
- data/lib/poe-css/version.rb +5 -0
- data/poe-css.gemspec +32 -0
- data/tests/poecss/all-clauses-1.output +23 -0
- data/tests/poecss/all-clauses-1.poecss +24 -0
- data/tests/poecss/alternation-1.output +8 -0
- data/tests/poecss/alternation-1.poecss +4 -0
- data/tests/poecss/nesting-1.output +8 -0
- data/tests/poecss/nesting-1.poecss +8 -0
- data/tests/poecss/nesting-2.output +30 -0
- data/tests/poecss/nesting-2.poecss +23 -0
- data/tests/poecss/nesting-3.output +15 -0
- data/tests/poecss/nesting-3.poecss +15 -0
- data/tests/poecss/nesting-4.output +14 -0
- data/tests/poecss/nesting-4.poecss +13 -0
- data/tests/poecss/performance-1.output +3 -0
- data/tests/poecss/performance-1.poecss +59 -0
- data/tests/poecss/performance-2.output +60 -0
- data/tests/poecss/performance-2.poecss +75 -0
- data/tests/poecss/rule-ordering-1.output +25 -0
- data/tests/poecss/rule-ordering-1.poecss +30 -0
- data/tests/poecss/rule-ordering-2.output +3 -0
- data/tests/poecss/rule-ordering-2.poecss +29 -0
- data/tests/poecss/rule-ordering-3.output +30 -0
- data/tests/poecss/rule-ordering-3.poecss +29 -0
- data/tests/poecss/rule-ordering-4.output +20 -0
- data/tests/poecss/rule-ordering-4.poecss +30 -0
- data/tests/poecss/rule-ordering-5.output +8 -0
- data/tests/poecss/rule-ordering-5.poecss +9 -0
- data/tests/poecss/rule-ordering-6.output +7 -0
- data/tests/poecss/rule-ordering-6.poecss +14 -0
- data/tests/poecss/simple-1.output +7 -0
- data/tests/poecss/simple-1.poecss +8 -0
- data/tests/poecss/simplification-1.output +6 -0
- data/tests/poecss/simplification-1.poecss +7 -0
- data/tests/poecss/simplification-2.output +6 -0
- data/tests/poecss/simplification-2.poecss +8 -0
- data/tests/poecss/simplification-3.output +2 -0
- data/tests/poecss/simplification-3.poecss +3 -0
- data/tests/poecss/simplification-4.output +3 -0
- data/tests/poecss/simplification-4.poecss +4 -0
- data/tests/poecss/simplification-5.output +3 -0
- data/tests/poecss/simplification-5.poecss +8 -0
- data/tests/poecss/simplification-6.output +2 -0
- data/tests/poecss/simplification-6.poecss +4 -0
- data/tests/preprocessor/arity-mismatch-1.error.preprocessor +4 -0
- data/tests/preprocessor/arity-mismatch-2.error.preprocessor +4 -0
- data/tests/preprocessor/complex.output +20 -0
- data/tests/preprocessor/complex.preprocessor +19 -0
- data/tests/preprocessor/empty.output +0 -0
- data/tests/preprocessor/empty.preprocessor +0 -0
- data/tests/preprocessor/macro-newlines.output +5 -0
- data/tests/preprocessor/macro-newlines.preprocessor +9 -0
- data/tests/preprocessor/macro.output +3 -0
- data/tests/preprocessor/macro.preprocessor +7 -0
- data/tests/preprocessor/missing-identifier-1.error.preprocessor +1 -0
- data/tests/preprocessor/missing-identifier-2.error.preprocessor +1 -0
- data/tests/preprocessor/normal-newlines.output +2 -0
- data/tests/preprocessor/normal-newlines.preprocessor +2 -0
- data/tests/preprocessor/parse-error-1.error.preprocessor +1 -0
- data/tests/preprocessor/parse-error-2.error.preprocessor +1 -0
- data/tests/preprocessor/parse-error-3.error.preprocessor +4 -0
- data/tests/preprocessor/simple.output +1 -0
- data/tests/preprocessor/simple.preprocessor +3 -0
- data/tests/run-all-tests.rb +60 -0
- data/tests/unit/simplify-bounds-test.rb +57 -0
- data/tests/unit/stricter-query-test.rb +24 -0
- 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
|
data/poe-css.gemspec
ADDED
@@ -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,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
|
+
|