cloudhead-less 0.8.12 → 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 (66) hide show
  1. data/README.md +10 -1
  2. data/Rakefile +21 -2
  3. data/VERSION +1 -1
  4. data/bin/lessc +0 -8
  5. data/less.gemspec +48 -15
  6. data/lib/less/command.rb +24 -25
  7. data/lib/less/engine/builder.rb +13 -0
  8. data/lib/less/engine/less.tt +379 -0
  9. data/lib/less/engine/nodes/element.rb +153 -0
  10. data/lib/less/engine/nodes/entity.rb +59 -0
  11. data/lib/less/engine/nodes/function.rb +61 -0
  12. data/lib/less/engine/nodes/literal.rb +132 -0
  13. data/lib/less/engine/nodes/property.rb +120 -0
  14. data/lib/less/engine/nodes/selector.rb +39 -0
  15. data/lib/less/engine/nodes.rb +8 -0
  16. data/lib/less/engine/parser.rb +3854 -0
  17. data/lib/less/engine.rb +42 -139
  18. data/lib/less.rb +65 -10
  19. data/spec/command_spec.rb +2 -6
  20. data/spec/css/accessors-1.0.css +20 -0
  21. data/spec/css/big-1.0.css +3770 -0
  22. data/spec/css/comments-1.0.css +11 -0
  23. data/spec/css/css-1.0.css +40 -0
  24. data/spec/css/functions-1.0.css +8 -0
  25. data/spec/css/import-1.0.css +13 -0
  26. data/spec/css/mixins-1.0.css +30 -0
  27. data/spec/css/operations-1.0.css +30 -0
  28. data/spec/css/rulesets-1.0.css +19 -0
  29. data/spec/css/scope-1.0.css +16 -0
  30. data/spec/css/strings-1.0.css +14 -0
  31. data/spec/css/variables-1.0.css +7 -0
  32. data/spec/css/whitespace-1.0.css +11 -0
  33. data/spec/engine_spec.rb +66 -18
  34. data/spec/less/accessors-1.0.less +20 -0
  35. data/spec/less/big-1.0.less +4810 -0
  36. data/spec/less/colors-1.0.less +0 -0
  37. data/spec/less/comments-1.0.less +46 -0
  38. data/spec/less/css-1.0.less +82 -0
  39. data/spec/less/exceptions/mixed-units-error.less +0 -0
  40. data/spec/less/exceptions/name-error-1.0.less +0 -0
  41. data/spec/less/exceptions/syntax-error-1.0.less +0 -0
  42. data/spec/less/functions-1.0.less +6 -0
  43. data/spec/less/import/import-test-a.less +2 -0
  44. data/spec/less/import/import-test-b.less +8 -0
  45. data/spec/less/import/import-test-c.less +5 -0
  46. data/spec/less/import-1.0.less +7 -0
  47. data/spec/less/mixins-1.0.less +43 -0
  48. data/spec/less/operations-1.0.less +39 -0
  49. data/spec/less/rulesets-1.0.less +30 -0
  50. data/spec/less/scope-1.0.less +33 -0
  51. data/spec/less/strings-1.0.less +14 -0
  52. data/spec/less/variables-1.0.less +16 -0
  53. data/spec/less/whitespace-1.0.less +21 -0
  54. data/spec/spec.css +81 -23
  55. data/spec/spec.less +3 -4
  56. data/spec/spec_helper.rb +4 -1
  57. metadata +46 -13
  58. data/lib/less/tree.rb +0 -82
  59. data/spec/css/less-0.8.10.css +0 -30
  60. data/spec/css/less-0.8.11.css +0 -31
  61. data/spec/css/less-0.8.12.css +0 -28
  62. data/spec/css/less-0.8.5.css +0 -24
  63. data/spec/css/less-0.8.6.css +0 -24
  64. data/spec/css/less-0.8.7.css +0 -24
  65. data/spec/css/less-0.8.8.css +0 -25
  66. data/spec/tree_spec.rb +0 -5
data/lib/less/engine.rb CHANGED
@@ -1,154 +1,57 @@
1
- module Less
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]+/, # 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
- def compile
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
- # Evaluate mixins
45
- #
46
- @tree = @tree.traverse :branch do |path, node|
47
- if node.include? :mixins
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
6
+ module Less
7
+ class Engine
8
+ attr_reader :css, :less
9
+
10
+ def initialize obj
11
+ @less = if obj.is_a? File
12
+ @path = File.dirname(File.expand_path obj.path)
13
+ obj.read
14
+ elsif obj.is_a? String
15
+ obj.dup
16
+ else
17
+ raise ArgumentError, "argument must be an instance of File or String!"
64
18
  end
65
19
 
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
78
-
79
- if (unit = operation.scan(/#{REGEX[:numeric]}|(#)/i).flatten.compact.uniq).size <= 1
80
- unit = unit.join
81
- operation = if unit == '#'
82
- evaluate = lambda do |v|
83
- result = eval v
84
- unit + ( result.zero?? '000' : result.to_s(16) )
85
- end
86
- operation.gsub REGEX[:color] do
87
- hex = $1 * ( $1.size < 6 ? 6 / $1.size : 1 )
88
- hex.to_i(16)
89
- end.delete unit
90
- else
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
20
+ begin
21
+ require Less::PARSER
22
+ rescue LoadError
23
+ Treetop.load Less::GRAMMAR
100
24
  end
25
+
26
+ @parser = LessParser.new
101
27
  end
102
- alias render compile
103
28
 
104
- #
105
- # Evaluate variables
106
- #
107
- def evaluate key, expression, path, node
108
- if expression.is_a? String and expression.include? '@' # There's a var to evaluate
109
- expression.scan /#{REGEX[:path]}#{REGEX[:variable]}/ do |var|
110
- name = var.last
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
+ log @tree.inspect
39
+
40
+ @tree
127
41
  end
42
+ alias :to_tree :parse
128
43
 
129
- def to_css chain = :desc
130
- self.compile.to_css chain
44
+ def to_css
45
+ "/* Generated with Less #{Less.version} */\n\n" +
46
+ (@css || @css = self.parse.to_css)
131
47
  end
132
48
 
133
- def hashify
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(/\/\/.*\n/, ''). # Comments //
143
- gsub(/\/\*.*?\*\//m, ''). # Comments /*
144
- gsub(/\s+/, ' '). # Replace \t\n
145
- gsub(/"/, "'"). # " to '
146
- gsub(/("|')(.*?)(\1)/) { $1 + CGI.escape( $2 ) + $1 }. # Escape string values
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}
49
+ def prepare
50
+ @less.gsub(/\r\n/, "\n"). # m$
51
+ gsub(/\t/, ' ') # Tabs to spaces
52
+ #gsub(/('|")(.*?)(\1)/) { $1 + CGI.escape( $2 ) + $1 } # Escape string values
53
+ # gsub(/\/\/.*\n/, ''). # Comments //
54
+ # gsub(/\/\*.*?\*\//m, '') # Comments /*
152
55
  end
153
56
  end
154
57
  end
data/lib/less.rb CHANGED
@@ -1,24 +1,79 @@
1
- $:.unshift File.dirname(__FILE__)
1
+ $:.unshift File.dirname(__FILE__), File.join(File.dirname(__FILE__), 'vendor', 'treetop', 'lib')
2
2
 
3
+ require 'rubygems'
3
4
  require 'cgi'
5
+ require 'delegate'
6
+ require 'treetop'
4
7
 
5
8
  require 'less/command'
6
9
  require 'less/engine'
7
- require 'less/tree'
8
10
 
9
11
  module Less
10
- MixedUnitsError = Class.new(Exception)
11
- PathError = Class.new(Exception)
12
- CompoundOperationError = Class.new(Exception)
12
+ PARSER = 'lib/less/engine/parser.rb'
13
+ GRAMMAR = 'lib/less/engine/less.tt'
14
+
15
+ MixedUnitsError = Class.new(Exception)
16
+ PathError = Class.new(Exception)
17
+ VariableNameError = Class.new(NameError)
18
+ MixinNameError = Class.new(NameError)
19
+ SyntaxError = Class.new(RuntimeError)
20
+ ImportError = Class.new(Exception)
21
+
22
+ $verbose = false
13
23
 
14
24
  def self.version
15
- File.read( File.join( File.dirname(__FILE__), '..', 'VERSION') )
25
+ File.read( File.join( File.dirname(__FILE__), '..', 'VERSION') ).strip
26
+ end
27
+
28
+ def self.parse less
29
+ Engine.new(less).to_css
30
+ end
31
+ end
32
+
33
+ module Treetop
34
+ module Runtime
35
+ class CompiledParser
36
+ def failure_message
37
+ return nil unless (tf = terminal_failures) && tf.size > 0
38
+ "on line #{failure_line}: expected " + (
39
+ tf.size == 1 ?
40
+ tf[0].expected_string :
41
+ "one of #{tf.map {|f| f.expected_string }.uniq * ' '}"
42
+ ) +
43
+ " got `#{input[failure_index]}`" +
44
+ " after:\n\n#{input[index...failure_index]}\n"
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ class Object
51
+ def verbose
52
+ $verbose = true
53
+ yield
54
+ ensure
55
+ $verbose = false
56
+ end
57
+
58
+ def tap
59
+ yield self
60
+ self
61
+ end
62
+
63
+ def log(s = '') puts "* #{s}" if $verbose end
64
+ def log!(s = '') puts "* #{s}" end
65
+ def error(s) $stderr.puts s end
66
+ def error!(s) raise CradleError, s end
67
+ end
68
+
69
+ class Class
70
+ def to_sym
71
+ self.to_s.to_sym
16
72
  end
17
73
  end
18
74
 
19
- class Hash
20
- # Convert a hash into a Tree
21
- def to_tree
22
- Less::Tree.new self
75
+ class Symbol
76
+ def to_proc
77
+ proc {|obj, *args| obj.send(self, *args) }
23
78
  end
24
79
  end
data/spec/command_spec.rb CHANGED
@@ -6,7 +6,7 @@ module LessCommandSpecHelper
6
6
  end
7
7
 
8
8
  def valid_options
9
- {:destination => File.dirname(__FILE__) + '/spec.css', :watch => true, :chain => true, :debug => false}.merge(required_options)
9
+ {:destination => File.dirname(__FILE__) + '/spec.css', :watch => true, :debug => false}.merge(required_options)
10
10
  end
11
11
  end
12
12
 
@@ -48,10 +48,6 @@ describe Less::Command do
48
48
  @command.options[:watch].should == valid_options[:watch]
49
49
  end
50
50
 
51
- it "should set the chain" do
52
- @command.options[:chain].should == valid_options[:chain]
53
- end
54
-
55
51
  it "should set the debug" do
56
52
  @command.options[:debug].should == valid_options[:debug]
57
53
  end
@@ -80,7 +76,7 @@ describe Less::Command do
80
76
  end
81
77
 
82
78
  it "should attempt to re-compile" do
83
- @command.should_receive(:compile).with().once
79
+ @command.should_receive(:parse).with().once
84
80
  end
85
81
  end
86
82
 
@@ -0,0 +1,20 @@
1
+ /* Generated with Less 1.0.0 */
2
+
3
+ .magic-box {
4
+ content: "gold";
5
+ width: 60cm;
6
+ height: 40cm;
7
+ }
8
+ .magic-box #lock { color: silver; }
9
+ #accessors {
10
+ content: "gold";
11
+ width: 60cm;
12
+ }
13
+ .unlock {
14
+ content: "gold";
15
+ width: 60cm;
16
+ height: 40cm;
17
+ color: silver;
18
+ border-color: orange;
19
+ }
20
+ .unlock #lock { color: silver; }