less 0.8.13 → 1.0.4
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.
- data/README.md +10 -1
- data/Rakefile +21 -3
- data/VERSION +1 -1
- data/bin/lessc +0 -8
- data/less.gemspec +138 -15
- data/lib/less.rb +67 -11
- data/lib/less/command.rb +24 -25
- data/lib/less/engine.rb +36 -140
- data/lib/less/engine/builder.rb +8 -0
- data/lib/less/engine/less.tt +374 -0
- data/lib/less/engine/nodes.rb +8 -0
- data/lib/less/engine/nodes/element.rb +150 -0
- data/lib/less/engine/nodes/entity.rb +73 -0
- data/lib/less/engine/nodes/function.rb +82 -0
- data/lib/less/engine/nodes/literal.rb +135 -0
- data/lib/less/engine/nodes/property.rb +112 -0
- data/lib/less/engine/nodes/selector.rb +39 -0
- data/lib/less/engine/parser.rb +3860 -0
- data/lib/vendor/treetop/.gitignore +7 -0
- data/lib/vendor/treetop/LICENSE +19 -0
- data/lib/vendor/treetop/README +164 -0
- data/lib/vendor/treetop/Rakefile +19 -0
- data/lib/vendor/treetop/benchmark/seqpar.gnuplot +15 -0
- data/lib/vendor/treetop/benchmark/seqpar.treetop +16 -0
- data/lib/vendor/treetop/benchmark/seqpar_benchmark.rb +107 -0
- data/lib/vendor/treetop/bin/tt +28 -0
- data/lib/vendor/treetop/lib/treetop.rb +8 -0
- data/lib/vendor/treetop/lib/treetop/bootstrap_gen_1_metagrammar.rb +45 -0
- data/lib/vendor/treetop/lib/treetop/compiler.rb +6 -0
- data/lib/vendor/treetop/lib/treetop/compiler/grammar_compiler.rb +42 -0
- data/lib/vendor/treetop/lib/treetop/compiler/lexical_address_space.rb +17 -0
- data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.rb +3097 -0
- data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.treetop +408 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes.rb +19 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/anything_symbol.rb +18 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/atomic_expression.rb +14 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/character_class.rb +23 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/choice.rb +31 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/grammar.rb +28 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/nonterminal.rb +13 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/optional.rb +19 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_expression.rb +146 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/predicate.rb +45 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/repetition.rb +55 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/sequence.rb +68 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/terminal.rb +20 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
- data/lib/vendor/treetop/lib/treetop/compiler/ruby_builder.rb +113 -0
- data/lib/vendor/treetop/lib/treetop/ruby_extensions.rb +2 -0
- data/lib/vendor/treetop/lib/treetop/ruby_extensions/string.rb +42 -0
- data/lib/vendor/treetop/lib/treetop/runtime.rb +5 -0
- data/lib/vendor/treetop/lib/treetop/runtime/compiled_parser.rb +109 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list.rb +4 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
- data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
- data/lib/vendor/treetop/lib/treetop/runtime/syntax_node.rb +90 -0
- data/lib/vendor/treetop/lib/treetop/runtime/terminal_parse_failure.rb +16 -0
- data/lib/vendor/treetop/lib/treetop/runtime/terminal_syntax_node.rb +17 -0
- data/lib/vendor/treetop/lib/treetop/version.rb +9 -0
- data/lib/vendor/treetop/spec/compiler/and_predicate_spec.rb +36 -0
- data/lib/vendor/treetop/spec/compiler/anything_symbol_spec.rb +44 -0
- data/lib/vendor/treetop/spec/compiler/character_class_spec.rb +247 -0
- data/lib/vendor/treetop/spec/compiler/choice_spec.rb +80 -0
- data/lib/vendor/treetop/spec/compiler/circular_compilation_spec.rb +28 -0
- data/lib/vendor/treetop/spec/compiler/failure_propagation_functional_spec.rb +21 -0
- data/lib/vendor/treetop/spec/compiler/grammar_compiler_spec.rb +84 -0
- data/lib/vendor/treetop/spec/compiler/grammar_spec.rb +41 -0
- data/lib/vendor/treetop/spec/compiler/nonterminal_symbol_spec.rb +40 -0
- data/lib/vendor/treetop/spec/compiler/not_predicate_spec.rb +38 -0
- data/lib/vendor/treetop/spec/compiler/one_or_more_spec.rb +35 -0
- data/lib/vendor/treetop/spec/compiler/optional_spec.rb +37 -0
- data/lib/vendor/treetop/spec/compiler/parenthesized_expression_spec.rb +19 -0
- data/lib/vendor/treetop/spec/compiler/parsing_rule_spec.rb +32 -0
- data/lib/vendor/treetop/spec/compiler/sequence_spec.rb +115 -0
- data/lib/vendor/treetop/spec/compiler/terminal_spec.rb +81 -0
- data/lib/vendor/treetop/spec/compiler/terminal_symbol_spec.rb +37 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar.treetop +7 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar.tt +7 -0
- data/lib/vendor/treetop/spec/compiler/test_grammar_do.treetop +7 -0
- data/lib/vendor/treetop/spec/compiler/zero_or_more_spec.rb +56 -0
- data/lib/vendor/treetop/spec/composition/a.treetop +11 -0
- data/lib/vendor/treetop/spec/composition/b.treetop +11 -0
- data/lib/vendor/treetop/spec/composition/c.treetop +10 -0
- data/lib/vendor/treetop/spec/composition/d.treetop +10 -0
- data/lib/vendor/treetop/spec/composition/f.treetop +17 -0
- data/lib/vendor/treetop/spec/composition/grammar_composition_spec.rb +40 -0
- data/lib/vendor/treetop/spec/composition/subfolder/e_includes_c.treetop +15 -0
- data/lib/vendor/treetop/spec/ruby_extensions/string_spec.rb +32 -0
- data/lib/vendor/treetop/spec/runtime/compiled_parser_spec.rb +101 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +164 -0
- data/lib/vendor/treetop/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
- data/lib/vendor/treetop/spec/runtime/syntax_node_spec.rb +68 -0
- data/lib/vendor/treetop/spec/spec_helper.rb +106 -0
- data/lib/vendor/treetop/spec/spec_suite.rb +4 -0
- data/lib/vendor/treetop/treetop.gemspec +17 -0
- data/spec/command_spec.rb +2 -6
- data/spec/css/accessors-1.0.css +18 -0
- data/spec/css/big-1.0.css +3768 -0
- data/spec/css/comments-1.0.css +9 -0
- data/spec/css/css-1.0.css +40 -0
- data/spec/css/functions-1.0.css +6 -0
- data/spec/css/import-1.0.css +11 -0
- data/spec/css/mixins-1.0.css +28 -0
- data/spec/css/operations-1.0.css +28 -0
- data/spec/css/rulesets-1.0.css +17 -0
- data/spec/css/scope-1.0.css +14 -0
- data/spec/css/strings-1.0.css +12 -0
- data/spec/css/variables-1.0.css +6 -0
- data/spec/css/whitespace-1.0.css +9 -0
- data/spec/engine_spec.rb +66 -18
- data/spec/less/accessors-1.0.less +20 -0
- data/spec/less/big-1.0.less +4810 -0
- data/spec/less/colors-1.0.less +0 -0
- data/spec/less/comments-1.0.less +46 -0
- data/spec/less/css-1.0.less +84 -0
- data/spec/less/exceptions/mixed-units-error.less +3 -0
- data/spec/less/exceptions/name-error-1.0.less +3 -0
- data/spec/less/exceptions/syntax-error-1.0.less +3 -0
- data/spec/less/functions-1.0.less +6 -0
- data/spec/less/import-1.0.less +7 -0
- data/spec/less/import/import-test-a.less +2 -0
- data/spec/less/import/import-test-b.less +8 -0
- data/spec/less/import/import-test-c.less +5 -0
- data/spec/less/mixins-1.0.less +43 -0
- data/spec/less/operations-1.0.less +39 -0
- data/spec/less/rulesets-1.0.less +30 -0
- data/spec/less/scope-1.0.less +33 -0
- data/spec/less/strings-1.0.less +14 -0
- data/spec/less/variables-1.0.less +18 -0
- data/spec/less/whitespace-1.0.less +21 -0
- data/spec/spec.css +79 -24
- data/spec/spec.less +2 -3
- data/spec/spec_helper.rb +4 -1
- metadata +136 -13
- data/lib/less/tree.rb +0 -82
- data/spec/css/less-0.8.10.css +0 -30
- data/spec/css/less-0.8.11.css +0 -31
- data/spec/css/less-0.8.12.css +0 -28
- data/spec/css/less-0.8.5.css +0 -24
- data/spec/css/less-0.8.6.css +0 -24
- data/spec/css/less-0.8.7.css +0 -24
- data/spec/css/less-0.8.8.css +0 -25
- data/spec/tree_spec.rb +0 -5
data/lib/less/engine.rb
CHANGED
|
@@ -1,154 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
class Engine < String
|
|
3
|
-
# Compound properties, such as `border: 1px solid black`
|
|
4
|
-
COMPOUND = {'font' => true, 'background' => false, 'border' => false }
|
|
5
|
-
REGEX = {
|
|
6
|
-
:path => /([#.][->#.\w ]+)?( ?> ?)?/, # #header > .title
|
|
7
|
-
:selector => /[-\w #.,>*+:\(\)\=\[\]']/, # .cow .milk > a
|
|
8
|
-
:variable => /@([-\w]+)/, # @milk-white
|
|
9
|
-
:property => /@[-\w]+|[-a-z*0-9_]+/, # font-size
|
|
10
|
-
:color => /#([a-zA-Z0-9]{3,6})\b/, # #f0f0f0
|
|
11
|
-
:number => /\d+(?>\.\d+)?/, # 24.8
|
|
12
|
-
:unit => /px|em|pt|cm|mm|%/ # em
|
|
13
|
-
}
|
|
14
|
-
REGEX[:numeric] = /#{REGEX[:number]}(#{REGEX[:unit]})?/
|
|
15
|
-
REGEX[:operand] = /#{REGEX[:color]}|#{REGEX[:numeric]}/
|
|
16
|
-
|
|
17
|
-
def initialize s
|
|
18
|
-
super
|
|
19
|
-
@tree = Tree.new self.hashify
|
|
20
|
-
end
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
|
21
2
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# Parse the variables and mixins
|
|
25
|
-
#
|
|
26
|
-
# We use symbolic keys, such as :mixins, to store LESS-only data,
|
|
27
|
-
# each branch has its own :mixins => [], and :variables => {}
|
|
28
|
-
# Once a declaration has been recognised as LESS-specific, it is copied
|
|
29
|
-
# in the appropriate data structure in that branch. The declaration itself
|
|
30
|
-
# can then be deleted.
|
|
31
|
-
#
|
|
32
|
-
@tree = @tree.traverse :leaf do |key, value, path, node|
|
|
33
|
-
matched = if match = key.match( REGEX[:variable] )
|
|
34
|
-
node[:variables] ||= Tree.new
|
|
35
|
-
node[:variables][ match.captures.first ] = value
|
|
36
|
-
elsif value == :mixin
|
|
37
|
-
node[:mixins] ||= []
|
|
38
|
-
node[:mixins] << key
|
|
39
|
-
end
|
|
40
|
-
node.delete key if matched # Delete the property if it's LESS-specific
|
|
41
|
-
end
|
|
3
|
+
require 'engine/builder'
|
|
4
|
+
require 'engine/nodes'
|
|
42
5
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
node[:mixins].each do |m|
|
|
49
|
-
@tree.find( :mixin, m.delete(' ').split('>') ).each {|k, v| node[ k ] = v }
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Call `evaluate` on variables, such as '@dark: @light / 2'
|
|
55
|
-
@tree = @tree.traverse :branch do |path, node|
|
|
56
|
-
node.vars.each do |key, value|
|
|
57
|
-
evaluate key, value, path, node.vars
|
|
58
|
-
end if node.vars?
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Call `evaluate` on css properties, such as 'font-size: @big'
|
|
62
|
-
@tree = @tree.traverse :leaf do |key, value, path, node|
|
|
63
|
-
evaluate key, value, path, node
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
#
|
|
67
|
-
# Evaluate operations (2+2)
|
|
68
|
-
#
|
|
69
|
-
# Units are: 1px, 1em, 1%, #111
|
|
70
|
-
@tree = @tree.traverse :leaf do |key, value, path, node|
|
|
71
|
-
node[ key ] = value.gsub /(#{REGEX[:operand]}(\s?)[-+\/*](\4))+(#{REGEX[:operand]})/ do |operation|
|
|
72
|
-
# Disallow operations on certain compound declarations
|
|
73
|
-
if COMPOUND[ key ]
|
|
74
|
-
next value
|
|
75
|
-
else
|
|
76
|
-
raise CompoundOperationError, "#{key}: #{value}"
|
|
77
|
-
end if COMPOUND.include? key
|
|
6
|
+
begin
|
|
7
|
+
require 'engine/parser'
|
|
8
|
+
rescue LoadError
|
|
9
|
+
Treetop.load LESS_GRAMMAR
|
|
10
|
+
end
|
|
78
11
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
evaluate = lambda {|v| eval( v ).to_s + unit }
|
|
92
|
-
operation.gsub REGEX[:unit], ''
|
|
93
|
-
end.to_s
|
|
94
|
-
next if operation.match /[a-z]/i
|
|
95
|
-
evaluate.call operation
|
|
96
|
-
else
|
|
97
|
-
raise MixedUnitsError, value
|
|
98
|
-
end
|
|
99
|
-
end
|
|
12
|
+
module Less
|
|
13
|
+
class Engine
|
|
14
|
+
attr_reader :css, :less
|
|
15
|
+
|
|
16
|
+
def initialize obj
|
|
17
|
+
@less = if obj.is_a? File
|
|
18
|
+
@path = File.dirname(File.expand_path obj.path)
|
|
19
|
+
obj.read
|
|
20
|
+
elsif obj.is_a? String
|
|
21
|
+
obj.dup
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError, "argument must be an instance of File or String!"
|
|
100
24
|
end
|
|
25
|
+
|
|
26
|
+
@parser = LessParser.new
|
|
101
27
|
end
|
|
102
|
-
alias render compile
|
|
103
28
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
var = var.join.delete ' '
|
|
112
|
-
|
|
113
|
-
value = if var.include? '>'
|
|
114
|
-
@tree.find :var, var.split('>') # Try finding it in a specific namespace
|
|
115
|
-
else
|
|
116
|
-
node.var(var) or @tree.nearest var, path # Try local first, then nearest scope
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
if value
|
|
120
|
-
# Substitute variable with value
|
|
121
|
-
node[ key ] = node[ key ].gsub /#{REGEX[:path]}@#{name}/, value
|
|
122
|
-
else
|
|
123
|
-
node.delete key # Discard the declaration if the variable wasn't found
|
|
124
|
-
end
|
|
125
|
-
end
|
|
29
|
+
def parse env = Node::Element.new
|
|
30
|
+
root = @parser.parse(self.prepare)
|
|
31
|
+
|
|
32
|
+
if root
|
|
33
|
+
@tree = root.build env.tap {|e| e.file = @path }
|
|
34
|
+
else
|
|
35
|
+
raise SyntaxError, @parser.failure_message
|
|
126
36
|
end
|
|
37
|
+
|
|
38
|
+
@tree
|
|
127
39
|
end
|
|
40
|
+
alias :to_tree :parse
|
|
128
41
|
|
|
129
|
-
def to_css
|
|
130
|
-
self.
|
|
42
|
+
def to_css
|
|
43
|
+
@css || @css = self.parse.to_css
|
|
131
44
|
end
|
|
132
45
|
|
|
133
|
-
def
|
|
134
|
-
|
|
135
|
-
# Parse the LESS structure into a hash
|
|
136
|
-
#
|
|
137
|
-
###
|
|
138
|
-
# less: color: black;
|
|
139
|
-
# hashify: "color" => "black"
|
|
140
|
-
#
|
|
141
|
-
hash = self.gsub(/\r\n/, "\n"). # m$
|
|
142
|
-
gsub(/"/, "'"). # " to '
|
|
143
|
-
gsub(/'(.*?)'/) { "'" + CGI.escape( $1 ) + "'" }. # Escape string values
|
|
144
|
-
gsub(/\/\/.*\n/, ''). # Comments //
|
|
145
|
-
gsub(/\/\*.*?\*\//m, ''). # Comments /*
|
|
146
|
-
gsub(/\s+/, ' '). # Replace \t\n
|
|
147
|
-
gsub(/(#{REGEX[:property]}): *([^\{\}]+?) *(;|(?=\}))/,'"\1"=>"\2",'). # Rules
|
|
148
|
-
gsub(/\}/, "},"). # Closing }
|
|
149
|
-
gsub(/([, ]*)(#{REGEX[:selector]}+?)[ \n]*(?=\{)/, '\1"\2"=>'). # Selectors
|
|
150
|
-
gsub(/([.#][->\w .#]+);/, '"\\1" => :mixin,') # Mixins
|
|
151
|
-
eval "{#{ hash }}" # Return {hash}
|
|
46
|
+
def prepare
|
|
47
|
+
@less.gsub(/\r\n/, "\n").gsub(/\t/, ' ')
|
|
152
48
|
end
|
|
153
49
|
end
|
|
154
50
|
end
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
grammar Less
|
|
2
|
+
rule primary
|
|
3
|
+
(declaration / ruleset / import / comment)+ <Builder> / declaration* <Builder> / import* <Builder> / comment*
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
rule comment
|
|
7
|
+
ws '/*' (!'*/' . )* '*/' ws / ws '//' (!"\n" .)* "\n" ws
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# div, .class, body > p {...}
|
|
12
|
+
#
|
|
13
|
+
rule ruleset
|
|
14
|
+
selectors "{" ws primary ws "}" ws {
|
|
15
|
+
def build env
|
|
16
|
+
# Build the ruleset for each selector
|
|
17
|
+
selectors.build(env, :tree).each do |sel|
|
|
18
|
+
primary.build sel
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
} / ws selectors ';' ws {
|
|
22
|
+
def build env
|
|
23
|
+
selectors.build(env, :path).each do |path|
|
|
24
|
+
rules = path.inject(env.root) do |current, node|
|
|
25
|
+
current.descend(node.selector, node) or raise MixinNameError, path.join
|
|
26
|
+
end.rules
|
|
27
|
+
env.rules += rules
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
rule import
|
|
34
|
+
"@import" S url:(string / url) medias? s ';' ws {
|
|
35
|
+
def build env
|
|
36
|
+
path = File.join(env.root.file, url.value)
|
|
37
|
+
path += '.less' unless path =~ /\.less$/
|
|
38
|
+
if File.exist? path
|
|
39
|
+
imported = Less::Engine.new(File.new path).to_tree
|
|
40
|
+
env.rules += imported.rules
|
|
41
|
+
else
|
|
42
|
+
raise ImportError, path
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
}
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
rule url
|
|
49
|
+
'url(' path:(string / [-a-zA-Z0-9_%$/.&=:;#+?]+) ')' {
|
|
50
|
+
def build env = nil
|
|
51
|
+
Node::String.new(CGI.unescape path.text_value)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def value
|
|
55
|
+
build
|
|
56
|
+
end
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
rule medias
|
|
61
|
+
[-a-z]+ (s ',' s [a-z]+)*
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
rule selectors
|
|
65
|
+
ws selector tail:(s ',' ws selector)* ws {
|
|
66
|
+
def build env, method
|
|
67
|
+
all.map do |e|
|
|
68
|
+
e.send(method, env) if e.respond_to? method
|
|
69
|
+
end.compact
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def all
|
|
73
|
+
[selector] + tail.elements.map {|e| e.selector }
|
|
74
|
+
end
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
#
|
|
79
|
+
# div > p a {...}
|
|
80
|
+
#
|
|
81
|
+
rule selector
|
|
82
|
+
(s select element s)+ {
|
|
83
|
+
def tree env
|
|
84
|
+
elements.inject(env) do |node, e|
|
|
85
|
+
node << Node::Element.new(e.element.text_value, e.select.text_value)
|
|
86
|
+
node.last
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def path env
|
|
91
|
+
elements.map do |e|
|
|
92
|
+
Node::Element.new(e.element.text_value, e.select.text_value)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
#
|
|
99
|
+
# @my-var: 12px;
|
|
100
|
+
# height: 100%;
|
|
101
|
+
#
|
|
102
|
+
rule declaration
|
|
103
|
+
ws name:(ident / variable) s ':' s expression s (';'/ ws &'}') ws {
|
|
104
|
+
def build env
|
|
105
|
+
env << (name.text_value =~ /^@/ ? Node::Variable : Node::Property).new(name.text_value)
|
|
106
|
+
expression.build env
|
|
107
|
+
end
|
|
108
|
+
# Empty rule
|
|
109
|
+
} / ws ident s ':' s ';' ws
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
#
|
|
113
|
+
# An operation or compound value
|
|
114
|
+
#
|
|
115
|
+
rule expression
|
|
116
|
+
entity (operator / S) expression <Builder> / entity
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#
|
|
120
|
+
# Entity: Any whitespace delimited token
|
|
121
|
+
#
|
|
122
|
+
rule entity
|
|
123
|
+
function / fonts / keyword / accessor / variable / literal / important
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
rule fonts
|
|
127
|
+
font family:(s ',' s font)+ {
|
|
128
|
+
def build env
|
|
129
|
+
fonts = ([font] + family.elements.map {|f| f.font }).map do |font|
|
|
130
|
+
font.build env
|
|
131
|
+
end
|
|
132
|
+
env.identifiers.last << Node::FontFamily.new(fonts)
|
|
133
|
+
end
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
rule font
|
|
138
|
+
[a-zA-Z] [-a-zA-Z0-9]* {
|
|
139
|
+
def build env
|
|
140
|
+
Node::Keyword.new(text_value)
|
|
141
|
+
end
|
|
142
|
+
} / string {
|
|
143
|
+
def build env
|
|
144
|
+
Node::String.new(text_value)
|
|
145
|
+
end
|
|
146
|
+
}
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#
|
|
150
|
+
# An identifier
|
|
151
|
+
#
|
|
152
|
+
rule ident
|
|
153
|
+
'-'? [-a-z0-9_]+
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
rule variable
|
|
157
|
+
'@' [-a-zA-Z0-9_]+ {
|
|
158
|
+
def build env
|
|
159
|
+
env.identifiers.last << env.nearest(text_value)
|
|
160
|
+
end
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
#
|
|
165
|
+
# div / .class / #id / input[type="text"] / lang(fr)
|
|
166
|
+
#
|
|
167
|
+
rule element
|
|
168
|
+
(class_id / tag / ident) attribute* ('(' ident ')')? / '@media' / '@font-face'
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
rule class_id
|
|
172
|
+
tag? class+ / tag? id
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
#
|
|
176
|
+
# [type="text"]
|
|
177
|
+
#
|
|
178
|
+
rule attribute
|
|
179
|
+
'[' [a-z]+ ([|~]? '=')? (tag / string) ']'
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
rule class
|
|
183
|
+
'.' [_a-z] [-a-zA-Z0-9_]*
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
rule id
|
|
187
|
+
'#' [_a-z] [-a-zA-Z0-9_]*
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
rule tag
|
|
191
|
+
[a-zA-Z] [-a-zA-Z]* [0-9]? / '*'
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
rule select
|
|
195
|
+
(s [:+>] s / S)?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# TODO: Merge this with attribute rule
|
|
199
|
+
rule accessor
|
|
200
|
+
ident:(class_id / tag) '[' attr:(string / variable) ']' {
|
|
201
|
+
def build env
|
|
202
|
+
env.identifiers.last << env.nearest(ident.text_value)[attr.text_value.delete(%q["'])].evaluate
|
|
203
|
+
end
|
|
204
|
+
}
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
rule operator
|
|
208
|
+
S [-+*/] S {
|
|
209
|
+
def build env
|
|
210
|
+
env.identifiers.last << Node::Operator.new(text_value.strip)
|
|
211
|
+
end
|
|
212
|
+
} / [-+*/] {
|
|
213
|
+
def build env
|
|
214
|
+
env.identifiers.last << Node::Operator.new(text_value)
|
|
215
|
+
end
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
#
|
|
220
|
+
# Tokens which don't need to be evaluated
|
|
221
|
+
#
|
|
222
|
+
rule literal
|
|
223
|
+
color / (dimension / [-a-z]+) '/' dimension {
|
|
224
|
+
def build env
|
|
225
|
+
env.identifiers.last << Node::Anonymous.new(text_value)
|
|
226
|
+
end
|
|
227
|
+
} / number unit {
|
|
228
|
+
def build env
|
|
229
|
+
env.identifiers.last << Node::Number.new(number.text_value, unit.text_value)
|
|
230
|
+
end
|
|
231
|
+
} / string {
|
|
232
|
+
def build env
|
|
233
|
+
env.identifiers.last << Node::String.new(text_value)
|
|
234
|
+
end
|
|
235
|
+
}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# !important
|
|
239
|
+
rule important
|
|
240
|
+
'!important' {
|
|
241
|
+
def build env
|
|
242
|
+
env.identifiers.last << Node::Keyword.new(text_value)
|
|
243
|
+
end
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
#
|
|
248
|
+
# `blue`, `small`, `normal` etc.
|
|
249
|
+
#
|
|
250
|
+
rule keyword
|
|
251
|
+
[a-zA-Z] [-a-zA-Z]* !ns {
|
|
252
|
+
def build env
|
|
253
|
+
env.identifiers.last << Node::Keyword.new(text_value)
|
|
254
|
+
end
|
|
255
|
+
}
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
#
|
|
259
|
+
# 'hello world' / "hello world"
|
|
260
|
+
#
|
|
261
|
+
rule string
|
|
262
|
+
"'" content:(!"'" . )* "'" {
|
|
263
|
+
def value
|
|
264
|
+
text_value[1...-1]
|
|
265
|
+
end
|
|
266
|
+
} / ["] content:(!["] . )* ["] {
|
|
267
|
+
def value
|
|
268
|
+
text_value[1...-1]
|
|
269
|
+
end
|
|
270
|
+
}
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
#
|
|
274
|
+
# Numbers & Units
|
|
275
|
+
#
|
|
276
|
+
rule dimension
|
|
277
|
+
number unit
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
rule number
|
|
281
|
+
'-'? [0-9]* '.' [0-9]+ / '-'? [0-9]+
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
rule unit
|
|
285
|
+
('px'/'em'/'pc'/'%'/'pt'/'cm'/'mm')?
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
#
|
|
290
|
+
# Color
|
|
291
|
+
#
|
|
292
|
+
rule color
|
|
293
|
+
'#' hex {
|
|
294
|
+
def build env
|
|
295
|
+
env.identifiers.last << Node::Color.new(hex.text_value)
|
|
296
|
+
end
|
|
297
|
+
} / fn:(('hsl'/'rgb') 'a'?) '(' arguments ')' {
|
|
298
|
+
def build env
|
|
299
|
+
args = arguments.build env
|
|
300
|
+
env.identifiers.last << Node::Function.new(fn.text_value, args.flatten)
|
|
301
|
+
end
|
|
302
|
+
}
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
rule hex
|
|
306
|
+
[a-fA-F0-9] [a-fA-F0-9] [a-fA-F0-9]+
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
#
|
|
310
|
+
# Functions and arguments
|
|
311
|
+
#
|
|
312
|
+
rule function
|
|
313
|
+
name:([-a-zA-Z_]+) '(' arguments ')' {
|
|
314
|
+
def build env
|
|
315
|
+
args = arguments.build env
|
|
316
|
+
env.identifiers.last << Node::Function.new(name.text_value, [args].flatten)
|
|
317
|
+
end
|
|
318
|
+
}
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
rule arguments
|
|
322
|
+
argument s ',' s arguments {
|
|
323
|
+
def build env
|
|
324
|
+
elements.map do |e|
|
|
325
|
+
e.build env if e.respond_to? :build
|
|
326
|
+
end.compact
|
|
327
|
+
end
|
|
328
|
+
} / argument
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
rule argument
|
|
332
|
+
color {
|
|
333
|
+
def build env
|
|
334
|
+
Node::Color.new text_value
|
|
335
|
+
end
|
|
336
|
+
} / number unit {
|
|
337
|
+
def build env
|
|
338
|
+
Node::Number.new number.text_value, unit.text_value
|
|
339
|
+
end
|
|
340
|
+
} / string {
|
|
341
|
+
def build env
|
|
342
|
+
Node::String.new text_value
|
|
343
|
+
end
|
|
344
|
+
} / [a-zA-Z]+ '=' dimension {
|
|
345
|
+
def build env
|
|
346
|
+
Node::Anonymous.new text_value
|
|
347
|
+
end
|
|
348
|
+
} / [-a-zA-Z0-9_%$/.&=:;#+?]+ {
|
|
349
|
+
def build env
|
|
350
|
+
Node::String.new text_value
|
|
351
|
+
end
|
|
352
|
+
}
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
#
|
|
356
|
+
# Whitespace
|
|
357
|
+
#
|
|
358
|
+
rule s
|
|
359
|
+
[ ]*
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
rule S
|
|
363
|
+
[ ]+
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
rule ws
|
|
367
|
+
[\n ]*
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Non-space char
|
|
371
|
+
rule ns
|
|
372
|
+
![ ;] .
|
|
373
|
+
end
|
|
374
|
+
end
|