csscss 0.0.1 → 0.1.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 (53) hide show
  1. data/.gitignore +0 -1
  2. data/CHANGELOG.md +3 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +39 -0
  5. data/README.md +47 -12
  6. data/bin/csscss +0 -1
  7. data/csscss.gemspec +2 -3
  8. data/lib/csscss/cli.rb +10 -1
  9. data/lib/csscss/json_reporter.rb +17 -0
  10. data/lib/csscss/parser/background.rb +72 -0
  11. data/lib/csscss/parser/base.rb +13 -0
  12. data/lib/csscss/parser/border.rb +45 -0
  13. data/lib/csscss/parser/border_color.rb +41 -0
  14. data/lib/csscss/parser/border_side.rb +91 -0
  15. data/lib/csscss/parser/border_style.rb +41 -0
  16. data/lib/csscss/parser/border_width.rb +37 -0
  17. data/lib/csscss/parser/color.rb +53 -0
  18. data/lib/csscss/parser/common.rb +86 -0
  19. data/lib/csscss/parser/css.rb +88 -0
  20. data/lib/csscss/parser/font.rb +96 -0
  21. data/lib/csscss/parser/list_style.rb +44 -0
  22. data/lib/csscss/parser/margin.rb +32 -0
  23. data/lib/csscss/parser/multi_side_transformer.rb +47 -0
  24. data/lib/csscss/parser/outline.rb +45 -0
  25. data/lib/csscss/parser/padding.rb +32 -0
  26. data/lib/csscss/parslet_optimizations.rb +77 -0
  27. data/lib/csscss/redundancy_analyzer.rb +75 -8
  28. data/lib/csscss/reporter.rb +2 -4
  29. data/lib/csscss/types.rb +77 -2
  30. data/lib/csscss/version.rb +1 -1
  31. data/lib/csscss.rb +22 -1
  32. data/test/csscss/json_reporter_test.rb +34 -0
  33. data/test/csscss/parser/background_test.rb +57 -0
  34. data/test/csscss/parser/border_color_test.rb +30 -0
  35. data/test/csscss/parser/border_side_test.rb +44 -0
  36. data/test/csscss/parser/border_style_test.rb +30 -0
  37. data/test/csscss/parser/border_test.rb +31 -0
  38. data/test/csscss/parser/border_width_test.rb +30 -0
  39. data/test/csscss/parser/color_test.rb +51 -0
  40. data/test/csscss/parser/common_test.rb +130 -0
  41. data/test/csscss/parser/css_test.rb +133 -0
  42. data/test/csscss/parser/font_test.rb +43 -0
  43. data/test/csscss/parser/list_style_test.rb +28 -0
  44. data/test/csscss/parser/margin_test.rb +50 -0
  45. data/test/csscss/parser/outline_test.rb +26 -0
  46. data/test/csscss/parser/padding_test.rb +50 -0
  47. data/test/csscss/redundancy_analyzer_test.rb +126 -4
  48. data/test/csscss/reporter_test.rb +4 -2
  49. data/test/csscss/types_test.rb +73 -0
  50. data/test/just_parse.rb +8 -0
  51. data/test/test_helper.rb +49 -6
  52. metadata +61 -23
  53. data/.rspec.default +0 -1
data/.gitignore CHANGED
@@ -4,7 +4,6 @@
4
4
  .config
5
5
  .yardoc
6
6
  .rspec
7
- Gemfile.lock
8
7
  InstalledFiles
9
8
  _yardoc
10
9
  coverage
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 ##
2
+
3
+ * Initial project release.
data/Gemfile CHANGED
@@ -5,5 +5,9 @@ gemspec
5
5
 
6
6
  gem "rake", :require => false
7
7
  gem "debugger"
8
+
9
+ gem "minitest"
8
10
  gem "m"
9
11
  gem "minitest-rg"
12
+
13
+ gem "ruby-prof"
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ csscss (0.1.0)
5
+ parslet (~> 1.5)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ blankslate (2.1.2.4)
11
+ columnize (0.3.6)
12
+ debugger (1.5.0)
13
+ columnize (>= 0.3.1)
14
+ debugger-linecache (~> 1.2.0)
15
+ debugger-ruby_core_source (~> 1.2.0)
16
+ debugger-linecache (1.2.0)
17
+ debugger-ruby_core_source (1.2.0)
18
+ m (1.3.1)
19
+ method_source (>= 0.6.7)
20
+ rake (>= 0.9.2.2)
21
+ method_source (0.8.1)
22
+ minitest (2.12.1)
23
+ minitest-rg (1.1.0)
24
+ parslet (1.5.0)
25
+ blankslate (~> 2.0)
26
+ rake (10.0.3)
27
+ ruby-prof (0.13.0)
28
+
29
+ PLATFORMS
30
+ ruby
31
+
32
+ DEPENDENCIES
33
+ csscss!
34
+ debugger
35
+ m
36
+ minitest
37
+ minitest-rg
38
+ rake
39
+ ruby-prof
data/README.md CHANGED
@@ -1,25 +1,60 @@
1
- # Csscss
2
-
3
1
  [![Build Status](https://travis-ci.org/zmoazeni/csscss.png?branch=master)](https://travis-ci.org/zmoazeni/csscss)
4
2
 
5
- A CSS redundancy analyzer that analyzes redundancy. My first attempt was
6
- in Haskell with https://github.com/zmoazeni/csscss-haskell this is the
7
- second in Ruby.
3
+ ## What is it? ##
4
+
5
+ csscss will parse any CSS files you give it and let you know which
6
+ rulesets have duplicated declarations.
7
+
8
+ ## What is it for? ##
9
+
10
+ One of the best strategies for me to maintain CSS is to reduce
11
+ duplication as much as possible. It's not a silver bullet, but it sure
12
+ helps.
8
13
 
9
- ## (Not ready yet) Installation
14
+ To do that, you need to have all the rulesets in your head at all times.
15
+ That's hard, csscss is easy. Let it tell you what is redundant.
10
16
 
11
- (Soon) Install it as:
17
+ ## How do I use it? ##
18
+
19
+ First you need to install it. It is currently packaged as a ruby gem:
12
20
 
13
21
  $ gem install csscss
14
22
 
15
- ## Usage
23
+ Then you can run it in at the command line against CSS files.
24
+
25
+ $ csscss path/to/styles.css path/to/other-styles.css
26
+
27
+ Run it in a verbose mode to see all the duplicated styles.
28
+
29
+ $ csscss -v path/to/styles.css
30
+
31
+ You can also choose a minimum number of matches, which will ignore any
32
+ rulesets that have fewer matches.
33
+
34
+ $ csscss -n 10 -v path/to/style.css # ignores rulesets with < 10 matches
35
+
36
+ ## I found bugs ##
37
+
38
+ This is still a new and evolving project. I heartily welcome feedback.
39
+ If you find any issues, please report them on
40
+ [github](https://github.com/zmoazeni/csscss/issues).
41
+
42
+ Please include the smallest CSS snippet to describe the issue and the
43
+ output you expect to see.
44
+
45
+ ## Who are you? ##
46
+
47
+ My name is [Zach Moazeni](https://twitter.com/zmoazeni). I work for [an
48
+ awesome company](http://www.getharvest.com/), which [is
49
+ hiring!](http://www.getharvest.com/careers).
16
50
 
17
- TODO: Write usage instructions here
51
+ ## I'm a dev, I can help ##
18
52
 
19
- ## Contributing
53
+ Awesome! Thanks! Here are the steps I ask:
20
54
 
21
55
  1. Fork it
22
56
  2. Create your feature branch (`git checkout -b my-new-feature`)
23
57
  3. Commit your changes (`git commit -am 'Add some feature'`)
24
- 4. Push to the branch (`git push origin my-new-feature`)
25
- 5. Create new Pull Request
58
+ 4. Make sure the tests pass (`bundle exec rake test`)
59
+ 5. Push to the branch (`git push origin my-new-feature`)
60
+ 6. Create new Pull Request
data/bin/csscss CHANGED
@@ -1,5 +1,4 @@
1
1
  #! /usr/bin/env ruby
2
2
 
3
- require "debugger"
4
3
  require "csscss"
5
4
  Csscss::CLI.run(ARGV)
data/csscss.gemspec CHANGED
@@ -9,14 +9,13 @@ Gem::Specification.new do |gem|
9
9
  gem.authors = ["Zach Moazeni"]
10
10
  gem.email = ["zach.moazeni@gmail.com"]
11
11
  gem.description = %q{A CSS redundancy analyzer that analyzes redundancy.}
12
- gem.summary = %q{A CSS redundancy analyzer that analyzes redundancy.}
13
- gem.homepage = ""
12
+ gem.summary = %q{csscss will parse any CSS files you give it and let you know which rulesets have duplicated declarations.}
13
+ gem.homepage = "http://zmoazeni.github.com/csscss/"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency "csspool", "~> 3.0"
21
20
  gem.add_dependency "parslet", "~> 1.5"
22
21
  end
data/lib/csscss/cli.rb CHANGED
@@ -27,7 +27,16 @@ module Csscss
27
27
  end
28
28
  end
29
29
 
30
- puts Reporter.new(combined_redundancies).report(@verbose)
30
+ if @json
31
+ puts JSONReporter.new(combined_redundancies).report
32
+ else
33
+ puts Reporter.new(combined_redundancies).report(@verbose)
34
+ end
35
+
36
+ rescue Parslet::ParseFailed => e
37
+ puts "Had a problem parsing the css"
38
+ puts e.cause.ascii_tree
39
+ exit 1
31
40
  end
32
41
 
33
42
  def parse(argv)
@@ -0,0 +1,17 @@
1
+ module Csscss
2
+ class JSONReporter
3
+ def initialize(redundancies)
4
+ @redundancies = redundancies
5
+ end
6
+
7
+ def report
8
+ JSON.dump(@redundancies.map {|selector_groups, declarations|
9
+ {
10
+ "selectors" => selector_groups.map(&:to_s),
11
+ "count" => declarations.count,
12
+ "declarations" => declarations.map(&:to_s)
13
+ }
14
+ })
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,72 @@
1
+ module Csscss
2
+ module Parser
3
+ module Background
4
+ extend Parser::Base
5
+
6
+ class Parser < Parslet::Parser
7
+ include Color
8
+
9
+ rule(:bg_color) { color | symbol("inherit") }
10
+ rule(:bg_image) { (url | symbol("none") | symbol("inherit")).as(:image_literal) }
11
+ rule(:bg_repeat) { symbol_list(%w(repeat-x repeat-y repeat no-repeat inherit)).as(:repeat) }
12
+ rule(:bg_attachment) { symbol_list(%w(scroll fixed inherit)).as(:attachment) }
13
+
14
+ rule(:bg_position) {
15
+ lcr_symbols = symbol_list(%w(left center right))
16
+ tcb_symbols = symbol_list(%w(top center bottom))
17
+
18
+ (symbol("inherit") | (
19
+ lcr = (percent | length | lcr_symbols)
20
+ tcb = (percent | length | tcb_symbols)
21
+
22
+ lcr >> tcb | lcr | tcb
23
+ )).as(:position)
24
+ }
25
+
26
+ rule(:background) {
27
+ (
28
+ symbol("inherit") >> eof | (
29
+ bg_color.maybe.as(:bg_color) >>
30
+ bg_image.maybe.as(:bg_image) >>
31
+ bg_repeat.maybe.as(:bg_repeat) >>
32
+ bg_attachment.maybe.as(:bg_attachment) >>
33
+ bg_position.maybe.as(:bg_position)
34
+ )
35
+ ).as(:background)
36
+ }
37
+ root(:background)
38
+ end
39
+
40
+ class Transformer < Parslet::Transform
41
+ @property = :background_color
42
+ extend Color::Transformer
43
+
44
+ rule(image_literal:simple(:value)) {Declaration.from_parser("background-image", value) }
45
+
46
+ rule(:repeat => simple(:repeat)) {
47
+ Declaration.from_parser("background-repeat", repeat)
48
+ }
49
+
50
+ rule(:attachment => simple(:attachment)) {
51
+ Declaration.from_parser("background-attachment", attachment)
52
+ }
53
+
54
+ rule(:position => simple(:position)) {
55
+ Declaration.from_parser("background-position", position)
56
+ }
57
+
58
+ rule(:background => simple(:inherit)) {[]}
59
+
60
+ rule(background: {
61
+ bg_color:simple(:color),
62
+ bg_image:simple(:url),
63
+ bg_repeat:simple(:repeat),
64
+ bg_attachment:simple(:attachment),
65
+ bg_position:simple(:position)
66
+ }) {
67
+ [color, url, repeat, attachment, position].compact
68
+ }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,13 @@
1
+ module Csscss
2
+ module Parser
3
+ module Base
4
+ def parse(_, inputs)
5
+ input = Array(inputs).join(" ")
6
+
7
+ if parsed = self::Parser.new.try_parse(input)
8
+ self::Transformer.new.apply(parsed)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,45 @@
1
+ module Csscss
2
+ module Parser
3
+ module Border
4
+ extend Parser::Base
5
+
6
+ class Parser < Parslet::Parser
7
+ include Color
8
+
9
+ rule(:border_side) { BorderSide::Parser.new(:top).border_side_anonymous }
10
+
11
+ rule(:border) {
12
+ (
13
+ symbol("inherit") >> eof |
14
+ border_side.maybe.as(:side)
15
+ ).as(:border)
16
+ }
17
+
18
+ root(:border)
19
+ end
20
+
21
+ class Transformer < Parslet::Transform
22
+ extend Color::Transformer
23
+ extend Color::PlainColorValue
24
+ extend BorderSide::Transformer::Helpers
25
+
26
+ rule(border: simple(:inherit)) {[]}
27
+ rule(border: {
28
+ side: {
29
+ width:simple(:width),
30
+ style:simple(:style),
31
+ color:simple(:color)
32
+ }
33
+ }) {|context|
34
+ [].tap do |declarations|
35
+ [:top, :right, :bottom, :left].each do |side|
36
+ declarations << transform_side(side, context)
37
+ end
38
+
39
+ declarations.flatten!
40
+ end
41
+ }
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ module Csscss
2
+ module Parser
3
+ module BorderColor
4
+ extend Parser::Base
5
+
6
+ class Parser < Parslet::Parser
7
+ include Color
8
+
9
+ rule(:border_color_side) {
10
+ color | symbol("transparent")
11
+ }
12
+
13
+ rule(:border_color) {
14
+ (
15
+ symbol("inherit") >> eof | (
16
+ border_color_side.maybe.as(:top) >>
17
+ border_color_side.maybe.as(:right) >>
18
+ border_color_side.maybe.as(:bottom) >>
19
+ border_color_side.maybe.as(:left)
20
+ )
21
+ ).as(:border_color)
22
+ }
23
+
24
+ root(:border_color)
25
+ end
26
+
27
+ class Transformer < Parslet::Transform
28
+ @property = :border_color
29
+ extend MultiSideTransformer
30
+ extend Color::Transformer
31
+ extend Color::PlainColorValue
32
+
33
+ class << self
34
+ def side_declaration(side, value)
35
+ Declaration.from_parser("border-#{side}-color", value)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,91 @@
1
+ module Csscss
2
+ module Parser
3
+ module BorderSide
4
+ class << self
5
+ def parse(property, inputs)
6
+ input = Array(inputs).join(" ")
7
+ side = find_side(property)
8
+
9
+ if parsed = self::Parser.new(side).try_parse(input)
10
+ self::Transformer.new.apply(parsed)
11
+ end
12
+ end
13
+
14
+ def find_side(property)
15
+ case property
16
+ when "border-top" then :top
17
+ when "border-right" then :right
18
+ when "border-bottom" then :bottom
19
+ when "border-left" then :left
20
+ else raise "Unknown property #{property}"
21
+ end
22
+ end
23
+ end
24
+
25
+ class Parser < Parslet::Parser
26
+ include Common
27
+
28
+ attr_reader :side
29
+ def initialize(side)
30
+ @side = side.to_sym
31
+ end
32
+
33
+ rule(:border_width) { BorderWidth::Parser.new.border_width_side }
34
+ rule(:border_style) { BorderStyle::Parser.new.border_style_side }
35
+ rule(:border_color) { BorderColor::Parser.new.border_color_side }
36
+
37
+ rule(:border_side_anonymous) {
38
+ border_width.maybe.as(:width) >>
39
+ border_style.maybe.as(:style) >>
40
+ border_color.maybe.as(:color)
41
+ }
42
+
43
+ rule(:border_side) {
44
+ (
45
+ symbol("inherit") >> eof |
46
+ border_side_anonymous.as(side)
47
+ ).as(:border_side)
48
+ }
49
+
50
+ root(:border_side)
51
+ end
52
+
53
+ class Transformer < Parslet::Transform
54
+ extend Color::Transformer
55
+ extend Color::PlainColorValue
56
+
57
+ module Helpers
58
+ def transform_top(context); transform_side("top", context); end
59
+ def transform_right(context); transform_side("right", context); end
60
+ def transform_bottom(context); transform_side("bottom", context); end
61
+ def transform_left(context); transform_side("left", context); end
62
+
63
+ def transform_side(side, context)
64
+ width = context[:width]
65
+ style = context[:style]
66
+ color = context[:color]
67
+
68
+ [].tap do |declarations|
69
+ declarations << Declaration.from_parser("border-#{side}-width", width) if width
70
+ declarations << Declaration.from_parser("border-#{side}-style", style) if style
71
+ declarations << Declaration.from_parser("border-#{side}-color", color) if color
72
+ end
73
+ end
74
+ end
75
+ extend Helpers
76
+
77
+ rule(border_side: simple(:inherit)) {[]}
78
+
79
+ [:top, :right, :bottom, :left].each do |side|
80
+ rule(border_side: {
81
+ side => {
82
+ width:simple(:width),
83
+ style:simple(:style),
84
+ color:simple(:color)
85
+ }
86
+ }, &method("transform_#{side}"))
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,41 @@
1
+ module Csscss
2
+ module Parser
3
+ module BorderStyle
4
+ extend Parser::Base
5
+
6
+ class Parser < Parslet::Parser
7
+ include Common
8
+
9
+ rule(:border_style_side) {
10
+ symbol_list(%w(none hidden dotted dashed solid
11
+ double groove ridge inset outset
12
+ ))
13
+ }
14
+
15
+ rule(:border_style) {
16
+ (
17
+ symbol("inherit") >> eof | (
18
+ border_style_side.maybe.as(:top) >>
19
+ border_style_side.maybe.as(:right) >>
20
+ border_style_side.maybe.as(:bottom) >>
21
+ border_style_side.maybe.as(:left)
22
+ )
23
+ ).as(:border_style)
24
+ }
25
+
26
+ root(:border_style)
27
+ end
28
+
29
+ class Transformer < Parslet::Transform
30
+ @property = :border_style
31
+ extend MultiSideTransformer
32
+
33
+ class << self
34
+ def side_declaration(side, value)
35
+ Declaration.from_parser("border-#{side}-style", value)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ module Csscss
2
+ module Parser
3
+ module BorderWidth
4
+ extend Parser::Base
5
+
6
+ class Parser < Parslet::Parser
7
+ include Common
8
+
9
+ rule(:border_width_side) {
10
+ symbol_list(%w(thin medium thick inherit)) | length
11
+ }
12
+
13
+ rule(:border_width) {
14
+ (
15
+ symbol("inherit") >> eof | (
16
+ border_width_side.maybe.as(:top) >>
17
+ border_width_side.maybe.as(:right) >>
18
+ border_width_side.maybe.as(:bottom) >>
19
+ border_width_side.maybe.as(:left)
20
+ )
21
+ ).as(:border_width)
22
+ }
23
+
24
+ root(:border_width)
25
+ end
26
+
27
+ class Transformer < Parslet::Transform
28
+ @property = :border_width
29
+ extend MultiSideTransformer
30
+
31
+ def self.side_declaration(side, value)
32
+ Declaration.from_parser("border-#{side}-width", value)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ module Csscss
2
+ module Parser
3
+ module Color
4
+ include Parslet
5
+ include Common
6
+
7
+ rule(:color) { (hexcolor | rgb | color_keyword).as(:color) }
8
+ rule(:rgb) { (rgb_with(numbers) | rgb_with(percent)).as(:rgb) }
9
+ rule(:hexcolor) { (str("#") >> match["a-fA-F0-9"].repeat(1)).as(:hexcolor) >> space? }
10
+ rule(:color_keyword) {
11
+ colors = %w(inherit black silver gray white maroon
12
+ red purple fuchsia green lime olive
13
+ yellow navy blue teal aqua)
14
+ colors.map {|c| symbol(c) }.reduce(:|).as(:keyword)
15
+ }
16
+
17
+ private
18
+ def rgb_with(parser)
19
+ symbol("rgb") >> parens do
20
+ parser >> space? >>
21
+ symbol(",") >>
22
+ parser >> space? >>
23
+ symbol(",") >>
24
+ parser >> space?
25
+ end
26
+ end
27
+
28
+ module Transformer
29
+ def self.extended(base)
30
+ base.instance_eval do
31
+ extend ClassMethods
32
+
33
+ rule(color:{rgb:simple(:value)}) {|c| transform_color(c)}
34
+ rule(color:{keyword:simple(:value)}) {|c| transform_color(c)}
35
+ rule(color:{hexcolor:simple(:value)}) {|c| transform_color(c)}
36
+ end
37
+ end
38
+
39
+ module ClassMethods
40
+ def transform_color(context)
41
+ Declaration.from_parser(@property.to_s.gsub("_", "-"), context[:value])
42
+ end
43
+ end
44
+ end
45
+
46
+ module PlainColorValue
47
+ def transform_color(context)
48
+ context[:value]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,86 @@
1
+ module Csscss
2
+ module Parser
3
+ module Common
4
+ include Parslet
5
+
6
+ UNITS = %w(px em ex in cm mm pt pc)
7
+
8
+ rule(:space) { match['\s'].repeat(1) }
9
+ rule(:space?) { space.maybe }
10
+ rule(:number) { match["0-9"] }
11
+ rule(:numbers) { number.repeat(1) }
12
+ rule(:decimal) { numbers >> str(".").maybe >> numbers.maybe }
13
+ rule(:percent) { decimal >> stri("%") >> space? }
14
+ rule(:length) { decimal >> stri_list(UNITS) >> space? }
15
+ rule(:identifier) { match["a-zA-Z"].repeat(1) }
16
+ rule(:inherit) { stri("inherit") }
17
+ rule(:eof) { any.absent? }
18
+ rule(:nada) { any.repeat.as(:nada) }
19
+
20
+ rule(:http) {
21
+ (match['a-zA-Z.:/'] | str('\(') | str('\)')).repeat >> space?
22
+ }
23
+
24
+ rule(:url) {
25
+ stri("url") >> parens do
26
+ (any_quoted { http } >> space?) |
27
+ http
28
+ end
29
+ }
30
+
31
+ def stri(str)
32
+ key_chars = str.split(//)
33
+ key_chars.
34
+ collect! { |char|
35
+ if char.upcase == char.downcase
36
+ str(char)
37
+ else
38
+ match["#{char.upcase}#{char.downcase}"]
39
+ end
40
+ }.reduce(:>>)
41
+ end
42
+
43
+ def symbol(s, label = nil)
44
+ if label
45
+ stri(s).as(label) >> space?
46
+ else
47
+ stri(s) >> space?
48
+ end
49
+ end
50
+
51
+ def between(left, right)
52
+ raise "block not given" unless block_given?
53
+ symbol(left) >> yield >> symbol(right)
54
+ end
55
+
56
+ def parens(&block)
57
+ between("(", ")", &block)
58
+ end
59
+
60
+ def double_quoted(&block)
61
+ between('"', '"', &block)
62
+ end
63
+
64
+ def single_quoted(&block)
65
+ between("'", "'", &block)
66
+ end
67
+
68
+ def any_quoted(&block)
69
+ double_quoted(&block) | single_quoted(&block)
70
+ end
71
+
72
+ def stri_list(list)
73
+ list.map {|u| stri(u) }.reduce(:|)
74
+ end
75
+
76
+ def symbol_list(list)
77
+ list.map {|u| symbol(u) }.reduce(:|)
78
+ end
79
+
80
+ def try_parse(input)
81
+ parsed = (root | nada).parse(input)
82
+ parsed[:nada] ? false : parsed
83
+ end
84
+ end
85
+ end
86
+ end