nirvdrum-less 1.1.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.
Files changed (164) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +179 -0
  3. data/README.md +39 -0
  4. data/Rakefile +90 -0
  5. data/VERSION +1 -0
  6. data/bin/lessc +86 -0
  7. data/less.gemspec +205 -0
  8. data/lib/ext.rb +62 -0
  9. data/lib/less.rb +33 -0
  10. data/lib/less/command.rb +106 -0
  11. data/lib/less/engine.rb +54 -0
  12. data/lib/less/engine/builder.rb +8 -0
  13. data/lib/less/engine/grammar/common.tt +29 -0
  14. data/lib/less/engine/grammar/entity.tt +139 -0
  15. data/lib/less/engine/grammar/less.tt +271 -0
  16. data/lib/less/engine/nodes.rb +9 -0
  17. data/lib/less/engine/nodes/element.rb +186 -0
  18. data/lib/less/engine/nodes/entity.rb +79 -0
  19. data/lib/less/engine/nodes/function.rb +79 -0
  20. data/lib/less/engine/nodes/literal.rb +167 -0
  21. data/lib/less/engine/nodes/property.rb +156 -0
  22. data/lib/less/engine/nodes/ruleset.rb +12 -0
  23. data/lib/less/engine/nodes/selector.rb +39 -0
  24. data/lib/vendor/treetop/.gitignore +7 -0
  25. data/lib/vendor/treetop/LICENSE +19 -0
  26. data/lib/vendor/treetop/README +164 -0
  27. data/lib/vendor/treetop/Rakefile +19 -0
  28. data/lib/vendor/treetop/benchmark/seqpar.gnuplot +15 -0
  29. data/lib/vendor/treetop/benchmark/seqpar.treetop +16 -0
  30. data/lib/vendor/treetop/benchmark/seqpar_benchmark.rb +107 -0
  31. data/lib/vendor/treetop/bin/tt +28 -0
  32. data/lib/vendor/treetop/lib/treetop.rb +8 -0
  33. data/lib/vendor/treetop/lib/treetop/bootstrap_gen_1_metagrammar.rb +45 -0
  34. data/lib/vendor/treetop/lib/treetop/compiler.rb +6 -0
  35. data/lib/vendor/treetop/lib/treetop/compiler/grammar_compiler.rb +42 -0
  36. data/lib/vendor/treetop/lib/treetop/compiler/lexical_address_space.rb +17 -0
  37. data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.rb +3097 -0
  38. data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.treetop +408 -0
  39. data/lib/vendor/treetop/lib/treetop/compiler/node_classes.rb +19 -0
  40. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/anything_symbol.rb +18 -0
  41. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/atomic_expression.rb +14 -0
  42. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/character_class.rb +24 -0
  43. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/choice.rb +31 -0
  44. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
  45. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/grammar.rb +28 -0
  46. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
  47. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/nonterminal.rb +13 -0
  48. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/optional.rb +19 -0
  49. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
  50. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_expression.rb +138 -0
  51. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
  52. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/predicate.rb +45 -0
  53. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/repetition.rb +55 -0
  54. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/sequence.rb +68 -0
  55. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/terminal.rb +20 -0
  56. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
  57. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
  58. data/lib/vendor/treetop/lib/treetop/compiler/ruby_builder.rb +113 -0
  59. data/lib/vendor/treetop/lib/treetop/ruby_extensions.rb +2 -0
  60. data/lib/vendor/treetop/lib/treetop/ruby_extensions/string.rb +42 -0
  61. data/lib/vendor/treetop/lib/treetop/runtime.rb +5 -0
  62. data/lib/vendor/treetop/lib/treetop/runtime/compiled_parser.rb +105 -0
  63. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list.rb +4 -0
  64. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
  65. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
  66. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
  67. data/lib/vendor/treetop/lib/treetop/runtime/syntax_node.rb +72 -0
  68. data/lib/vendor/treetop/lib/treetop/runtime/terminal_parse_failure.rb +16 -0
  69. data/lib/vendor/treetop/lib/treetop/runtime/terminal_syntax_node.rb +17 -0
  70. data/lib/vendor/treetop/lib/treetop/version.rb +9 -0
  71. data/lib/vendor/treetop/spec/compiler/and_predicate_spec.rb +36 -0
  72. data/lib/vendor/treetop/spec/compiler/anything_symbol_spec.rb +44 -0
  73. data/lib/vendor/treetop/spec/compiler/character_class_spec.rb +182 -0
  74. data/lib/vendor/treetop/spec/compiler/choice_spec.rb +80 -0
  75. data/lib/vendor/treetop/spec/compiler/circular_compilation_spec.rb +28 -0
  76. data/lib/vendor/treetop/spec/compiler/failure_propagation_functional_spec.rb +21 -0
  77. data/lib/vendor/treetop/spec/compiler/grammar_compiler_spec.rb +84 -0
  78. data/lib/vendor/treetop/spec/compiler/grammar_spec.rb +41 -0
  79. data/lib/vendor/treetop/spec/compiler/nonterminal_symbol_spec.rb +40 -0
  80. data/lib/vendor/treetop/spec/compiler/not_predicate_spec.rb +38 -0
  81. data/lib/vendor/treetop/spec/compiler/one_or_more_spec.rb +35 -0
  82. data/lib/vendor/treetop/spec/compiler/optional_spec.rb +37 -0
  83. data/lib/vendor/treetop/spec/compiler/parenthesized_expression_spec.rb +19 -0
  84. data/lib/vendor/treetop/spec/compiler/parsing_rule_spec.rb +32 -0
  85. data/lib/vendor/treetop/spec/compiler/sequence_spec.rb +115 -0
  86. data/lib/vendor/treetop/spec/compiler/terminal_spec.rb +81 -0
  87. data/lib/vendor/treetop/spec/compiler/terminal_symbol_spec.rb +37 -0
  88. data/lib/vendor/treetop/spec/compiler/test_grammar.treetop +7 -0
  89. data/lib/vendor/treetop/spec/compiler/test_grammar.tt +7 -0
  90. data/lib/vendor/treetop/spec/compiler/test_grammar_do.treetop +7 -0
  91. data/lib/vendor/treetop/spec/compiler/zero_or_more_spec.rb +56 -0
  92. data/lib/vendor/treetop/spec/composition/a.treetop +11 -0
  93. data/lib/vendor/treetop/spec/composition/b.treetop +11 -0
  94. data/lib/vendor/treetop/spec/composition/c.treetop +10 -0
  95. data/lib/vendor/treetop/spec/composition/d.treetop +10 -0
  96. data/lib/vendor/treetop/spec/composition/f.treetop +17 -0
  97. data/lib/vendor/treetop/spec/composition/grammar_composition_spec.rb +40 -0
  98. data/lib/vendor/treetop/spec/composition/subfolder/e_includes_c.treetop +15 -0
  99. data/lib/vendor/treetop/spec/ruby_extensions/string_spec.rb +32 -0
  100. data/lib/vendor/treetop/spec/runtime/compiled_parser_spec.rb +101 -0
  101. data/lib/vendor/treetop/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
  102. data/lib/vendor/treetop/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
  103. data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
  104. data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
  105. data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
  106. data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
  107. data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
  108. data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +164 -0
  109. data/lib/vendor/treetop/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
  110. data/lib/vendor/treetop/spec/runtime/syntax_node_spec.rb +53 -0
  111. data/lib/vendor/treetop/spec/spec_helper.rb +106 -0
  112. data/lib/vendor/treetop/spec/spec_suite.rb +4 -0
  113. data/lib/vendor/treetop/treetop.gemspec +17 -0
  114. data/spec/command_spec.rb +102 -0
  115. data/spec/css/accessors.css +18 -0
  116. data/spec/css/big.css +3768 -0
  117. data/spec/css/colors.css +11 -0
  118. data/spec/css/comments.css +9 -0
  119. data/spec/css/css-3.css +2 -0
  120. data/spec/css/css.css +45 -0
  121. data/spec/css/functions.css +6 -0
  122. data/spec/css/import.css +12 -0
  123. data/spec/css/lazy-eval.css +1 -0
  124. data/spec/css/mixins-args.css +0 -0
  125. data/spec/css/mixins.css +28 -0
  126. data/spec/css/operations.css +28 -0
  127. data/spec/css/parens.css +16 -0
  128. data/spec/css/rulesets.css +17 -0
  129. data/spec/css/scope.css +11 -0
  130. data/spec/css/selectors.css +8 -0
  131. data/spec/css/strings.css +12 -0
  132. data/spec/css/variables.css +6 -0
  133. data/spec/css/whitespace.css +11 -0
  134. data/spec/engine_spec.rb +111 -0
  135. data/spec/less/accessors.less +20 -0
  136. data/spec/less/big.less +4810 -0
  137. data/spec/less/colors.less +34 -0
  138. data/spec/less/comments.less +46 -0
  139. data/spec/less/css-3.less +11 -0
  140. data/spec/less/css.less +98 -0
  141. data/spec/less/exceptions/mixed-units-error.less +3 -0
  142. data/spec/less/exceptions/name-error-1.0.less +3 -0
  143. data/spec/less/exceptions/syntax-error-1.0.less +3 -0
  144. data/spec/less/functions.less +6 -0
  145. data/spec/less/import.less +7 -0
  146. data/spec/less/import/import-test-a.less +2 -0
  147. data/spec/less/import/import-test-b.less +8 -0
  148. data/spec/less/import/import-test-c.less +6 -0
  149. data/spec/less/import/import-test-d.css +1 -0
  150. data/spec/less/lazy-eval.less +6 -0
  151. data/spec/less/mixins-args.less +0 -0
  152. data/spec/less/mixins.less +43 -0
  153. data/spec/less/operations.less +39 -0
  154. data/spec/less/parens.less +21 -0
  155. data/spec/less/rulesets.less +30 -0
  156. data/spec/less/scope.less +32 -0
  157. data/spec/less/selectors.less +15 -0
  158. data/spec/less/strings.less +14 -0
  159. data/spec/less/variables.less +18 -0
  160. data/spec/less/whitespace.less +30 -0
  161. data/spec/spec.css +82 -0
  162. data/spec/spec.less +148 -0
  163. data/spec/spec_helper.rb +8 -0
  164. metadata +219 -0
@@ -0,0 +1,62 @@
1
+ module Treetop
2
+ module Runtime
3
+ class CompiledParser
4
+ def failure_message
5
+ return nil unless (tf = terminal_failures) && tf.size > 0
6
+ "on line #{failure_line}: expected " + (
7
+ tf.size == 1 ?
8
+ tf[0].expected_string :
9
+ "one of #{Less::YELLOW[tf.map {|f| f.expected_string }.uniq * ' ']}"
10
+ ) +
11
+ " got #{Less::YELLOW[input[failure_index].chr]}" +
12
+ " after:\n\n#{input[index...failure_index]}\n"
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class Object
19
+ def verbose
20
+ $verbose = true
21
+ yield
22
+ ensure
23
+ $verbose = false
24
+ end
25
+
26
+ def tap
27
+ yield self
28
+ self
29
+ end
30
+
31
+ def log(s = '') puts "* #{s}" if $verbose end
32
+ def log!(s = '') puts "* #{s}" end
33
+ def error(s) $stderr.puts s end
34
+ def error!(s) raise Exception, s end
35
+ end
36
+
37
+ class Array
38
+ def dissolve
39
+ ary = flatten.compact
40
+ case ary.size
41
+ when 0 then []
42
+ when 1 then first
43
+ else ary
44
+ end
45
+ end
46
+
47
+ def one?
48
+ size == 1
49
+ end
50
+ end
51
+
52
+ class Class
53
+ def to_sym
54
+ self.to_s.to_sym
55
+ end
56
+ end
57
+
58
+ class Symbol
59
+ def to_proc
60
+ proc {|obj, *args| obj.send(self, *args) }
61
+ end
62
+ end
@@ -0,0 +1,33 @@
1
+ $:.unshift File.join(File.dirname(__FILE__), 'vendor', 'treetop', 'lib'),
2
+ File.dirname(__FILE__)
3
+
4
+ require 'cgi'
5
+ require 'treetop'
6
+ require 'delegate'
7
+
8
+ LESS_ROOT = File.expand_path(File.dirname(__FILE__))
9
+ LESS_PARSER = File.join(LESS_ROOT, 'less', 'engine', 'parser.rb')
10
+ LESS_GRAMMAR = File.join(LESS_ROOT, 'less', 'engine', 'grammar')
11
+
12
+ require 'ext'
13
+ require 'less/command'
14
+ require 'less/engine'
15
+
16
+ module Less
17
+ MixedUnitsError = Class.new(RuntimeError)
18
+ PathError = Class.new(RuntimeError)
19
+ VariableNameError = Class.new(NameError)
20
+ MixinNameError = Class.new(NameError)
21
+ SyntaxError = Class.new(RuntimeError)
22
+ ImportError = Class.new(RuntimeError)
23
+
24
+ $verbose = false
25
+
26
+ def self.version
27
+ File.read( File.join( File.dirname(__FILE__), '..', 'VERSION') ).strip
28
+ end
29
+
30
+ def self.parse less
31
+ Engine.new(less).to_css
32
+ end
33
+ end
@@ -0,0 +1,106 @@
1
+ module Less
2
+ ESC = "\033"
3
+ RESET = "#{ESC}[0m"
4
+
5
+ GREEN = lambda {|s| "#{ESC}[1;32m#{s}#{RESET}"}
6
+ RED = lambda {|s| "#{ESC}[1;31m#{s}#{RESET}"}
7
+ YELLOW = lambda {|s| "#{ESC}[1;33m#{s}#{RESET}"}
8
+ BOLD = lambda {|s| "#{ESC}[1m#{s}#{RESET}"}
9
+
10
+ class Command
11
+ attr_accessor :source, :destination, :options
12
+
13
+ def initialize options
14
+ $verbose = options[:debug]
15
+ @source = options[:source]
16
+ @destination = (options[:destination] || options[:source]).gsub /\.(less|lss)/, '.css'
17
+ @options = options
18
+ end
19
+
20
+ def watch?() @options[:watch] end
21
+ def compress?() @options[:compress] end
22
+ def debug?() @options[:debug] end
23
+
24
+ # little function which allows us to
25
+ # Ctrl-C exit inside the passed block
26
+ def watch
27
+ begin
28
+ yield
29
+ rescue Interrupt
30
+ puts
31
+ exit 0
32
+ end
33
+ end
34
+
35
+ def run!
36
+ parse(true) unless File.exist? @destination
37
+
38
+ if watch?
39
+ log "Watching for changes in #@source... Ctrl-C to abort.\n: "
40
+
41
+ # Main watch loop
42
+ loop do
43
+ watch { sleep 1 }
44
+
45
+ # File has changed
46
+ if File.stat( @source ).mtime > File.stat( @destination ).mtime
47
+ print "Change detected... "
48
+
49
+ # Loop until error is fixed
50
+ until parse
51
+ log "Press [return] to continue..."
52
+ watch { $stdin.gets }
53
+ end
54
+ end
55
+ end
56
+ else
57
+ parse
58
+ end
59
+ end
60
+
61
+ def parse new = false
62
+ begin
63
+ # Create a new Less object with the contents of a file
64
+ css = Less::Engine.new(File.new(@source)).to_css
65
+ css = css.delete " \n" if compress?
66
+
67
+ File.open( @destination, "w" ) do |file|
68
+ file.write css
69
+ end
70
+ print "#{GREEN['* ' + (new ? 'Created' : 'Updated')]} " +
71
+ "#{@destination.split('/').last}\n: " if watch?
72
+ rescue Errno::ENOENT => e
73
+ abort "#{e}"
74
+ rescue SyntaxError => e
75
+ err "#{e}\n", "Syntax"
76
+ rescue MixedUnitsError => e
77
+ err "`#{e}` you're mixing units together! What do you expect?\n"
78
+ rescue PathError => e
79
+ err "`#{e}` was not found.\n", "Path"
80
+ rescue VariableNameError => e
81
+ err "#{YELLOW[e]} is undefined.\n", "Name"
82
+ rescue MixinNameError => e
83
+ err "#{YELLOW[e]} is undefined.\n", "Name"
84
+ else
85
+ true
86
+ end
87
+ end
88
+
89
+ # Just a logging function to avoid typing '*'
90
+ def log s = ''
91
+ print '* ' + s.to_s
92
+ end
93
+
94
+ def err s = '', type = ''
95
+ type = type.strip + ' ' unless type.empty?
96
+ print "#{RED["! #{type}Error"]}: #{s}"
97
+ if @options[:growl]
98
+ growl = Growl.new
99
+ growl.title = "LESS"
100
+ growl.message = "#{type}Error in #@source!"
101
+ growl.run
102
+ false
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,54 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'engine/builder'
4
+ require 'engine/nodes'
5
+
6
+ begin
7
+ require 'engine/parser'
8
+ rescue LoadError
9
+ Treetop.load File.join(LESS_GRAMMAR, 'common.tt')
10
+ Treetop.load File.join(LESS_GRAMMAR, 'entity.tt')
11
+ Treetop.load File.join(LESS_GRAMMAR, 'less.tt')
12
+ end
13
+
14
+ module Less
15
+ class Engine
16
+ attr_reader :css, :less
17
+
18
+ def initialize obj
19
+ @less = if obj.is_a? File
20
+ @path = File.dirname File.expand_path(obj.path)
21
+ obj.read
22
+ elsif obj.is_a? String
23
+ obj.dup
24
+ else
25
+ raise ArgumentError, "argument must be an instance of File or String!"
26
+ end
27
+
28
+ @parser = StyleSheetParser.new
29
+ end
30
+
31
+ def parse build = true, env = Node::Element.new
32
+ root = @parser.parse(self.prepare)
33
+
34
+ return root unless build
35
+
36
+ if root
37
+ @tree = root.build env.tap {|e| e.file = @path }
38
+ else
39
+ raise SyntaxError, @parser.failure_message
40
+ end
41
+
42
+ @tree
43
+ end
44
+ alias :to_tree :parse
45
+
46
+ def to_css
47
+ @css || @css = self.parse.group.to_css
48
+ end
49
+
50
+ def prepare
51
+ @less.gsub(/\r\n/, "\n").gsub(/\t/, ' ')
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,8 @@
1
+ module Builder
2
+ def build env = Less::Element.new
3
+ elements.map do |e|
4
+ e.build env if e.respond_to? :build
5
+ end
6
+ env
7
+ end
8
+ end
@@ -0,0 +1,29 @@
1
+ module Less
2
+ module StyleSheet
3
+ grammar Common
4
+ #
5
+ # Whitespace
6
+ #
7
+ rule s
8
+ [ ]*
9
+ end
10
+
11
+ rule S
12
+ [ ]+
13
+ end
14
+
15
+ rule ws
16
+ [\n ]*
17
+ end
18
+
19
+ rule WS
20
+ [\n ]+
21
+ end
22
+
23
+ # Non-space char
24
+ rule ns
25
+ ![ ;\n] .
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,139 @@
1
+ module Less
2
+ module StyleSheet
3
+ grammar Entity
4
+ #
5
+ # Entity: Any whitespace delimited token
6
+ #
7
+ rule entity
8
+ function / fonts / keyword / accessor / variable / literal / important
9
+ end
10
+
11
+ rule fonts
12
+ font family:(s ',' s font)+ {
13
+ def build
14
+ Node::FontFamily.new(all.map(&:build))
15
+ end
16
+
17
+ def all
18
+ [font] + family.elements.map {|f| f.font }
19
+ end
20
+ }
21
+ end
22
+
23
+ rule font
24
+ [a-zA-Z] [-a-zA-Z0-9]* {
25
+ def build
26
+ Node::Keyword.new(text_value)
27
+ end
28
+ } / string {
29
+ def build
30
+ Node::String.new(text_value)
31
+ end
32
+ }
33
+ end
34
+
35
+ #
36
+ # Tokens which don't need to be evaluated
37
+ #
38
+ rule literal
39
+ color / (dimension / [-a-z]+) '/' dimension {
40
+ def build
41
+ Node::Anonymous.new(text_value)
42
+ end
43
+ } / number unit {
44
+ def build
45
+ Node::Number.new(number.text_value, unit.text_value)
46
+ end
47
+ } / string {
48
+ def build
49
+ Node::String.new(text_value)
50
+ end
51
+ }
52
+ end
53
+
54
+ # !important
55
+ rule important
56
+ '!important' {
57
+ def build
58
+ Node::Keyword.new(text_value)
59
+ end
60
+ }
61
+ end
62
+
63
+ #
64
+ # `blue`, `small`, `normal` etc.
65
+ #
66
+ rule keyword
67
+ [-a-zA-Z]+ !ns {
68
+ def build
69
+ Node::Keyword.new(text_value)
70
+ end
71
+ }
72
+ end
73
+
74
+ #
75
+ # 'hello world' / "hello world"
76
+ #
77
+ rule string
78
+ "'" content:(!"'" . )* "'" {
79
+ def value
80
+ content.text_value
81
+ end
82
+ } / ["] content:(!["] . )* ["] {
83
+ def value
84
+ content.text_value
85
+ end
86
+ }
87
+ end
88
+
89
+ #
90
+ # Numbers & Units
91
+ #
92
+ rule dimension
93
+ number unit
94
+ end
95
+
96
+ rule number
97
+ '-'? [0-9]* '.' [0-9]+ / '-'? [0-9]+
98
+ end
99
+
100
+ rule unit
101
+ ('px'/'em'/'pc'/'%'/'ex'/'pt'/'cm'/'mm')?
102
+ end
103
+
104
+ #
105
+ # Color
106
+ #
107
+ rule color
108
+ '#' rgb {
109
+ def build
110
+ Node::Color.new(*rgb.build)
111
+ end
112
+ } / fn:(('hsl'/'rgb') 'a'?) arguments {
113
+ def build
114
+ Node::Function.new(fn.text_value, arguments.build.flatten)
115
+ end
116
+ }
117
+ end
118
+
119
+ #
120
+ # 00ffdd / 0fd
121
+ #
122
+ rule rgb
123
+ r:(hex hex) g:(hex hex) b:(hex hex) {
124
+ def build
125
+ [r.text_value, g.text_value, b.text_value]
126
+ end
127
+ } / r:hex g:hex b:hex {
128
+ def build
129
+ [r.text_value, g.text_value, b.text_value].map {|c| c * 2 }
130
+ end
131
+ }
132
+ end
133
+
134
+ rule hex
135
+ [a-fA-F0-9]
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,271 @@
1
+ module Less
2
+ grammar StyleSheet
3
+ include Common
4
+ include Entity
5
+
6
+ rule primary
7
+ (declaration / ruleset / import / comment)+ <Builder> / declaration* <Builder> / import* <Builder> / comment*
8
+ end
9
+
10
+ rule comment
11
+ ws '/*' (!'*/' . )* '*/' ws / ws '//' (!"\n" .)* "\n" ws
12
+ end
13
+
14
+ #
15
+ # div, .class, body > p {...}
16
+ #
17
+ rule ruleset
18
+ selectors "{" ws primary ws "}" ws {
19
+ def build env
20
+ # Build the ruleset for each selector
21
+ selectors.build(env, :ruleset).each do |sel|
22
+ primary.build sel
23
+ end
24
+ end
25
+ } / ws selectors ';' ws {
26
+ def build env
27
+ selectors.build(env, :mixin).each do |path|
28
+ rules = path.inject(env.root) do |current, node|
29
+ current.descend(node.selector, node) or raise MixinNameError, path.join
30
+ end.rules
31
+ env.rules += rules
32
+ end
33
+ end
34
+ }
35
+ end
36
+
37
+ rule import
38
+ "@import" S url:(string / url) medias? s ';' ws {
39
+ def build env
40
+ path = File.join(env.root.file, url.value)
41
+ path += '.less' unless path =~ /\.(le|c)ss$/
42
+ if File.exist? path
43
+ imported = Less::Engine.new(File.new(path)).to_tree
44
+ env.rules += imported.rules
45
+ else
46
+ raise ImportError, path
47
+ end
48
+ end
49
+ }
50
+ end
51
+
52
+ rule url
53
+ 'url(' path:(string / [-a-zA-Z0-9_%$/.&=:;#+?]+) ')' {
54
+ def build env = nil
55
+ Node::String.new CGI.unescape(path.text_value)
56
+ end
57
+
58
+ def value
59
+ build
60
+ end
61
+ }
62
+ end
63
+
64
+ rule medias
65
+ [-a-z]+ (s ',' s [a-z]+)*
66
+ end
67
+
68
+ rule selectors
69
+ ws selector tail:(s ',' ws selector)* ws {
70
+ def build env, method
71
+ all.map do |e|
72
+ e.send(method, env) if e.respond_to? method
73
+ end.compact
74
+ end
75
+
76
+ def all
77
+ [selector] + tail.elements.map {|e| e.selector }
78
+ end
79
+ }
80
+ end
81
+
82
+ #
83
+ # div > p a {...}
84
+ #
85
+ rule selector
86
+ sel:(s select element s)+ arguments? {
87
+ def ruleset env
88
+ sel.elements.inject(env) do |node, e|
89
+ node << Node::Element.new(e.element.text_value, e.select.text_value)
90
+ node.last
91
+ end
92
+ end
93
+
94
+ def mixin env
95
+ sel.elements.map do |e|
96
+ Node::Element.new(e.element.text_value, e.select.text_value)
97
+ end
98
+ end
99
+ }
100
+ end
101
+
102
+ #
103
+ # @my-var: 12px;
104
+ # height: 100%;
105
+ #
106
+ rule declaration
107
+ ws name:(ident / variable) s ':' s expressions s (';'/ ws &'}') ws {
108
+ def build env
109
+ env << (name.text_value =~ /^@/ ?
110
+ Node::Variable : Node::Property).new(name.text_value, expressions.build(env), env)
111
+ end
112
+ # Empty rule
113
+ } / ws ident s ':' s ';' ws
114
+ end
115
+
116
+ #
117
+ # An operation or compound value
118
+ #
119
+ rule expressions
120
+ # Operation
121
+ expression tail:(operator expression)+ {
122
+ def build env
123
+ all.map {|e| e.build(env) }.dissolve
124
+ end
125
+
126
+ def all
127
+ [expression] + tail.elements.map {|i| [i.operator, i.expression] }.flatten.compact
128
+ end
129
+ # Space-delimited expressions
130
+ } / expression tail:(WS expression)* {
131
+ def build env
132
+ all.map {|e| e.build(env) if e.respond_to? :build }.compact
133
+ end
134
+
135
+ def all
136
+ [expression] + tail.elements.map {|f| f.expression }
137
+ end
138
+ }
139
+ end
140
+
141
+ rule expression
142
+ '(' s expressions s ')' {
143
+ def build env
144
+ Node::Expression.new(['('] + expressions.build(env).flatten + [')'])
145
+ end
146
+ } / entity '' {
147
+ def build env
148
+ entity.method(:build).arity.zero?? entity.build : entity.build(env)
149
+ end
150
+ }
151
+ end
152
+
153
+ #
154
+ # An identifier
155
+ #
156
+ rule ident
157
+ '*'? '-'? [-a-z0-9_]+
158
+ end
159
+
160
+ rule variable
161
+ '@' [-a-zA-Z0-9_]+ {
162
+ def build
163
+ Node::Variable.new(text_value)
164
+ end
165
+ }
166
+ end
167
+
168
+ #
169
+ # div / .class / #id / input[type="text"] / lang(fr)
170
+ #
171
+ rule element
172
+ (class_id / tag / ident) attribute* ('(' ident? attribute* ')')? / attribute+ / '@media' / '@font-face'
173
+ end
174
+
175
+ rule class_id
176
+ tag? (class / id)+
177
+ end
178
+
179
+ #
180
+ # [type="text"]
181
+ #
182
+ rule attribute
183
+ '[' tag ([|~*$^]? '=') (tag / string) ']' / '[' (tag / string) ']'
184
+ end
185
+
186
+ rule class
187
+ '.' [_a-zA-Z] [-a-zA-Z0-9_]*
188
+ end
189
+
190
+ rule id
191
+ '#' [_a-zA-Z] [-a-zA-Z0-9_]*
192
+ end
193
+
194
+ rule tag
195
+ [a-zA-Z] [-a-zA-Z]* [0-9]? / '*'
196
+ end
197
+
198
+ rule select
199
+ (s [+>~] s / s ':' / S)?
200
+ end
201
+
202
+ # TODO: Merge this with attribute rule
203
+ rule accessor
204
+ ident:(class_id / tag) '[' attr:(string / variable) ']' {
205
+ def build env
206
+ env.nearest(ident.text_value)[attr.text_value.delete(%q["'])].evaluate
207
+ end
208
+ }
209
+ end
210
+
211
+ rule operator
212
+ S [-+*/] S {
213
+ def build env
214
+ Node::Operator.new(text_value.strip)
215
+ end
216
+ } / [-+*/] {
217
+ def build env
218
+ Node::Operator.new(text_value)
219
+ end
220
+ }
221
+ end
222
+
223
+ #
224
+ # Functions and arguments
225
+ #
226
+ rule function
227
+ name:([-a-zA-Z_]+) arguments {
228
+ def build
229
+ Node::Function.new(name.text_value, [arguments.build].flatten)
230
+ end
231
+ }
232
+ end
233
+
234
+ rule arguments
235
+ '(' s argument s tail:(',' s argument s)* ')' {
236
+ def build
237
+ all.map do |e|
238
+ e.build if e.respond_to? :build
239
+ end.compact
240
+ end
241
+
242
+ def all
243
+ [argument] + tail.elements.map {|e| e.argument }
244
+ end
245
+ }
246
+ end
247
+
248
+ rule argument
249
+ color / number unit {
250
+ def build
251
+ Node::Number.new number.text_value, unit.text_value
252
+ end
253
+ } / string {
254
+ def build
255
+ Node::String.new text_value
256
+ end
257
+ } / [a-zA-Z]+ '=' dimension {
258
+ def build
259
+ Node::Anonymous.new text_value
260
+ end
261
+ } / [-a-zA-Z0-9_%$/.&=:;#+?]+ {
262
+ def build
263
+ Node::String.new text_value
264
+ end
265
+ } / function / keyword other:(S keyword)* {
266
+ def build
267
+ end
268
+ }
269
+ end
270
+ end
271
+ end