Permalink
Browse files

grand refactoring

  • Loading branch information...
1 parent d813bef commit 3215a6acdb5e985a95e058b705c52826b420a523 Sven Fuchs committed May 23, 2010
Showing with 1,236 additions and 293 deletions.
  1. +39 −0 NOTES
  2. +4 −0 lib/css/color.rb
  3. +0 −24 lib/css/css.treetop
  4. +0 −15 lib/css/css_declaration.treetop
  5. +0 −23 lib/css/css_expression.treetop
  6. +0 −12 lib/css/css_pre_parser.rb
  7. +0 −104 lib/css/css_primitives.treetop
  8. +11 −0 lib/css/number.rb
  9. +4 −4 lib/css/{css_color.treetop → parser/colors.treetop}
  10. +16 −0 lib/css/parser/pre_parser.rb
  11. +86 −0 lib/css/parser/primitives.treetop
  12. +27 −0 lib/css/parser/properties.treetop
  13. +55 −0 lib/css/parser/property/background.treetop
  14. +40 −0 lib/css/parser/property/border.treetop
  15. +56 −0 lib/css/parser/property/font.treetop
  16. +23 −0 lib/css/parser/property/rectangle.treetop
  17. +4 −4 lib/css/{css_selector.treetop → parser/selector.treetop}
  18. +31 −0 lib/css/parser/stylesheet.treetop
  19. +29 −0 lib/css/parser/symbols.treetop
  20. +17 −0 lib/css/parser/units.treetop
  21. +1 −1 lib/css/{ → parser}/url.treetop
  22. +67 −0 lib/css/primitive.rb
  23. +53 −0 lib/css/property.rb
  24. +44 −0 lib/css/property/background.rb
  25. +25 −0 lib/css/property/border.rb
  26. +10 −0 lib/css/property/font.rb
  27. +40 −0 lib/css/property/rectangle.rb
  28. +14 −0 lib/css/ruleset.rb
  29. +15 −0 lib/css/stylesheet.rb
  30. +4 −0 lib/css/uri.rb
  31. +24 −9 lib/treetop_css.rb
  32. +0 −3 lib/treetop_css/version.rb
  33. +0 −22 test/css_declaration_test.rb
  34. +0 −59 test/css_primitives_test.rb
  35. +221 −0 test/primitives_test.rb
  36. +52 −0 test/property/property_background_test.rb
  37. +49 −0 test/property/property_border_test.rb
  38. +44 −0 test/property/property_font_test.rb
  39. +39 −0 test/property/property_rectangle_test.rb
  40. +54 −0 test/property_test.rb
  41. +18 −0 test/ruleset_test.rb
  42. +1 −1 test/{css_selector_test.rb → selector_test.rb}
  43. +15 −10 test/{css_test.rb → stylesheet_test.rb}
  44. +4 −2 test/test_helper.rb
View
39 NOTES
@@ -0,0 +1,39 @@
+h1 {
+ margin: 10px 0;
+}
+
+1. get all rulesets
+2. for each ruleset get all properties
+3. for each property get all values
+
+class Stylesheet
+ def rulesets
+ end
+end
+
+class Ruleset
+ def selectors
+ end
+
+ def properties
+ end
+end
+
+class Property
+ def name
+ end
+
+ def value
+ end
+end
+
+class ShorthandProperty < Property
+ def expanded_properties
+ end
+end
+
+class FontProperty < ShorthandProperty
+end
+
+class DimensionProperty < ShorthandProperty
+end
View
4 lib/css/color.rb
@@ -0,0 +1,4 @@
+module Css
+ module Color
+ end
+end
View
24 lib/css/css.treetop
@@ -1,24 +0,0 @@
-grammar CSS
- include CSSSelector
- include CSSDeclaration
-
- rule stylesheet
- (ruleset / media)*
- end
-
- rule media
- s? '@media' media_list '{' ruleset* '}' s?
- end
-
- rule media_list
- s? medium ( s? ',' s? medium )* s?
- end
-
- rule medium
- (identifier / '(' s declaration s ')' ) s? (medium)* s
- end
-
- rule ruleset
- s? selector ( s? ',' s? selector)* s? '{' s? ( declaration s? )* '}' s?
- end
-end
View
15 lib/css/css_declaration.treetop
@@ -1,15 +0,0 @@
-grammar CSSDeclaration
- include CSSExpression
-
- rule declaration
- property s ':' s expression? s priority? s (semicolon s)*
- end
-
- rule property
- '*'? s identifier
- end
-
- rule priority
- '!important'
- end
-end
View
23 lib/css/css_expression.treetop
@@ -1,23 +0,0 @@
-grammar CSSExpression
- include CSSPrimitives
-
- rule expression
- term (operator? term)*
- end
-
- rule term
- quoted_string / function / primitive_data / string / number / identifier
- end
-
- rule identifier
- [-]? [_a-zA-Z]+ [_a-zA-Z0-9-]*
- end
-
- rule function
- identifier ( [:.] identifier )* '(' [^)]* ')'
- end
-
- rule operator
- s ('/' / ',' / '=') s
- end
-end
View
12 lib/css/css_pre_parser.rb
@@ -1,12 +0,0 @@
-module CSSPreparser
- def parse(string)
- replace_comments(string)
- super
- end
-
- private
-
- def replace_comments(string)
- string.gsub!(/\/\*.*?\*\//m, '')
- end
-end
View
104 lib/css/css_primitives.treetop
@@ -1,104 +0,0 @@
-grammar CSSPrimitives
- include URL
- include CSSColor
-
- rule primitive_data
- length / percentage / color / space / url_declaration / angle / time / frequency
- end
-
- rule length
- sign? float length_unit
- end
-
- rule length_unit
- 'em' / 'ex' / 'px' / 'in' / 'cm' / 'mm' / 'pt' / 'pc'
- end
-
- rule percentage
- sign? number '%'
- end
-
- rule url_declaration
- 'url(' q? url q? ')'
- end
-
- rule angle
- sign? float angle_unit
- end
-
- rule angle_unit
- 'deg' / 'grad' / 'rad'
- end
-
- rule time
- positive_float time_unit
- end
-
- rule time_unit
- 'ms' / 's'
- end
-
- rule frequency
- positive_float frequency_unit
- end
-
- rule frequency_unit
- 'Hz' / 'kHz'
- end
-
- rule number
- sign? positive_number
- end
-
- rule positive_number
- [0-9]+
- end
-
- rule float
- number ('.' number)?
- end
-
- rule positive_float
- '+'? positive_number ('.' positive_number)?
- end
-
- rule quoted_string
- q [^"']* q
- end
-
- rule string
- [a-zA-Z\.\/]+
- end
-
- rule symbol
- semicolon / colon / sign / space
- end
-
- rule semicolon
- ';'
- end
-
- rule colon
- ':'
- end
-
- rule sign
- '+' / '-'
- end
-
- rule space
- ' '
- end
-
- rule q
- '"' / "'"
- end
-
- rule s
- [ \r\n\t]*
- end
-
- rule comment
- '/*' [.]* '*/'
- end
-end
View
11 lib/css/number.rb
@@ -0,0 +1,11 @@
+module Css
+ class Number < Treetop::Runtime::SyntaxNode
+ def value
+ @value ||= eval(text_value.gsub(/[^\d.+-]/, ''))
+ end
+
+ def to_s
+ value.to_s
+ end
+ end
+end
View
8 lib/css/css_color.treetop → lib/css/parser/colors.treetop
@@ -1,12 +1,12 @@
-grammar CSSColor
+grammar Colors
rule color
- color_keyword / hex_value / rgb
+ (color_keyword / hex_value / rgb)
end
rule color_keyword
'aqua' / 'black' / 'blue' / 'fuchsia' / 'gray' / 'green' / 'lime' /
'maroon' / 'navy' / 'olive' / 'purple' / 'red' / 'silver' / 'teal' /
- 'white' / 'yellow'
+ 'white' / 'yellow' / 'transparent'
end
rule rgb
@@ -18,7 +18,7 @@ grammar CSSColor
end
rule two_fifty_six_color
- number
+ integer
end
rule hex_value
View
16 lib/css/parser/pre_parser.rb
@@ -0,0 +1,16 @@
+module Css
+ module Parser
+ module PreParser
+ def parse(string)
+ replace_comments(string)
+ super
+ end
+
+ private
+
+ def replace_comments(string)
+ string.gsub!(/\/\*.*?\*\//m, '')
+ end
+ end
+ end
+end
View
86 lib/css/parser/primitives.treetop
@@ -0,0 +1,86 @@
+grammar Primitives
+ include Symbols
+ include Units
+ include URL
+ include Colors
+
+ rule primitive
+ uri / size / percentage / color / angle / time / frequency / signed_number / string
+ end
+
+ rule uri
+ 'url(' q? url q? ')' <Css::Primitive>
+ end
+
+ rule size
+ signed_number length_unit <Css::Primitive>
+ end
+
+ rule length
+ signed_number length_unit <Css::Primitive>
+ end
+
+ rule percentage
+ signed_number percent <Css::Primitive>
+ end
+
+ rule angle
+ signed_number angle_unit <Css::Primitive>
+ end
+
+ rule time
+ positive_number time_unit <Css::Primitive>
+ end
+
+ rule frequency
+ positive_number frequency_unit <Css::Primitive>
+ end
+
+ rule signed_number
+ sign? number <Css::Number>
+ end
+
+ rule number
+ (float / integer)
+ end
+
+ rule positive_number
+ (positive_float / positive_integer)
+ end
+
+ rule integer
+ sign? positive_integer
+ end
+
+ rule positive_integer
+ '+'? [0-9]+ <Css::Number>
+ end
+
+ rule float
+ integer '.' integer
+ end
+
+ rule positive_float
+ '+'? positive_integer '.' positive_integer <Css::Number>
+ end
+
+ rule function
+ identifier ( [:.] identifier )* '(' [^)]* ')'
+ end
+
+ rule identifier
+ [-]? [_a-zA-Z]+ [_a-zA-Z0-9-]*
+ end
+
+ rule quoted_string
+ q [^"']* q
+ end
+
+ rule string
+ [a-zA-Z\.\/]+
+ end
+
+ rule s
+ [ \r\n\t]*
+ end
+end
View
27 lib/css/parser/properties.treetop
@@ -0,0 +1,27 @@
+grammar Properties
+ include Primitives
+ include BackgroundProperty
+ include BorderProperty
+ include FontProperty
+ include RectangleProperty
+
+ rule properties
+ (font_property / font_family_property / background_property / border_property / rectangle_property / property)*
+ end
+
+ rule property
+ property_name s ':' s property_value s property_priority s (';' s)* <Css::Property>
+ end
+
+ rule property_value
+ function / quoted_string / identifier / primitive
+ end
+
+ rule property_name
+ '*'? s identifier
+ end
+
+ rule property_priority
+ '!important'?
+ end
+end
View
55 lib/css/parser/property/background.treetop
@@ -0,0 +1,55 @@
+grammar BackgroundProperty
+ include Primitives
+
+ rule background_property
+ name:background_property_name s ':' s
+ property_value:background_property_list s
+ property_priority s (';' s)* <Css::Property::Background>
+ end
+
+ rule background_property_name
+ 'background-color' / 'background-image' / 'background-repeat' /
+ 'background-attachment' / 'background-position' / 'background'
+ end
+
+ rule background_property_list
+ background_value background_value? background_value?
+ background_value? background_value? background_value?
+ end
+
+ rule background_value
+ background_color / background_image / background_repeat /
+ background_attachment / background_position
+ end
+
+ rule background_color
+ color s
+ end
+
+ rule background_image
+ uri s
+ end
+
+ rule background_repeat
+ '' background_repeat:('repeat-x' / 'repeat-y' / 'no-repeat' / 'repeat') s
+ end
+
+ rule background_attachment
+ '' background_attachment:('scroll' / 'fixed') s
+ end
+
+ rule background_position
+ background_horizontal_position s /
+ background_vertical_position s /
+ (background_horizontal_position s background_vertical_position s) /
+ (background_vertical_position s background_horizontal_position s)
+ end
+
+ rule background_horizontal_position
+ percentage / length / 'left' / 'center' / 'right'
+ end
+
+ rule background_vertical_position
+ percentage / length / 'top' / 'center' / 'bottom'
+ end
+end
View
40 lib/css/parser/property/border.treetop
@@ -0,0 +1,40 @@
+grammar BorderProperty
+ include Primitives
+
+ rule border_property
+ name:border_property_name s ':' s property_value:border_value_list s property_priority s (';' s)* <Css::Property::Border>
+ end
+
+ rule border_property_name
+ 'border-width' / 'border-color' / 'border-style' /
+ 'border-top' / 'border-right' / 'border-bottom' / 'border-left' /
+ 'border'
+ end
+
+ rule border_value_list
+ border_value border_value? border_value? border_value?
+ end
+
+ rule border_value
+ border_width / border_color / border_style
+ end
+
+ rule border_width
+ border_width_value s border_width_value s border_width_value s border_width_value s /
+ border_width_value s border_width_value s border_width_value s /
+ border_width_value s border_width_value s /
+ border_width_value s <Css::Property::Rectangle>
+ end
+
+ rule border_width_value
+ length / 'thin' / 'medium' / 'thick'
+ end
+
+ rule border_color
+ color s
+ end
+
+ rule border_style
+ '' border_style:('none' / 'hidden' / 'dotted' / 'dashed' / 'solid' / 'double' / 'groove' / 'ridge' / 'inset' / 'outset') s
+ end
+end
View
56 lib/css/parser/property/font.treetop
@@ -0,0 +1,56 @@
+grammar FontProperty
+ include Primitives
+
+ rule font_property
+ name:'font' s ':' s property_value:font_value_list property_priority s (';' s)* <Css::Property::Font>
+ end
+
+ rule font_family_property
+ name:'font-family' s ':' s property_value:font_family_list s property_priority s (';' s)* <Css::Property>
+ end
+
+ rule font_value_list
+ font_value (s font_value)* (s font_value)* (s font_value)* (s font_value)*
+ end
+
+ rule font_family_list
+ font_family (s ',' s font_family)* {
+ def value; font_family_list.text_value; end
+ }
+ end
+
+ rule font_value
+ font_weight / font_style / font_variant / font_size ('/' line_height)? / font_family_list
+ end
+
+ rule font_family
+ q? [a-zA-Z0-9\-_ ]+ q?
+ end
+
+ rule font_size
+ size / percentage / 'normal' / 'larger' / 'smaller' / 'xx-small' /
+ 'x-small' / 'small' / 'medium' / 'large' / 'x-large' / 'xx-large'
+ end
+
+ rule line_height
+ size / percentage / 'normal'
+ end
+
+ rule font_weight
+ integer / 'normal' / 'bold' / 'bolder'
+ end
+
+ rule font_style
+ 'normal' / 'italic' / 'oblique'
+ end
+
+ rule font_variant
+ 'normal' / 'small-caps'
+ end
+
+ rule font_stretch
+ 'normal' / 'wider' / 'narrower' / 'ultra-condensed' / 'extra-condensed' /
+ 'condensed' / 'semi-condensed' / 'semi-expanded' / 'expanded' /
+ 'extra-expanded' / 'ultra-expanded'
+ end
+end
View
23 lib/css/parser/property/rectangle.treetop
@@ -0,0 +1,23 @@
+grammar RectangleProperty
+ include Primitives
+
+ rule rectangle_property
+ name:rectangle_property_name s ':' s property_value:rectangle s property_priority s (';' s)* <Css::Property::Rectangle>
+ end
+
+ rule rectangle_property_name
+ 'margin' / 'padding'
+ end
+
+ rule rectangle
+ rectangle_value s rectangle_value s rectangle_value s rectangle_value s /
+ rectangle_value s rectangle_value s rectangle_value s /
+ rectangle_value s rectangle_value s /
+ rectangle_value s <Css::Property::Rectangle>
+ end
+
+ rule rectangle_value
+ length / percentage / signed_number
+ end
+
+end
View
8 lib/css/css_selector.treetop → lib/css/parser/selector.treetop
@@ -1,12 +1,12 @@
-grammar CSSSelector
- include CSSExpression
+grammar Selector
+ include Primitives
rule selector
- simple_selector s? combinator? s? selector?
+ simple_selector s combinator? s selector?
end
rule simple_selector
- tag_selector / property_selector+
+ s (tag_selector / property_selector+)
end
rule tag_selector
View
31 lib/css/parser/stylesheet.treetop
@@ -0,0 +1,31 @@
+grammar Stylesheet
+ include Selector
+
+ rule stylesheet
+ (ruleset / media)* <Css::Stylesheet>
+ end
+
+ rule ruleset
+ selectors '{' declaration '}' s
+ end
+
+ rule selectors
+ selector ( s ',' s selector)* s
+ end
+
+ rule media
+ s '@media' media_list '{' ruleset* '}' s
+ end
+
+ rule media_list
+ s medium ( s ',' s medium )* s
+ end
+
+ rule medium
+ (identifier / '(' s [^)]* s ')' ) s (medium)* s
+ end
+
+ rule declaration
+ s [^}]*
+ end
+end
View
29 lib/css/parser/symbols.treetop
@@ -0,0 +1,29 @@
+grammar Symbols
+ rule symbol
+ semicolon / colon / sign / space
+ end
+
+ rule semicolon
+ ';'
+ end
+
+ rule colon
+ ':'
+ end
+
+ rule sign
+ '+' / '-'
+ end
+
+ rule percent
+ '%'
+ end
+
+ rule space
+ ' '
+ end
+
+ rule q
+ '"' / "'"
+ end
+end
View
17 lib/css/parser/units.treetop
@@ -0,0 +1,17 @@
+grammar Units
+ rule length_unit
+ 'em' / 'ex' / 'px' / 'in' / 'cm' / 'mm' / 'pt' / 'pc'
+ end
+
+ rule angle_unit
+ 'deg' / 'grad' / 'rad'
+ end
+
+ rule time_unit
+ 'ms' / 's'
+ end
+
+ rule frequency_unit
+ 'Hz' / 'kHz'
+ end
+end
View
2 lib/css/url.treetop → lib/css/parser/url.treetop
@@ -14,7 +14,7 @@ grammar URL
end
rule port_number
- positive_number
+ positive_integer
end
rule domain
View
67 lib/css/primitive.rb
@@ -0,0 +1,67 @@
+module Css
+ class Primitive < Treetop::Runtime::SyntaxNode
+ NUMBER_TYPES = [:signed_number, :positive_number, :number, :integer, :positive_float, :float]
+ UNIT_TYPES = [:length_unit, :angle_unit, :time_unit, :frequency_unit, :percent]
+
+ def type
+ case true
+ when respond_to?(:url)
+ :reference
+ # when respond_to?(:color)
+ # :color
+ when respond_to?(:length_unit)
+ :length
+ when respond_to?(:percent)
+ :percentage
+ when respond_to?(:angle_unit)
+ :angle
+ when respond_to?(:time_unit)
+ :time
+ when respond_to?(:frequency_unit)
+ :frequency
+ end
+ end
+
+ def value
+ case type
+ when :length, :percentage, :angle, :time, :frequency, :float
+ number
+ end
+ end
+
+ def unit
+ case type
+ when :length, :percentage, :angle, :time, :frequency
+ number
+ end
+ end
+
+ def to_s
+ @string ||= [value, unit].compact.join
+ end
+
+ def number_type
+ @number_type ||= NUMBER_TYPES.detect { |type| respond_to?(type) }
+ end
+
+ def number?
+ !!number_type
+ end
+
+ def number
+ @number ||= send(number_type).value if number?
+ end
+
+ def unit_type
+ @unit_type ||= UNIT_TYPES.detect { |type| respond_to?(type) }
+ end
+
+ def unit?
+ !!unit_type
+ end
+
+ def unit
+ @unit ||= send(unit_type).text_value if unit?
+ end
+ end
+end
View
53 lib/css/property.rb
@@ -0,0 +1,53 @@
+module Css
+ module Property
+ autoload :Background, 'css/property/background'
+ autoload :Border, 'css/property/border'
+ autoload :Font, 'css/property/font'
+ autoload :Rectangle, 'css/property/rectangle'
+
+ def name
+ property_name.text_value
+ end
+
+ def priority
+ property_priority.text_value
+ end
+
+ def value
+ property_value.text_value
+ end
+
+ def value_nodes
+ @value_nodes ||= property_value.elements.reject { |element| element.text_value.empty? }
+ end
+
+ def numeric_value
+ property_value.number if property_value.number?
+ end
+
+ def unit
+ property_value.unit
+ end
+
+ protected
+
+ def find_node(method)
+ value_nodes.detect { |node| parser_methods(node).include?(method) }
+ end
+
+ def parser_methods(node)
+ modules = node.extension_modules
+ node.methods - [modules.last ? modules.last.methods : nil] - node.class.instance_methods
+ end
+
+ def expand_property(result, type, name, value)
+ result[:"#{type}#{name[0].upcase}#{name[1..-1]}"] = parse_property(type, name, value) if value
+ result
+ end
+
+ def parse_property(type, name, value)
+ value = Array(value).compact.map { |value| value.text_value.strip }.join(' ')
+ PropertiesParser.new.parse("#{type}-#{name}: #{value}").elements.first
+ end
+ end
+end
View
44 lib/css/property/background.rb
@@ -0,0 +1,44 @@
+module Css
+ module Property
+ module Background
+ include Property
+
+ def expand_properties
+ [:color, :image, :repeat, :attachment, :position].inject({}) do |result, name|
+ if value = send(name)
+ property = expand_property(result, 'background', name, value)
+ end
+ result
+ end
+ end
+
+ def color
+ @color ||= find_node(:color)
+ end
+
+ def image
+ @image ||=find_node(:uri)
+ end
+
+ def repeat
+ @repeat ||= find_node(:background_repeat)
+ end
+
+ def attachment
+ @attachment ||=find_node(:background_attachment)
+ end
+
+ def position
+ @position ||= [vertical_position, horizontal_position].compact
+ end
+
+ def vertical_position
+ find_node(:background_vertical_position)
+ end
+
+ def horizontal_position
+ find_node(:background_horizontal_position)
+ end
+ end
+ end
+end
View
25 lib/css/property/border.rb
@@ -0,0 +1,25 @@
+module Css
+ module Property
+ module Border
+ include Property
+
+ def expand_properties
+ [:color, :style, :width].inject({}) do |result, name|
+ expand_property(result, 'border', name, send(name))
+ end
+ end
+
+ def color
+ @color ||= find_node(:color)
+ end
+
+ def style
+ @style ||= find_node(:border_style)
+ end
+
+ def width
+ @width ||= find_node(:border_width_value)
+ end
+ end
+ end
+end
View
10 lib/css/property/font.rb
@@ -0,0 +1,10 @@
+module Css
+ module Property
+ module Font
+ include Property
+
+ def expand_properties
+ end
+ end
+ end
+end
View
40 lib/css/property/rectangle.rb
@@ -0,0 +1,40 @@
+module Css
+ module Property
+ module Rectangle
+ def top
+ expanded_values[0]
+ end
+
+ def right
+ expanded_values[1]
+ end
+
+ def bottom
+ expanded_values[2]
+ end
+
+ def left
+ expanded_values[3]
+ end
+
+ def values
+ @values ||= property_value.elements.map do |element|
+ element.text_value unless element.text_value.strip.empty?
+ end.compact
+ end
+
+ def expanded_values
+ case values.size
+ when 1
+ values * 4
+ when 2
+ values * 2
+ when 3
+ values + [values[1]]
+ when 4
+ values
+ end
+ end
+ end
+ end
+end
View
14 lib/css/ruleset.rb
@@ -0,0 +1,14 @@
+module Css
+ class Ruleset
+ attr_reader :selectors, :declaration
+
+ def initialize(selectors, declaration)
+ @selectors = selectors
+ @declaration = declaration
+ end
+
+ def properties
+ @properties ||= PropertiesParser.new.parse(declaration).elements
+ end
+ end
+end
View
15 lib/css/stylesheet.rb
@@ -0,0 +1,15 @@
+module Css
+ class Stylesheet < Treetop::Runtime::SyntaxNode
+ class << self
+ def parse(css)
+ StylesheetParser.new.parse(css)
+ end
+ end
+
+ def rulesets
+ elements.map do |element|
+ Ruleset.new(element.selectors, element.declaration) if element.respond_to?(:declaration)
+ end
+ end
+ end
+end
View
4 lib/css/uri.rb
@@ -0,0 +1,4 @@
+module Css
+ module Uri
+ end
+end
View
33 lib/treetop_css.rb
@@ -1,13 +1,28 @@
require "rubygems"
require "treetop"
-require File.dirname(__FILE__) + "/css/css_pre_parser"
+require File.dirname(__FILE__) + "/css/parser/pre_parser"
-Treetop.load File.dirname(__FILE__) + "/css/url"
-Treetop.load File.dirname(__FILE__) + "/css/css_color"
-Treetop.load File.dirname(__FILE__) + "/css/css_primitives"
-Treetop.load File.dirname(__FILE__) + "/css/css_expression"
-Treetop.load File.dirname(__FILE__) + "/css/css_declaration"
-Treetop.load File.dirname(__FILE__) + "/css/css_selector"
-Treetop.load File.dirname(__FILE__) + "/css/css"
+Treetop.load File.dirname(__FILE__) + "/css/parser/colors"
+Treetop.load File.dirname(__FILE__) + "/css/parser/symbols"
+Treetop.load File.dirname(__FILE__) + "/css/parser/units"
+Treetop.load File.dirname(__FILE__) + "/css/parser/url"
+Treetop.load File.dirname(__FILE__) + "/css/parser/primitives"
+Treetop.load File.dirname(__FILE__) + "/css/parser/property/border.treetop"
+Treetop.load File.dirname(__FILE__) + "/css/parser/property/background.treetop"
+Treetop.load File.dirname(__FILE__) + "/css/parser/property/rectangle.treetop"
+Treetop.load File.dirname(__FILE__) + "/css/parser/property/font.treetop"
+Treetop.load File.dirname(__FILE__) + "/css/parser/properties"
+Treetop.load File.dirname(__FILE__) + "/css/parser/selector"
+Treetop.load File.dirname(__FILE__) + "/css/parser/stylesheet"
-CSSParser.send(:include, CSSPreparser)
+StylesheetParser.send(:include, Css::Parser::PreParser)
+
+module Css
+ autoload :Color, 'css/color'
+ autoload :Number, 'css/number'
+ autoload :Primitive, 'css/primitive'
+ autoload :Property, 'css/property'
+ autoload :Ruleset, 'css/ruleset'
+ autoload :Stylesheet, 'css/stylesheet'
+ autoload :Uri, 'css/uri'
+end
View
3 lib/treetop_css/version.rb
@@ -1,3 +0,0 @@
-module TreetopCss
- VERSION = "0.0.1"
-end
View
22 test/css_declaration_test.rb
@@ -1,22 +0,0 @@
-require File.expand_path('../test_helper', __FILE__)
-
-class CssDeclarationParserTest < Test::Unit::TestCase
- PARSER = CSSDeclarationParser
-
- test "declaration" do
- assert_parses 'font-size: 10pt'
- assert_parses 'font-size: 10pt;'
- assert_parses '-moz-border-radius-topright: 2px !important;'
- assert_parses 'background: #efd url(/images/foo.gif) no-repeat left top;'
- assert_parses '*display:inline'
- assert_parses 'background-position:;'
- assert_parses 'font-family: "lucida grande", helvetica, sans-serif;'
- assert_parses 'opacity:.8;'
- assert_parses 'filter:Alpha(Opacity=80);'
- assert_parses '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";'
- assert_parses "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='img/3.0/video/416_player.png');"
- assert_parses 'padding:15px 20px 17px 20px;;'
- assert_parses 'padding:15px 20px 17px 20px; ;'
- # assert_parses "content: '»';"
- end
-end
View
59 test/css_primitives_test.rb
@@ -1,59 +0,0 @@
-require File.expand_path('../test_helper', __FILE__)
-
-class CssPrimitivesParserTest < Test::Unit::TestCase
- PARSER = CSSPrimitivesParser
-
- test "3-digit hex color" do
- assert_parses '#333'
- end
-
- test "6-digit hex color" do
- assert_parses '#333333'
- end
-
- test "rgb color" do
- assert_parses 'rgb( 2, 0, 0 )', 'rgb(2, 0, 0)', 'rgb(2,0,0)'
- end
-
- test "rgb color percentage" do
- assert_parses 'rgb(10%, 20%, 100%)'
- end
-
- test "named colors" do
- assert_parses *%w(aqua black blue fuchsia gray green lime maroon navy olive purple red silver teal white yellow)
- end
-
- test "lengths" do
- assert_parses *%w(0.5em 1em -1em 1ex 1px -134.4px 0.5in 2cm 4mm 12pt 1pc)
- end
-
- test "degrees" do
- assert_parses *%w(10deg 10grad 10rad -10deg 1234.45deg +10deg)
- end
-
- test "times" do
- assert_parses *%w(10ms 10s 11.2s)
- end
-
- test "frequencies" do
- assert_parses *%w(10Hz 10kHz +10Hz 11.2Hz)
- end
-
- test "percentages" do
- assert_parses *%w(0% 10% -10% 100%)
- end
-
- test "urls" do
- assert_parses *%w(
- url('http://google.com')
- url("http://google.com")
- url(http://google.com)
- url(http://google.com/foo/bar/baz)
- url(/foo/bar.gif)
- url(http://localhost:3000)
- url(http://localhost:3000/foo?bar=quxx&foo=bar&one=two)
- url(http://www.w3.org)
- )
- # /StyleSheets/TR/logo-CR
- end
-end
View
221 test/primitives_test.rb
@@ -0,0 +1,221 @@
+require File.expand_path('../test_helper', __FILE__)
+
+class CssPrimitivesParserTest < Test::Unit::TestCase
+ PARSER = PrimitivesParser
+
+ def primitive(string)
+ PrimitivesParser.new.parse(string)
+ end
+
+ test "primitive formatting :float" do
+ assert_equal '0.55', primitive('0.55').to_s
+ end
+
+ test "primitive type :url" do
+ assert_equal :reference, primitive('url(http://google.com)').type
+ end
+
+ # PERCENTAGE
+
+ test "primitive type :percentage" do
+ assert_equal :percentage, primitive('2%').type
+ end
+
+ test "primitive value :percentage (integer)" do
+ assert_equal 2, primitive('2%').value
+ end
+
+ test "primitive value :percentage (float)" do
+ assert_equal 2.0, primitive('2.0%').value
+ end
+
+ test "primitive unit % :percentage" do
+ assert_equal '%', primitive('2%').unit
+ end
+
+ %w(0% 10% -10% 100% 0.1% -0.1%).each do |value|
+ test "primitive #{value} to_s :percentage" do
+ assert_equal value, primitive(value).to_s
+ end
+ end
+
+ test "primitive +10% to_s :percentage" do
+ assert_equal '10%', primitive('+10%').to_s
+ end
+
+ test "primitive +0.1% to_s :percentage" do
+ assert_equal '0.1%', primitive('+0.1%').to_s
+ end
+
+ # LENGTH
+
+ test "primitive type :length" do
+ assert_equal :length, primitive('2px').type
+ end
+
+ test "primitive value :length (integer)" do
+ assert_equal 2, primitive('2px').value
+ end
+
+ test "primitive value :length (float)" do
+ assert_equal 2.0, primitive('2.0pt').value
+ end
+
+ test "primitive unit em :length" do
+ assert_equal 'em', primitive('2em').unit
+ end
+
+ test "primitive unit ex :length" do
+ assert_equal 'ex', primitive('2ex').unit
+ end
+
+ test "primitive unit px :length" do
+ assert_equal 'px', primitive('2px').unit
+ end
+
+ test "primitive unit in :length" do
+ assert_equal 'in', primitive('2in').unit
+ end
+
+ test "primitive unit cm :length" do
+ assert_equal 'cm', primitive('2cm').unit
+ end
+
+ test "primitive unit mm :length" do
+ assert_equal 'mm', primitive('2mm').unit
+ end
+
+ test "primitive unit pt :length" do
+ assert_equal 'pt', primitive('2pt').unit
+ end
+
+ test "primitive unit pc :length" do
+ assert_equal 'pc', primitive('2pc').unit
+ end
+
+ # ANGLE
+
+ test "primitive type :angle" do
+ assert_equal :angle, primitive('2deg').type
+ end
+
+ test "primitive value :angle (integer)" do
+ assert_equal 2, primitive('2deg').value
+ end
+
+ test "primitive value :angle (float)" do
+ assert_equal 2.0, primitive('2.0deg').value
+ end
+
+ test "primitive unit deg :angle" do
+ assert_equal 'deg', primitive('2deg').unit
+ end
+
+ test "primitive unit grad :angle" do
+ assert_equal 'grad', primitive('2grad').unit
+ end
+
+ test "primitive unit rad :angle" do
+ assert_equal 'rad', primitive('2rad').unit
+ end
+
+ # FREQUENCY
+
+ test "primitive type :frequency" do
+ assert_equal :frequency, primitive('2kHz').type
+ end
+
+ test "primitive value :frequency (integer)" do
+ assert_equal 2, primitive('2kHz').value
+ end
+
+ test "primitive value :frequency (float)" do
+ assert_equal 2.0, primitive('2.0kHz').value
+ end
+
+ test "primitive unit Hz :frequency" do
+ assert_equal 'Hz', primitive('2Hz').unit
+ end
+
+ test "primitive unit kHz :frequency" do
+ assert_equal 'kHz', primitive('2kHz').unit
+ end
+
+ # TIME
+
+ test "primitive type :time" do
+ assert_equal :time, primitive('2s').type
+ end
+
+ test "primitive value :time (integer)" do
+ assert_equal 2, primitive('2s').value
+ end
+
+ test "primitive value :time (float)" do
+ assert_equal 2.0, primitive('2.0s').value
+ end
+
+ test "primitive unit s :time" do
+ assert_equal 's', primitive('2s').unit
+ end
+
+ test "primitive unit ms :time" do
+ assert_equal 'ms', primitive('2ms').unit
+ end
+
+ # PARSING
+
+ test "3-digit hex color" do
+ assert_parses '#333'
+ end
+
+ test "6-digit hex color" do
+ assert_parses '#333333'
+ end
+
+ test "rgb color" do
+ assert_parses 'rgb( 2, 0, 0 )', 'rgb(2, 0, 0)', 'rgb(2,0,0)'
+ end
+
+ test "rgb color percentage" do
+ assert_parses 'rgb(10%, 20%, 100%)'
+ end
+
+ test "named colors" do
+ assert_parses *%w(aqua black blue fuchsia gray green lime maroon navy olive purple red silver teal white yellow)
+ end
+
+ test "lengths" do
+ assert_parses *%w(0.5em 1em -1em 1ex 1px -134.4px 0.5in 2cm 4mm 12pt 1pc)
+ end
+
+ test "degrees" do
+ assert_parses *%w(10deg 10grad 10rad -10deg 1234.45deg +10deg)
+ end
+
+ test "times" do
+ assert_parses *%w(10ms 10s 11.2s)
+ end
+
+ test "frequencies" do
+ assert_parses *%w(10Hz 10kHz +10Hz 11.2Hz)
+ end
+
+ test "percentages" do
+ assert_parses *%w(0% 10% -10% 100% 0.1% -0.1%)
+ end
+
+ test "urls" do
+ assert_parses *%w(
+ url('http://google.com')
+ url("http://google.com")
+ url(http://google.com)
+ url(http://google.com/foo/bar/baz)
+ url(/foo/bar.gif)
+ url(http://localhost:3000)
+ url(http://localhost:3000/foo?bar=quxx&foo=bar&one=two)
+ url(http://www.w3.org)
+ )
+ # /StyleSheets/TR/logo-CR
+ end
+end
View
52 test/property/property_background_test.rb
@@ -0,0 +1,52 @@
+require File.expand_path('../../test_helper', __FILE__)
+
+class PropertyBackgroundTest < Test::Unit::TestCase
+ def property(string)
+ Css::Ruleset.new('h1', string).properties.first
+ end
+
+ test "background-position" do
+ assert_equal '2px', property('background-position: 2px;').value
+ assert_equal 'top', property('background-position: top;').value
+ assert_equal '2px -2%', property('background-position: 2px -2%;').value
+ assert_equal 'top left', property('background-position: top left;').value
+ assert_equal 'left top', property('background-position: left top;').value
+ end
+
+ test "background-color" do
+ assert_equal '#ccc', property('background-color: #ccc;').value
+ end
+
+ test "background-image" do
+ assert_equal 'url("http://example.org/logo.gif")', property('background-image: url("http://example.org/logo.gif");').value
+ end
+
+ test "background-repeat" do
+ assert_equal 'no-repeat', property('background-repeat: no-repeat;').value
+ assert_equal 'repeat-x', property('background-repeat: repeat-x;').value
+ assert_equal 'repeat-y', property('background-repeat: repeat-y;').value
+ assert_equal 'repeat', property('background-repeat: repeat;').value
+ end
+
+ test "background-attachment" do
+ assert_equal 'fixed', property('background-attachment: fixed;').value
+ end
+
+ backgrounds = [ '2px', 'no-repeat', '#ccc', 'url("http://example.org/logo.gif")',
+ 'transparent url("http://example.org/logo.gif") no-repeat top bottom' ]
+ backgrounds.each do |background|
+ test "background #{background}" do
+ assert_equal background, property("background: #{background};").value
+ end
+ end
+
+ test "expand background properties" do
+ background = property('background: transparent url("http://example.org/logo.gif") no-repeat top left scroll')
+ properties = background.expand_properties
+ assert_equal 'url("http://example.org/logo.gif")', properties[:backgroundImage].value
+ assert_equal 'transparent', properties[:backgroundColor].value
+ assert_equal 'no-repeat', properties[:backgroundRepeat].value
+ assert_equal 'top left', properties[:backgroundPosition].value
+ assert_equal 'scroll', properties[:backgroundAttachment].value
+ end
+end
View
49 test/property/property_border_test.rb
@@ -0,0 +1,49 @@
+require File.expand_path('../../test_helper', __FILE__)
+
+class PropertyBorderTest < Test::Unit::TestCase
+ def property(string)
+ Css::Ruleset.new('h1', string).properties.first
+ end
+
+ ['1px', '1px 2px', '1px 2px 3px', '1px 2px 3px 4px'].each do |width|
+ test "border-width #{width}" do
+ assert_equal width, property("border-width: #{width};").value
+ end
+ end
+
+ test "border-top-width" do
+ assert_equal '10px', property("border-top-width: 10px;").value
+ end
+
+ test "border-color" do
+ assert_equal '#333', property("border-top-width: #333;").value
+ end
+
+ test "border-top-color" do
+ assert_equal '#333', property("border-top-width: #333;").value
+ end
+
+ test "border-style" do
+ assert_equal 'dotted', property("border-top-width: dotted;").value
+ end
+
+ test "border-top-style" do
+ assert_equal 'dotted', property("border-top-width: dotted;").value
+ end
+
+ test "border-top" do
+ assert_equal '1px solid red', property("border-top: 1px solid red;").value
+ end
+
+ test "border" do
+ assert_equal '1px solid red', property("border: 1px solid red;").value
+ end
+
+ test "expand border properties" do
+ properties = property('border: 1px solid red;').expand_properties
+
+ assert_equal 'solid', properties[:borderStyle].value
+ assert_equal '1px', properties[:borderWidth].value
+ assert_equal 'red', properties[:borderColor].value
+ end
+end
View
44 test/property/property_font_test.rb
@@ -0,0 +1,44 @@
+require File.expand_path('../../test_helper', __FILE__)
+
+class PropertyFontTest < Test::Unit::TestCase
+ def property(string)
+ Css::Ruleset.new('h1', string).properties.first
+ end
+
+ test "font-family property" do
+ assert_equal 'Helvetica', property('font-family: Helvetica;').value
+ end
+
+ test "font-family property quoted value" do
+ assert_equal '"Helvetica"', property('font-family: "Helvetica";').value
+ end
+
+ test "font shorthand family" do
+ assert_equal 'Helvetica', property('font: Helvetica;').value
+ assert_equal '"Helvetica"', property('font: "Helvetica";').value
+ assert_equal '"Helvetica", sans-serif', property('font: "Helvetica", sans-serif;').value
+ end
+
+ test "font shorthand size" do
+ assert_equal 'normal', property('font: normal;').value
+ assert_equal '14px', property('font: 14px;').value
+ # assert_equal '14px/14px', property('font: 14px/14px;').value
+ # assert_equal 'normal/normal', property('font: normal/normal;').value
+ # assert_equal '14px/normal', property('font: 14px/normal;').value
+ # assert_equal 'normal/14px', property('font: normal/14px;').value
+ end
+
+ test "font shorthand weight" do
+ assert_equal 'bold', property('font: bold;').value
+ assert_equal '900', property('font: 900;').value
+ end
+
+ test "font shorthand style" do
+ assert_equal 'normal', property('font: normal;').value
+ assert_equal 'italic', property('font: italic;').value
+ end
+
+ test "font shorthand combination" do
+ assert_equal '300 italic medium/14px "Helvetica", sans-serif', property('font: 300 italic medium/14px "Helvetica", sans-serif;').value
+ end
+end
View
39 test/property/property_rectangle_test.rb
@@ -0,0 +1,39 @@
+require File.expand_path('../../test_helper', __FILE__)
+
+class PropertyRectangleTest < Test::Unit::TestCase
+ def rectangle(str)
+ Css::Ruleset.new('h1', str).properties.first
+ end
+
+ test "rectangle w/ 1 value" do
+ rectangle = rectangle('margin: 10px;')
+ assert_equal '10px', rectangle.top.to_s
+ assert_equal '10px', rectangle.bottom.to_s
+ assert_equal '10px', rectangle.right.to_s
+ assert_equal '10px', rectangle.left.to_s
+ end
+
+ test "rectangle w/ 2 values" do
+ rectangle = rectangle('margin: 10px 0;')
+ assert_equal '10px', rectangle.top.to_s
+ assert_equal '10px', rectangle.bottom.to_s
+ assert_equal '0', rectangle.right.to_s
+ assert_equal '0', rectangle.left.to_s
+ end
+
+ test "rectangle w/ 3 values" do
+ rectangle = rectangle('margin: 10px 5% 0;')
+ assert_equal '10px', rectangle.top.to_s
+ assert_equal '0', rectangle.bottom.to_s
+ assert_equal '5%', rectangle.right.to_s
+ assert_equal '5%', rectangle.left.to_s
+ end
+
+ test "rectangle w/ 4 values" do
+ rectangle = rectangle('margin: 1px 2px 3px 4px;')
+ assert_equal '1px', rectangle.top.to_s
+ assert_equal '2px', rectangle.right.to_s
+ assert_equal '3px', rectangle.bottom.to_s
+ assert_equal '4px', rectangle.left.to_s
+ end
+end
View
54 test/property_test.rb
@@ -0,0 +1,54 @@
+require File.expand_path('../test_helper', __FILE__)
+
+class PropertyTest < Test::Unit::TestCase
+ PARSER = PropertiesParser
+
+ def property(string)
+ Css::Ruleset.new('h1', string).properties.first
+ end
+
+ test "parses individual declarations" do
+ assert_parses 'font-size: 10pt'
+ assert_parses 'font-size: 10pt;'
+ assert_parses '-moz-border-radius-topright: 2px !important;'
+ assert_parses 'background: #efd top;'
+ assert_parses 'background: #efd url(/images/foo.gif) no-repeat left top;'
+ assert_parses '*display:inline'
+ # assert_parses 'background-position:;'
+ assert_parses 'font-family: "lucida grande", helvetica, sans-serif;'
+ # assert_parses 'opacity:.8;'
+ assert_parses 'filter:Alpha(Opacity=80);'
+ assert_parses '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";'
+ assert_parses "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='img/3.0/video/416_player.png');"
+ assert_parses 'padding:15px 20px 17px 20px;;'
+ assert_parses 'padding:15px 20px 17px 20px; ;'
+ assert_parses 'font: medium/14px;'
+ assert_parses 'font: verdana, helvetica, sans-serif;'
+ assert_parses 'font: 300 italic medium/14px verdana, helvetica, sans-serif;'
+ # assert_parses "content: '»';"
+ end
+
+ test "parses multiple declarations" do
+ assert_parses 'padding: 15px; font-size: 10pt;'
+ end
+
+ test "property name" do
+ assert_equal 'width', property('width: 10px;').name
+ end
+
+ test "property value" do
+ assert_equal '10px', property('width: 10px;').value
+ end
+
+ test "property numeric_value" do
+ assert_equal 10, property('width: 10px;').numeric_value
+ end
+
+ test "property unit" do
+ assert_equal 'px', property('width: 10px;').unit
+ end
+
+ test "property priority" do
+ assert_equal '!important', property('width: 10px !important;').priority
+ end
+end
View
18 test/ruleset_test.rb
@@ -0,0 +1,18 @@
+require File.expand_path('../test_helper', __FILE__)
+
+class RulesetTest < Test::Unit::TestCase
+ test "selectors" do
+ ruleset = Css::Ruleset.new('h1', 'width: 10px !important;')
+ assert_equal 'h1', ruleset.selectors
+ end
+
+ test "declaration" do
+ ruleset = Css::Ruleset.new('h1', 'width: 10px !important;')
+ assert_equal 'width: 10px !important;', ruleset.declaration
+ end
+
+ test "properties" do
+ property = Css::Ruleset.new('h1', 'width: 10px !important;').properties.first
+ assert_equal 'width: 10px !important;', property.text_value
+ end
+end
View
2 test/css_selector_test.rb → test/selector_test.rb
@@ -1,7 +1,7 @@
require File.expand_path('../test_helper', __FILE__)
class CssSelectorParserTest < Test::Unit::TestCase
- PARSER = CSSSelectorParser
+ PARSER = SelectorParser
test "selector" do
assert_parses *%w(
View
25 test/css_test.rb → test/stylesheet_test.rb
@@ -1,37 +1,42 @@
require File.expand_path('../test_helper', __FILE__)
-class CssParserTest < Test::Unit::TestCase
- PARSER = CSSParser
+class StylesheetTest < Test::Unit::TestCase
+ PARSER = StylesheetParser
test "parses a single ruleset" do
- assert_parses 'h1 { font-size: 10pt; }'
+ assert_parses ' h1 { font-size: 10pt; } '
end
-
+
+ test "returns rulesets" do
+ assert Css::Stylesheet.parse('h1 { font-size: 10pt; }').rulesets[0].respond_to?(:selectors)
+ end
+
test "parses an empty single ruleset" do
- assert_parses '.p_signup { }'
+ assert_parses 'h1 { }'
end
-
- test "parses a single wicked rulesets" do
+
+ test "parses a single wicked ruleset" do
assert_parses <<-css
h1#foo:nth-child(1) > :visited * * p.bar ~ span, p, span {
- font-size: 10pt; /* some comment here */
+ font-size: 10pt; /* some comment */
-moz-border-radius-topright: 2px !important;
background: #efd url(/images/foo.gif) no-repeat left top;
}
css
end
- test "parses a multiple rulesets" do
+ test "parses multiple rulesets" do
assert_parses <<-css
h1 {
font-size: 10pt;
}
span {
font-size: 10pt;
+ font-size: 10pt;
}
css
end
-
+
test "parses a media ruleset" do
assert_parses <<-css
@media all {
View
6 test/test_helper.rb
@@ -1,4 +1,4 @@
-$: << File.expand_path('../../lib', __FILE__)
+$:.unshift File.expand_path('../../lib', __FILE__)
require 'rubygems'
require 'test/unit'
@@ -28,7 +28,9 @@ class Test::Unit::TestCase
def assert_parses(*strings)
strings.each do |string|
- css = self.class::PARSER.new.parse(string)
+ parser = self.class::PARSER.new
+ css = parser.parse(string)
+ # p parser.failure_reason
assert(css, "could not parse #{truncate(string).inspect}")
assert_equal(string, css.text_value)
end

0 comments on commit 3215a6a

Please sign in to comment.