rdparser 0.1.1
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.
- 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: []
|