parsr 0.0.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.
@@ -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