rdparser 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/Rakefile +43 -0
- data/lib/patches/array.rb +10 -0
- data/lib/rdparser.rb +12 -0
- data/lib/rdparser/exceptions.rb +4 -0
- data/lib/rdparser/rdparser.rb +195 -0
- data/lib/rdparser/scanner.rb +56 -0
- data/lib/rdparser/version.rb +3 -0
- data/rdparser.gemspec +27 -0
- data/spec/examples/a4_spec.rb +32 -0
- data/spec/support/parser_helper.rb +28 -0
- data/spec/support/spec_helper.rb +17 -0
- metadata +133 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2@rdparser --create
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rdoc/task'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
|
8
|
+
require 'rake/packagetask'
|
9
|
+
require 'rubygems/package_task'
|
10
|
+
|
11
|
+
RDPARSER_GEMSPEC = eval(File.read(File.expand_path("../rdparser.gemspec", __FILE__)))
|
12
|
+
|
13
|
+
desc 'Default: run specs'
|
14
|
+
task :default => 'spec'
|
15
|
+
|
16
|
+
namespace :spec do
|
17
|
+
desc 'Run all specs in spec directory (format=progress)'
|
18
|
+
RSpec::Core::RakeTask.new(:progress) do |t|
|
19
|
+
t.pattern = './spec/**/*_spec.rb'
|
20
|
+
t.rspec_opts = ['--color', '--format=progress']
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Run all specs in spec directory (format=documentation)'
|
24
|
+
RSpec::Core::RakeTask.new(:documentation) do |t|
|
25
|
+
t.pattern = './spec/**/*_spec.rb'
|
26
|
+
t.rspec_opts = ['--color', '--format=documentation']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task :spec => 'spec:progress'
|
31
|
+
|
32
|
+
desc 'Generate documentation for the a4-core plugin.'
|
33
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
34
|
+
rdoc.rdoc_dir = 'rdoc'
|
35
|
+
rdoc.title = 'RDParser'
|
36
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--charset=UTF-8'
|
37
|
+
rdoc.rdoc_files.include('README')
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
end
|
40
|
+
|
41
|
+
Gem::PackageTask.new(RDPARSER_GEMSPEC) do |p|
|
42
|
+
p.gem_spec = RDPARSER_GEMSPEC
|
43
|
+
end
|
data/lib/rdparser.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require File.expand_path('../patches/array.rb', __FILE__)
|
3
|
+
|
4
|
+
# A Simple Recursive Descending (LL) Parser for Ruby
|
5
|
+
module RDParser
|
6
|
+
require File.expand_path('../rdparser/exceptions.rb', __FILE__)
|
7
|
+
autoload :RDParser, File.expand_path('../rdparser/rdparser.rb', __FILE__)
|
8
|
+
autoload :Scanner, File.expand_path('../rdparser/scanner.rb', __FILE__)
|
9
|
+
|
10
|
+
# change to true to get flooded with debug messages during parsing
|
11
|
+
DEBUG = false
|
12
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
class RDParser::RDParser
|
2
|
+
# Class used to accept Ruby metafoo when specifying grammars with a block and virtual methods
|
3
|
+
class GenericBlockToHashCreator; def method_missing(m, *args); @h ||= {}; @h[m] = args.first; @h; end; end
|
4
|
+
|
5
|
+
# Creates a parser based on a certain grammar provided either as a hash or as
|
6
|
+
# methods within a block (that then uses GrammarCreator)
|
7
|
+
def initialize(grammar = '')
|
8
|
+
@grammar = grammar == '' ? yield(GenericBlockToHashCreator.new) : grammar.dup
|
9
|
+
end
|
10
|
+
|
11
|
+
# Parse content using the parser object's grammar set
|
12
|
+
def parse(rule, content, options = {})
|
13
|
+
# @depth is used to make debugging output look nice and nested
|
14
|
+
@depth = -1
|
15
|
+
|
16
|
+
# Create a string scanner object based on the content
|
17
|
+
@content = RDParser::Scanner.new(content, :slurp_whitespace => !options[:manual_whitespace])
|
18
|
+
|
19
|
+
# Kick off the show by parsing from the rule specified
|
20
|
+
result = parse_section(rule.to_sym).flatten
|
21
|
+
|
22
|
+
# Raises ParserError when cannot get parse the entire content and :partial is not set.
|
23
|
+
raise ParserError, %{Cannot parse the entire content! (error was on position ##{@content.position}: "#{content[0...@content.position]}|#{content[@content.position..@content.position]}|#{content[@content.position+1..-1]}")} unless options[:partial] == true or @content.eos?
|
24
|
+
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parse the content based on the rulesets for a particular grammar context
|
29
|
+
def parse_section(rule)
|
30
|
+
# Increase the depth of parsing - used for debug messages currently
|
31
|
+
@depth += 1
|
32
|
+
|
33
|
+
# For each distinct set of rules within a ruleset, get parsing..
|
34
|
+
sub_rulesets(rule).each do |ruleset|
|
35
|
+
RDParser::DEBUG && debug_message("RULE SET : #{ruleset}")
|
36
|
+
|
37
|
+
# By default, we assume failure!
|
38
|
+
success = false
|
39
|
+
output = []
|
40
|
+
|
41
|
+
# Keep a local copy of the current position to roll back to if things get hairy..
|
42
|
+
was_position = @content.position
|
43
|
+
|
44
|
+
# Go through each rule in this ruleset
|
45
|
+
sub_rules(ruleset).each do |r|
|
46
|
+
RDParser::DEBUG && debug_message("Running rule '#{r}' against '#{@content.lookahead}'")
|
47
|
+
suboutput = []
|
48
|
+
|
49
|
+
# Match a rule with 1 or more occurrences (e.g. "rule(s)")
|
50
|
+
if r =~ /(\w+)(\(s\))$/
|
51
|
+
r = $1
|
52
|
+
|
53
|
+
# Force the first occurrence to succeed or break this sub-ruleset
|
54
|
+
unless result = match_for_rule(r.to_sym)
|
55
|
+
success = false
|
56
|
+
break
|
57
|
+
end
|
58
|
+
|
59
|
+
suboutput.append_or_blend result
|
60
|
+
|
61
|
+
# Now pick up any of the "or more" occurrences for free
|
62
|
+
while result = match_for_rule(r.to_sym)
|
63
|
+
suboutput.append_or_blend result
|
64
|
+
end
|
65
|
+
|
66
|
+
# Match a rule with 0 or more occurrences (e.g. "rule(s?)")
|
67
|
+
elsif r =~ /(\w+)(\(s\?\))$/
|
68
|
+
r = $1
|
69
|
+
while result = match_for_rule(r.to_sym)
|
70
|
+
suboutput.append_or_blend result
|
71
|
+
end
|
72
|
+
|
73
|
+
# Match a rule with 0 or 1 occurrences (e.g. "rules(?)")
|
74
|
+
elsif r =~ /(\w+)(\(\?\))$/
|
75
|
+
r = $1
|
76
|
+
if result = match_for_rule(r.to_sym)
|
77
|
+
suboutput.append_or_blend result
|
78
|
+
end
|
79
|
+
|
80
|
+
# Match a rule that has one single occurrence
|
81
|
+
else
|
82
|
+
unless result = match_for_rule(r.to_sym)
|
83
|
+
success = false
|
84
|
+
break
|
85
|
+
end
|
86
|
+
|
87
|
+
suboutput.append_or_blend result
|
88
|
+
end
|
89
|
+
|
90
|
+
success = true
|
91
|
+
|
92
|
+
# Append the output from this rule to the output we'll use later..
|
93
|
+
output += suboutput
|
94
|
+
end
|
95
|
+
|
96
|
+
# We've either processed all the rules for this ruleset, or.. it failed
|
97
|
+
if success
|
98
|
+
RDParser::DEBUG && debug_message("Success of all rules in #{ruleset}")
|
99
|
+
|
100
|
+
# No need to check any more rulesets! We've just passed one,
|
101
|
+
# so drop the depth a notch, we're headed back up the tree of recursion!
|
102
|
+
@depth -= 1
|
103
|
+
RDParser::DEBUG && debug_message("SUCCEED: #{ruleset}", :passback)
|
104
|
+
return output
|
105
|
+
else
|
106
|
+
RDParser::DEBUG && debug_message("failed #{ruleset}.. moving on")
|
107
|
+
|
108
|
+
# If the rule set failed, revert the position back to that we stored earlier
|
109
|
+
@content.rollback_to(was_position)
|
110
|
+
|
111
|
+
# And clean the output.. because any output we got from a broken rule is as useful
|
112
|
+
# as an ashtray on a motorbike, a chocolate teapot, or ice cutlery.
|
113
|
+
RDParser::DEBUG && debug_message("FAIL: #{ruleset}", :passback)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Well nothing passed, so this rule was totally bogus. Great. We've totally wasted our time.
|
118
|
+
@depth -= 1
|
119
|
+
return false
|
120
|
+
end
|
121
|
+
|
122
|
+
# Parse the content based on a single subrule
|
123
|
+
def match_for_rule(rule)
|
124
|
+
RDParser::DEBUG && debug_message("TRYING #{rule}")
|
125
|
+
output = []
|
126
|
+
rule_data = @grammar[rule]
|
127
|
+
|
128
|
+
# If the rule is a string literal "likethis" then match it
|
129
|
+
if rule.to_s =~ /^\"(.+)\"$/
|
130
|
+
m = $1
|
131
|
+
if @content.scan(m)
|
132
|
+
RDParser::DEBUG && debug_message("GOT #{m}")
|
133
|
+
output << {rule => m}
|
134
|
+
else
|
135
|
+
return false
|
136
|
+
end
|
137
|
+
|
138
|
+
# Is the rule a regular expression?
|
139
|
+
elsif rule_data.class == Regexp
|
140
|
+
# If we get a match.. do stuff!
|
141
|
+
if c = @content.scan(rule_data)
|
142
|
+
RDParser::DEBUG && debug_message("GOT IT --> #{c}")
|
143
|
+
output << {rule => c}
|
144
|
+
else
|
145
|
+
# If we get no match, go and cry to mommy^H^H^H^H^Hhead up the recursion ladder
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
# Is the rule a string of other rules?
|
150
|
+
elsif rule_data.class == String
|
151
|
+
# A RULE THAT HAS RULES?? RECURSION IN THE HOUSE!
|
152
|
+
response = parse_section(rule)
|
153
|
+
|
154
|
+
# But did it really work out as planned?
|
155
|
+
if response
|
156
|
+
# Yes.. so celebrate and process the response.
|
157
|
+
RDParser::DEBUG && debug_message("GOT #{rule}")
|
158
|
+
return {rule => response}
|
159
|
+
else
|
160
|
+
# No.. so throw a hissyfit
|
161
|
+
RDParser::DEBUG && debug_message("NOT GOT #{rule}")
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
output
|
166
|
+
end
|
167
|
+
|
168
|
+
# Splits a ruleset into its constituent rules or matches by whitespace
|
169
|
+
def sub_rules(ruleset); ruleset.split(/\s+/); end
|
170
|
+
|
171
|
+
# Extracts all rulesets from a single rule
|
172
|
+
# e.g. 'rule1 rule2 | rule3 | rule4 rule 5' is three rule sets separated by bars
|
173
|
+
def sub_rulesets(rule); @grammar[rule].split(/\s+\|\s+/); end
|
174
|
+
|
175
|
+
# A 'pretty printer' for RDParser's syntax trees (pp just doesn't cut it for these beasties)
|
176
|
+
# There's probably a nice iterative way of doing this but I'm tired.
|
177
|
+
def RDParser.text_syntax_tree(v, depth = 0, output = '')
|
178
|
+
if v.class == Array
|
179
|
+
v.each { |a| output = text_syntax_tree(a, depth, output) }
|
180
|
+
elsif v.class == Hash
|
181
|
+
v.each do |a, b|
|
182
|
+
output += (" " * depth) + a.to_s
|
183
|
+
b.class == String ? output += " => #{b}\n" : output = text_syntax_tree(b, depth + 1, output + "\n")
|
184
|
+
end
|
185
|
+
end
|
186
|
+
output
|
187
|
+
end
|
188
|
+
|
189
|
+
# Prints a debugging message if debug mode is enabled
|
190
|
+
def debug_message(message, priority = true)
|
191
|
+
# Let's different types of message through
|
192
|
+
return if priority != RDParser::DEBUG
|
193
|
+
puts "#{(" " * @depth.to_i) if @depth && @depth > -1 } #{message}"
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Attached class below for simplicity of transfer at the moment.. but this should really be
|
2
|
+
# separated.
|
3
|
+
|
4
|
+
# Scanner is basically a proxy class for StringScanner that adds some convenience features.
|
5
|
+
# Could probably be a subclass actually, but this was a quick, scrappy job so I could get
|
6
|
+
# onto the fun stuff in RDParser!
|
7
|
+
class RDParser::Scanner
|
8
|
+
def initialize(string, options = {})
|
9
|
+
@options = options
|
10
|
+
@scanner = StringScanner.new(string.dup)
|
11
|
+
@rollback_pointers = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def position
|
15
|
+
@scanner.pos
|
16
|
+
end
|
17
|
+
|
18
|
+
def eos?
|
19
|
+
@scanner.eos?
|
20
|
+
end
|
21
|
+
|
22
|
+
def rollback_position
|
23
|
+
@rollback_pointers.last
|
24
|
+
end
|
25
|
+
|
26
|
+
def lookahead
|
27
|
+
@scanner.peek(10)
|
28
|
+
end
|
29
|
+
|
30
|
+
def scan(regexp)
|
31
|
+
space = @options[:slurp_whitespace] && @options[:slurp_whitespace] == true ? /\s*/ : //
|
32
|
+
if regexp.class == String
|
33
|
+
regexp = Regexp.new(regexp.gsub(/\(|\)|\+|\|/) { |a| '\\' + a })
|
34
|
+
end
|
35
|
+
if match = @scanner.scan(/(#{space}(#{regexp}))/)
|
36
|
+
@rollback_pointers << (@scanner.pos - match.length)
|
37
|
+
@options[:slurp_whitespace] && @options[:slurp_whitespace] == true ? match.sub(/^\s*/, '') : match
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def rollback
|
44
|
+
begin
|
45
|
+
(@scanner.pos = @rollback_pointers.pop) && true
|
46
|
+
rescue
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def rollback_to(posi)
|
52
|
+
@scanner.pos = posi
|
53
|
+
@rollback_pointers.delete_if { |p| p >= posi }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
data/rdparser.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path("../lib/rdparser/version", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "rdparser"
|
5
|
+
s.version = RDParser::VERSION
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Peter Cooper", "Bence Golda"]
|
8
|
+
s.email = ["golda@bitandpixel.hu"]
|
9
|
+
s.homepage = "http://www.rubyinside.com/recursive-descent-parser-for-ruby-300.html"
|
10
|
+
s.summary = "rdparser-#{RDParser::VERSION}"
|
11
|
+
s.description = "A Simple Recursive Descending (LL) Parser for Ruby"
|
12
|
+
s.rubyforge_project = "rdparser"
|
13
|
+
|
14
|
+
s.add_development_dependency "rake"
|
15
|
+
s.add_development_dependency "bundler"
|
16
|
+
s.add_development_dependency "rspec"
|
17
|
+
s.add_development_dependency "simplecov"
|
18
|
+
s.add_development_dependency "spork"
|
19
|
+
s.add_development_dependency "watchr"
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.executables = `git ls-files`.split("\n").select{|f| f =~ /^bin/}
|
23
|
+
#s.extra_rdoc_files = [ "README.markdown" ]
|
24
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
25
|
+
s.require_path = 'lib'
|
26
|
+
end
|
27
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path('../../support/spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe 'A4::Authorization::Parser' do
|
4
|
+
include ParserHelper
|
5
|
+
|
6
|
+
parser do |p|
|
7
|
+
p.expression 'expression_u binary expression | expression_u'
|
8
|
+
p.expression_u 'unary expression_u | "(" expression ")" | role'
|
9
|
+
p.binary /and|or/
|
10
|
+
p.unary /not/
|
11
|
+
p.role 'role_name preposition instance_name | role_name preposition class_name | role_name'
|
12
|
+
p.preposition /of|on|in|at|for|to/
|
13
|
+
p.instance_name /:[a-z][a-zA-Z0-9]*/
|
14
|
+
p.class_name /[A-Z][a-zA-Z0-9:]*/
|
15
|
+
p.role_name /[a-z][-_a-zA-Z0-9]*/
|
16
|
+
end
|
17
|
+
|
18
|
+
it { should parse("role") }
|
19
|
+
it { should parse("role1") }
|
20
|
+
it { should parse("role1 and role2") }
|
21
|
+
it { should parse("role1 or role2") }
|
22
|
+
it { should parse("(role)") }
|
23
|
+
it { should parse("(role1) and (role2) or (role3)") }
|
24
|
+
it { should parse("(role1 and role2) or (role3)") }
|
25
|
+
it { should parse("reader of Klass") }
|
26
|
+
it { should parse("reader of :instance") }
|
27
|
+
it { should parse("admin or reader of Post or (writer of :instance and guest)") }
|
28
|
+
it { should parse("role-with-dashes") }
|
29
|
+
it { should parse("role_with_underscores") }
|
30
|
+
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ParserHelper
|
2
|
+
def self.included base
|
3
|
+
base.extend const_get("ClassMethods") if const_defined?("ClassMethods")
|
4
|
+
base.send :include, const_get("InstanceMethods") if const_defined?("InstanceMethods")
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def parser &block
|
9
|
+
subject { RDParser::RDParser.new(&block) }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module InstanceMethods
|
14
|
+
def parse expression, root=:expression
|
15
|
+
class << (o=Object.new)
|
16
|
+
def description
|
17
|
+
%{should parse "#{@_expression}" starting with :#{@_root}}
|
18
|
+
end
|
19
|
+
def matches? parser
|
20
|
+
parser.parse(@_root, @_expression).kind_of?(Array)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
o.instance_variable_set('@_expression', expression)
|
24
|
+
o.instance_variable_set('@_root', root)
|
25
|
+
o
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start
|
6
|
+
|
7
|
+
require 'rspec'
|
8
|
+
require File.expand_path('../../../lib/rdparser.rb', __FILE__)
|
9
|
+
|
10
|
+
Dir[File.expand_path('../**/*.rb', __FILE__)].each {|f| require f}
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.alias_it_should_behave_like_to(:it_should_behave_like, '')
|
14
|
+
config.filter_run :focused => true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
end
|
17
|
+
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rdparser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Peter Cooper
|
9
|
+
- Bence Golda
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2011-12-17 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rake
|
17
|
+
requirement: &15362520 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *15362520
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bundler
|
28
|
+
requirement: &15362100 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *15362100
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
requirement: &15361680 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *15361680
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: simplecov
|
50
|
+
requirement: &15361260 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :development
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *15361260
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: spork
|
61
|
+
requirement: &15360840 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *15360840
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: watchr
|
72
|
+
requirement: &15360420 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *15360420
|
81
|
+
description: A Simple Recursive Descending (LL) Parser for Ruby
|
82
|
+
email:
|
83
|
+
- golda@bitandpixel.hu
|
84
|
+
executables: []
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- .gitignore
|
89
|
+
- .rvmrc
|
90
|
+
- Gemfile
|
91
|
+
- Rakefile
|
92
|
+
- lib/patches/array.rb
|
93
|
+
- lib/rdparser.rb
|
94
|
+
- lib/rdparser/exceptions.rb
|
95
|
+
- lib/rdparser/rdparser.rb
|
96
|
+
- lib/rdparser/scanner.rb
|
97
|
+
- lib/rdparser/version.rb
|
98
|
+
- rdparser.gemspec
|
99
|
+
- spec/examples/a4_spec.rb
|
100
|
+
- spec/support/parser_helper.rb
|
101
|
+
- spec/support/spec_helper.rb
|
102
|
+
homepage: http://www.rubyinside.com/recursive-descent-parser-for-ruby-300.html
|
103
|
+
licenses: []
|
104
|
+
post_install_message:
|
105
|
+
rdoc_options:
|
106
|
+
- --charset=UTF-8
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
hash: 2323939059109042522
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
hash: 2323939059109042522
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project: rdparser
|
129
|
+
rubygems_version: 1.8.10
|
130
|
+
signing_key:
|
131
|
+
specification_version: 3
|
132
|
+
summary: rdparser-0.1.1
|
133
|
+
test_files: []
|