Question

I'm writing a simple ruby parser for CSS files and I'm kind of stumped on how I can get a block of CSS (that is, a selector and everything between curly braces immediately following it) as a ruby object on which I can perform my dark and nasty magic.

Ideally, I would like to get a ruby hash with each attribute / value and the selector. Is there any clear and easy to understand way how to do this?

Was it helpful?

Solution

As mentioned in the comments it's silly to do it yourself beyond a fun exercise. I'd read the css file into a string and then scan it with regular expressions.

Ruby - Convert File to String

http://www.ruby-doc.org/core-1.9.3/String.html#method-i-scan

for testing the regex: http://rubular.com/

OTHER TIPS

Everyone should write a parser.

This should get you started...

require 'parslet'
require 'ostruct'
require 'pp'

class Parser < Parslet::Parser
  rule(:opencurl)   { str('{') }
  rule(:closecurl)  { str('}') }
  rule(:space)      { str(' ') }
  rule(:space?)     { space.maybe }
  rule(:comma)      { str(',') }
  rule(:semi)       { str(';') }
  rule(:colon)      { str(':') }
  rule(:eol)        { str("\r").maybe >> str("\n") }
  rule(:eol?)       { eol.maybe }
  rule(:indent?)    { str(" ").repeat(0) }
  rule(:ws?)        {indent? >> eol? >> indent?}

  rule(:value)      { (semi.absent? >> any).repeat(1).as(:value) } #cheating

  rule(:word)       { match['a-zA-Z0-9'].repeat(1) }
  rule(:property)   { (word >> (str("-") >> word).repeat(0)).as(:property) }
  rule(:setting)    { (indent? >> property >> colon >> indent? >> value >> semi  >> eol?) }
  rule(:body?)      { setting.repeat(0).as(:body) }
  rule(:block)      { opencurl >> ws? >> body? >> ws? >> closecurl >> ws? }
  rule(:selector_expression)   { (opencurl.absent? >> any).repeat(1) } #cheating
  rule(:scope)      { ws? >> selector_expression.as("selector") >> indent? >> block.as(:settings) }

  rule(:css?)       { (eol.repeat(0) >> scope).repeat(0) } 
  root(:css?)
end  

class MyTransform < Parslet::Transform
  rule(:property => simple(:p), :value => simple(:v)) { OpenStruct.new(p:p,v:v) }
  rule(:body => sequence(:b)) { b.each_with_object({}){|i,o| o[i.p] = i.v} }
end 

css = <<-css
  h2{ background-image: url(abc);}
  #bob { 
  background-image: url(abc);
  background-color: red;
}
css

par = Parser.new.parse(css)
tar = MyTransform.new.apply(par) 
pp tar

Outputs:

[{"selector"=>"h2", :settings=>{"background-image"=>"url(abc)"}},
 {"selector"=>"#bob ",
  :settings=>
   {"background-image"=>"url(abc)", "background-color"=>"red"}}]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top