parsr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in parsr.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,91 @@
1
+ require 'strscan'
2
+
3
+ class Parsr
4
+
5
+ BASE_PATH = File.expand_path(File.join(File.dirname(__FILE__), 'parsr'))
6
+
7
+ autoload :ArrayRule, "#{BASE_PATH}/array_rule"
8
+ autoload :FloatRule, "#{BASE_PATH}/float_rule"
9
+ autoload :HashRule, "#{BASE_PATH}/hash_rule"
10
+ autoload :IntegerRule, "#{BASE_PATH}/integer_rule"
11
+ autoload :RawStringRule, "#{BASE_PATH}/raw_string_rule"
12
+ autoload :StringRule, "#{BASE_PATH}/string_rule"
13
+ autoload :SymbolRule, "#{BASE_PATH}/symbol_rule"
14
+ autoload :Token, "#{BASE_PATH}/token"
15
+ autoload :VERSION, "#{BASE_PATH}/version"
16
+
17
+ Error = Class.new(Exception)
18
+ IllegalValue = Class.new(Error)
19
+
20
+ class SyntaxError < Error
21
+
22
+ attr_reader :scanner
23
+
24
+ def self.message(message=nil)
25
+ @message = "Syntax error, #{message}" if message
26
+ @message
27
+ end
28
+
29
+ def initialize(scanner)
30
+ @scanner = scanner
31
+ super(self.class.message % context)
32
+ end
33
+
34
+ protected
35
+ def context
36
+ if scanner.eos? || scanner.rest =~ /^\s*$/
37
+ rest = 'end of string'
38
+ else
39
+ scanner.rest.scan(/^\s*([^\s])/)
40
+ rest = $1
41
+ end
42
+ {:rest => rest}
43
+ end
44
+
45
+ end
46
+
47
+ class << self
48
+
49
+ def safe_literal_eval(string)
50
+ @safe_eval_parser ||= self.new(
51
+ ArrayRule,
52
+ HashRule,
53
+ SymbolRule,
54
+ FloatRule,
55
+ IntegerRule,
56
+ RawStringRule,
57
+ StringRule
58
+ )
59
+ @safe_eval_parser.parse(string)
60
+ end
61
+
62
+ end
63
+
64
+ attr_accessor :rules
65
+
66
+ def initialize(*rules)
67
+ @rules = [rules].flatten
68
+ end
69
+
70
+ def parse(string)
71
+ scanner = StringScanner.new(string)
72
+ token = parse_value(scanner)
73
+ raise IllegalValue unless token.is_a?(Parsr::Token)
74
+ token.value
75
+ end
76
+
77
+ protected
78
+ def parse_value(scanner)
79
+ trim_space(scanner)
80
+ rules.each do |rule|
81
+ token = rule.match(scanner){ parse_value(scanner) }
82
+ return token if token.is_a?(Parsr::Token)
83
+ end
84
+ nil
85
+ end
86
+
87
+ def trim_space(scanner)
88
+ scanner.scan(/\s+/)
89
+ end
90
+
91
+ end
@@ -0,0 +1,44 @@
1
+ module Parsr::ArrayRule
2
+
3
+ class Unterminated < Parsr::SyntaxError
4
+ message "unexpected '%{rest}', expecting ']'"
5
+ end
6
+
7
+ class << self
8
+
9
+ def match(scanner, &block)
10
+ if scanner.scan(/\[\s*/)
11
+ array = []
12
+ begin
13
+ if scanner.scan(/[a-zA-Z_][0-9a-zA-Z_]*\:/)
14
+ token = Parsr::Token.new(scanner.matched.chomp(':').to_sym)
15
+ array << parse_unclosed_hash(scanner, token, &block)
16
+ break
17
+ end
18
+
19
+ token = yield
20
+ if token.is_a?(Parsr::Token) && scanner.scan(/\s*=>\s*/)
21
+ array << parse_unclosed_hash(scanner, token, &block)
22
+ break
23
+ end
24
+
25
+ array << token.value if token.is_a?(Parsr::Token)
26
+ end while scanner.scan(/\s*,\s*/)
27
+ raise Unterminated.new(scanner) unless scanner.scan(/\s*\]/)
28
+ Parsr::Token.new(array)
29
+ end
30
+ end
31
+
32
+ def parse_unclosed_hash(scanner, token, &block)
33
+ hash = []
34
+ while pair = Parsr::HashRule.parse_pair(scanner, token, &block)
35
+ token = nil
36
+ hash << pair
37
+ break unless scanner.scan(/\s*\,\s*/)
38
+ end
39
+ Hash[hash]
40
+ end
41
+
42
+ end
43
+
44
+ end
@@ -0,0 +1,15 @@
1
+ module Parsr::FloatRule
2
+
3
+ PATTERN = /[\-\+]?\d*\.\d+/
4
+
5
+ class << self
6
+
7
+ def match(scanner)
8
+ if scanner.scan(PATTERN)
9
+ Parsr::Token.new(Float(scanner.matched))
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,46 @@
1
+ module Parsr::HashRule
2
+
3
+ class MissingValue < Parsr::SyntaxError
4
+ message "unexpected '%{rest}'"
5
+ end
6
+
7
+ class MissingSeparator < Parsr::SyntaxError
8
+ message "unexpected '%{rest}', expecting '=>'"
9
+ end
10
+
11
+ class Unterminated < Parsr::SyntaxError
12
+ message "unexpected '%{rest}', expecting '}'"
13
+ end
14
+
15
+ class << self
16
+
17
+ def match(scanner, &block)
18
+ if scanner.scan(/\{\s*/)
19
+ hash = []
20
+ while pair = parse_pair(scanner, &block)
21
+ hash << pair
22
+ break unless scanner.scan(/\s*\,\s*/)
23
+ end
24
+ raise Unterminated.new(scanner) unless scanner.scan(/\s*\}\s*/)
25
+ Parsr::Token.new(Hash[hash])
26
+ end
27
+ end
28
+
29
+ def parse_pair(scanner, key=nil)
30
+ unless key.is_a?(Parsr::Token)
31
+ if scanner.scan(/[a-zA-Z_][0-9a-zA-Z_]*\:/)
32
+ key = Parsr::Token.new(scanner.matched.chomp(':').to_sym)
33
+ elsif key = yield and key.is_a?(Parsr::Token)
34
+ raise MissingSeparator.new(scanner) unless scanner.scan(/\s*=>\s*/)
35
+ else
36
+ return false
37
+ end
38
+ end
39
+ value = yield
40
+ raise MissingValue.new(scanner) unless value && value.is_a?(Parsr::Token)
41
+ [key, value].map(&:value)
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,15 @@
1
+ module Parsr::IntegerRule
2
+
3
+ PATTERN = /[\-\+]?\d+/
4
+
5
+ class << self
6
+
7
+ def match(scanner)
8
+ if scanner.scan(PATTERN)
9
+ Parsr::Token.new(Integer(scanner.matched))
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,38 @@
1
+ module Parsr::RawStringRule
2
+
3
+ class Unterminated < Parsr::SyntaxError
4
+ message 'unterminated string meets end of file'
5
+ end
6
+
7
+ class << self
8
+
9
+ def match(scanner)
10
+ if scanner.scan(/'/)
11
+ buffer = ''
12
+ while chunk = (parse_content(scanner) || parse_escape(scanner))
13
+ buffer << chunk
14
+ end
15
+ raise Unterminated.new(scanner) unless terminated?(scanner)
16
+ return Parsr::Token.new(buffer)
17
+ end
18
+ nil
19
+ end
20
+
21
+ protected
22
+ def parse_content(scanner)
23
+ scanner.matched if scanner.scan(/[^\\']+/)
24
+ end
25
+
26
+ def parse_escape(scanner)
27
+ return %q{'} if scanner.scan(/\\'/)
28
+ return %q{\\} if scanner.scan(/\\\\/)
29
+ return scanner.matched if scanner.scan(%r{\\[^'\\/]})
30
+ end
31
+
32
+ def terminated?(scanner)
33
+ scanner.scan(/'/)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,40 @@
1
+ module Parsr::StringRule
2
+
3
+ ESCAPED_CHARS = {'\n' => "\n", '\b' => "\b", '\f' => "\f", '\r' => "\r", '\t' => "\t"}
4
+
5
+ class Unterminated < Parsr::SyntaxError
6
+ message 'unterminated string meets end of file'
7
+ end
8
+
9
+ class << self
10
+
11
+ def match(scanner)
12
+ if scanner.scan(/"/)
13
+ buffer = ''
14
+ while chunk = (parse_content(scanner) || parse_escape(scanner))
15
+ buffer << chunk
16
+ end
17
+ raise Unterminated.new(scanner) unless terminated?(scanner)
18
+ return Parsr::Token.new(buffer)
19
+ end
20
+ nil
21
+ end
22
+
23
+ protected
24
+ def parse_content(scanner)
25
+ scanner.matched if scanner.scan(/[^\\"]+/)
26
+ end
27
+
28
+ def parse_escape(scanner)
29
+ return %q{"} if scanner.scan(/\\"/)
30
+ return %q{\\} if scanner.scan(/\\\\/)
31
+ return ESCAPED_CHARS[scanner.matched] if scanner.scan(/\\[bfnrt]/)
32
+ return [Integer("0x#{scanner.matched[2..-1]}")].pack('U') if scanner.scan(/\\u[0-9a-fA-F]{4}/)
33
+ end
34
+
35
+ def terminated?(scanner)
36
+ scanner.scan(/"/)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ module Parsr::SymbolRule
2
+
3
+ PATTERN = /\:[a-zA-Z_][0-9a-zA-Z_]*/
4
+
5
+ class << self
6
+
7
+ def match(scanner)
8
+ if scanner.scan(PATTERN)
9
+ Parsr::Token.new(scanner.matched[1..-1].to_sym)
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,9 @@
1
+ class Parsr::Token
2
+
3
+ attr_reader :value
4
+
5
+ def initialize(value)
6
+ @value = value
7
+ end
8
+
9
+ end
@@ -0,0 +1,3 @@
1
+ class Parsr
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "parsr"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "parsr"
7
+ s.version = Parsr::VERSION
8
+ s.authors = ["Jean Boussier"]
9
+ s.email = ["jean.boussier@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Simple parser to safe eval ruby literals}
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
16
+ s.require_paths = ["lib"]
17
+
18
+ s.add_development_dependency "rspec"
19
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr::ArrayRule do
4
+ include_context 'rule'
5
+
6
+ describe '.match' do
7
+
8
+ it 'should be able to parse an empty array' do
9
+ match('[]').value.should be == []
10
+ end
11
+
12
+ it 'should yield to get its content values' do
13
+ values = [3, 2, 1]
14
+
15
+ result = match('[1,2,3]') do |scanner|
16
+ val = values.pop
17
+ scanner.scan(Regexp.new(val.to_s))
18
+ Parsr::Token.new(val)
19
+ end
20
+
21
+ result.value.should be == [1, 2, 3]
22
+ end
23
+
24
+ it 'should not be disturbed by a trailing comma' do
25
+ values = [3, 2, 1]
26
+
27
+ result = match('[1,2,3,]') do |scanner|
28
+ val = values.pop
29
+ scanner.scan(Regexp.new(val.to_s))
30
+ Parsr::Token.new(val) if val
31
+ end
32
+
33
+ result.value.should be == [1, 2, 3]
34
+ end
35
+
36
+ it 'should not be disturbed by spaces around commas' do
37
+ values = [3, 2, 1]
38
+
39
+ result = match("[ 1 ,\t2\r\n, 3 ]") do |scanner|
40
+ val = values.pop
41
+ scanner.scan(Regexp.new(val.to_s))
42
+ Parsr::Token.new(val)
43
+ end
44
+
45
+ result.value.should be == [1, 2, 3]
46
+ end
47
+
48
+ it 'should be able to parse a final unenclosed Hash' do
49
+ values = [6, 5, 4, 3, 2, 1]
50
+
51
+ result = match("[1, 2, 3 => 4, 5=>6]") do |scanner|
52
+ val = values.pop
53
+ scanner.scan(Regexp.new(val.to_s))
54
+ Parsr::Token.new(val) if val
55
+ end
56
+
57
+ result.value.should be == [1, 2, {3 => 4, 5 => 6}]
58
+ end
59
+
60
+ it 'should be able to parse a final unenclosed Ruby1.9 Hash' do
61
+ values = [6, 5, 4, 2, 1]
62
+
63
+ result = match("[1, 2, foo: 4, 5=>6]") do |scanner|
64
+ val = values.pop
65
+ scanner.scan(/\s*/)
66
+ scanner.scan(Regexp.new(val.to_s))
67
+ Parsr::Token.new(val) if val
68
+ end
69
+
70
+ result.value.should be == [1, 2, {:foo => 4, 5 => 6}]
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr::FloatRule do
4
+ include_context 'rule'
5
+
6
+ describe '.match' do
7
+
8
+ it 'should not match integers' do
9
+ match('42').should be_nil
10
+ end
11
+
12
+ it 'should match a simple float' do
13
+ match('42.42').value.should be == 42.42
14
+ end
15
+
16
+ it 'should match positive signed floats' do
17
+ match('+42.42').value.should be == 42.42
18
+ end
19
+
20
+ it 'should match negative signed floats' do
21
+ match('-42.42').value.should be == -42.42
22
+ end
23
+
24
+ it 'should match floats with a implicit 0 unit' do
25
+ match('.42').value.should be == 0.42
26
+ end
27
+
28
+ it 'should match positive signed floats with a implicit 0 unit' do
29
+ match('+.42').value.should be == 0.42
30
+ end
31
+
32
+ it 'should match negative signed floats with a implicit 0 unit' do
33
+ match('-.42').value.should be == -0.42
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr::HashRule do
4
+ include_context 'rule'
5
+
6
+ describe '.match' do
7
+
8
+ it 'should be able to parse an empty hash' do
9
+ match('{}').value.should be == {}
10
+ end
11
+
12
+ it 'should yield to get its content values' do
13
+ values = [1, 2, 3, 4]
14
+
15
+ result = match("{1 => 2, 3 => 4}") do |scanner|
16
+ val = values.shift
17
+ scanner.scan(Regexp.new(Regexp.escape(val.inspect)))
18
+ Parsr::Token.new(val) if val
19
+ end
20
+
21
+ result.value.should be == {1 => 2, 3 => 4}
22
+ end
23
+
24
+ it 'should not be disturbed by a trailing comma' do
25
+ values = [1, 2, 3, 4]
26
+
27
+ result = match("{1 => 2, 3 => 4, }") do |scanner|
28
+ val = values.shift
29
+ scanner.scan(/\s*/)
30
+ scanner.scan(Regexp.new(Regexp.escape(val.inspect)))
31
+ scanner.scan(/\s*/)
32
+ Parsr::Token.new(val) if val
33
+ end
34
+
35
+ result.value.should be == {1 => 2, 3 => 4}
36
+ end
37
+
38
+ it 'should not be disturbed by spaces around commas' do
39
+ values = [1, 2, 3, 4]
40
+
41
+ result = match("{1\t=>\n\r 2, 3 => 4, }") do |scanner|
42
+ val = values.shift
43
+ scanner.scan(/\s*/)
44
+ scanner.scan(Regexp.new(Regexp.escape(val.inspect)))
45
+ scanner.scan(/\s*/)
46
+ Parsr::Token.new(val) if val
47
+ end
48
+
49
+ result.value.should be == {1 => 2, 3 => 4}
50
+ end
51
+
52
+ it 'should support Ruby 1.9 hash syntax' do
53
+ values = [1, 2, 4]
54
+
55
+ result = match("{1 => 2, foo: 4}") do |scanner|
56
+ val = values.shift
57
+ scanner.scan(/\s*/)
58
+ scanner.scan(Regexp.new(val.inspect))
59
+ Parsr::Token.new(val) if val
60
+ end
61
+
62
+ result.value.should be == {1 => 2, :foo => 4}
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr::IntegerRule do
4
+ include_context 'rule'
5
+
6
+ describe '.match' do
7
+
8
+ it 'should match digits' do
9
+ result = match('42')
10
+ result.should be_a(Parsr::Token)
11
+ result.value.should be == 42
12
+ end
13
+
14
+ it 'should match only digits' do
15
+ result = match('42abcd')
16
+ result.should be_a(Parsr::Token)
17
+ result.value.should be == 42
18
+ end
19
+
20
+ it 'should also match prepended sign' do
21
+ result = match('+42')
22
+ result.should be_a(Parsr::Token)
23
+ result.value.should be == 42
24
+
25
+ result = match('-42')
26
+ result.should be_a(Parsr::Token)
27
+ result.value.should be == -42
28
+ end
29
+
30
+ it 'should not return a Token if it does not match' do
31
+ result = match('AZIJjkdjfs42|')
32
+ result.should_not be_a(Parsr::Token)
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe Parsr::RawStringRule do
5
+ include_context 'rule'
6
+
7
+ describe '.match' do
8
+
9
+ it 'should match an empty string' do
10
+ match(%q{''}).value.should be == ''
11
+ end
12
+
13
+ it 'should match a simple word' do
14
+ match(%q{'foo'}).value.should be == 'foo'
15
+ end
16
+
17
+ it 'should match a simple sentence' do
18
+ match(%q{'foo bar'}).value.should be == 'foo bar'
19
+ end
20
+
21
+ it 'should match a simple sentence with punctuation' do
22
+ match(%q{'foo bar.,;$?!'}).value.should be == 'foo bar.,;$?!'
23
+ end
24
+
25
+ it 'should match a simple string with unicode characters' do
26
+ match(%q{'éùäüûꀢ©◊√'}).value.should be == 'éùäüûꀢ©◊√'
27
+ end
28
+
29
+ it 'should match a string containing double quotes' do
30
+ match(%q{'"'}).value.should be == '"'
31
+ end
32
+
33
+ it 'should match a string containing a backslash' do
34
+ match(%q{'\n'}).value.should be == '\n'
35
+ end
36
+
37
+ it 'should match a string containing an escaped backslash' do
38
+ match(%q{'\\\\'}).value.should be == '\\'
39
+ end
40
+
41
+ it 'should match a string containing an escaped single quote' do
42
+ match(%q{'\\''}).value.should be == "'"
43
+ end
44
+
45
+ it 'should raise an Parsr::RawStringRule::Unterminated if raw string is not terminated' do
46
+ expect{
47
+ match(%q{'bryan\\'s kitchen})
48
+ }.to raise_error(Parsr::RawStringRule::Unterminated)
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe Parsr::StringRule do
5
+ include_context 'rule'
6
+
7
+ describe '.match' do
8
+
9
+ it 'should match an empty string' do
10
+ match(%q{""}).value.should be == ""
11
+ end
12
+
13
+ it 'should match a simple word' do
14
+ match(%q{"foo"}).value.should be == "foo"
15
+ end
16
+
17
+ it 'should match a simple sentence' do
18
+ match(%q{"foo bar"}).value.should be == "foo bar"
19
+ end
20
+
21
+ it 'should match a simple sentence with punctuation' do
22
+ match(%q{"foo bar.,;$?!"}).value.should be == "foo bar.,;$?!"
23
+ end
24
+
25
+ it 'should match a simple string with unicode characters' do
26
+ match(%{"éùäüûꀢ©◊√"}).value.should be == "éùäüûꀢ©◊√"
27
+ end
28
+
29
+ it 'should match a string containing a simple quotes' do
30
+ match(%q{"'"}).value.should be == "'"
31
+ end
32
+
33
+ it 'should match a string containing a backslash' do
34
+ match(%q{"\n"}).value.should be == "\n"
35
+ end
36
+
37
+ it 'should match a string containing an escaped backslash' do
38
+ match(%q{"\\\\"}).value.should be == "\\"
39
+ end
40
+
41
+ it 'should match a string containing an escaped double quote' do
42
+ match('"\\""').value.should be == '"'
43
+ end
44
+
45
+ it 'should raise an Parsr::RawStringRule::Unterminated if raw string is not terminated' do
46
+ expect{
47
+ match(%q{"bryan\\"s kitchen})
48
+ }.to raise_error
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr::SymbolRule do
4
+ include_context 'rule'
5
+
6
+ it 'should match identifiers' do
7
+ match(':_').value.should be == :_
8
+ match(':foo').value.should be == :foo
9
+ match(':bar42').value.should be == :bar42
10
+ end
11
+
12
+ it 'should not match invalid identitifers' do
13
+ match(':42').should be_nil
14
+ match(':-').should be_nil
15
+ end
16
+
17
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parsr do
4
+
5
+ it 'should have a VERSION' do
6
+ Parsr::VERSION.should be_a(String)
7
+ end
8
+
9
+ describe '#parse' do
10
+
11
+ let(:first_rule) { mock(:rule) }
12
+
13
+ let(:second_rule) { mock(:rule) }
14
+
15
+ let(:rules) { [first_rule, second_rule] }
16
+
17
+ subject{ Parsr.new(*rules) }
18
+
19
+ context 'call #match on all its rules' do
20
+
21
+ it 'should raise an IllegalValue if none of theses return a token' do
22
+ first_rule.should_receive(:match).and_return(true)
23
+ second_rule.should_receive(:match).and_return(false)
24
+ expect{
25
+ subject.parse('foo')
26
+ }.to raise_error(Parsr::IllegalValue)
27
+ end
28
+
29
+ it 'should return first matched token value' do
30
+ first_rule.should_receive(:match).and_return(true)
31
+ second_rule.should_receive(:match).and_return(Parsr::Token.new(42))
32
+ subject.parse('foo').should be == 42
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ describe '.safe_literal_eval' do
40
+
41
+ it 'should be able to parse some complexes literals' do
42
+ Parsr::safe_literal_eval(%q{
43
+ [1, "2", foo: {egg: 'spam', 'bar,' => 'plop'}, 3 => 4.2]
44
+ }).should be == [1, "2", {:foo => {:egg => 'spam', 'bar,' => 'plop'}, 3 => 4.2}]
45
+ end
46
+
47
+ it 'should be able to parse an array included in another array' do
48
+ Parsr::safe_literal_eval(%q{
49
+ [1, [2], 3]
50
+ }).should be == [1, [2], 3]
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,9 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.dirname(__FILE__) + '/../lib/parsr'
3
+ require 'rspec'
4
+
5
+ RSpec.configure do |c|
6
+ c.mock_with :rspec
7
+ end
8
+
9
+ Dir[File.dirname(__FILE__) + '/support/**/*.rb'].each{ |f| require f }
@@ -0,0 +1,8 @@
1
+ shared_context 'rule' do
2
+
3
+ def match(string)
4
+ scanner = StringScanner.new(string)
5
+ described_class.match(scanner) { yield scanner if block_given? }
6
+ end
7
+
8
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: parsr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jean Boussier
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-13 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70315696184660 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70315696184660
25
+ description:
26
+ email:
27
+ - jean.boussier@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - .rspec
34
+ - Gemfile
35
+ - Rakefile
36
+ - lib/parsr.rb
37
+ - lib/parsr/array_rule.rb
38
+ - lib/parsr/float_rule.rb
39
+ - lib/parsr/hash_rule.rb
40
+ - lib/parsr/integer_rule.rb
41
+ - lib/parsr/raw_string_rule.rb
42
+ - lib/parsr/string_rule.rb
43
+ - lib/parsr/symbol_rule.rb
44
+ - lib/parsr/token.rb
45
+ - lib/parsr/version.rb
46
+ - parsr.gemspec
47
+ - spec/parsr/array_rule_spec.rb
48
+ - spec/parsr/float_rule_spec.rb
49
+ - spec/parsr/hash_rule_spec.rb
50
+ - spec/parsr/integer_rule_spec.rb
51
+ - spec/parsr/raw_string_rule_spec.rb
52
+ - spec/parsr/string_rule_spec.rb
53
+ - spec/parsr/symbol_rule_spec.rb
54
+ - spec/parsr_spec.rb
55
+ - spec/spec_helper.rb
56
+ - spec/support/rule_shared_context.rb
57
+ homepage: ''
58
+ licenses: []
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ requirements: []
76
+ rubyforge_project:
77
+ rubygems_version: 1.8.6
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Simple parser to safe eval ruby literals
81
+ test_files:
82
+ - spec/parsr/array_rule_spec.rb
83
+ - spec/parsr/float_rule_spec.rb
84
+ - spec/parsr/hash_rule_spec.rb
85
+ - spec/parsr/integer_rule_spec.rb
86
+ - spec/parsr/raw_string_rule_spec.rb
87
+ - spec/parsr/string_rule_spec.rb
88
+ - spec/parsr/symbol_rule_spec.rb
89
+ - spec/parsr_spec.rb
90
+ - spec/spec_helper.rb
91
+ - spec/support/rule_shared_context.rb