ptolemy 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.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ptolemy.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ guard 'rspec', :version => 2 do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
4
+ watch(%r{^lib/(.+)\.tt$}) { "spec" }
5
+ watch('spec/spec_helper.rb') { "spec" }
6
+ end
7
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Natansh Verma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,5 @@
1
+ ![Ptolemy](https://raw.github.com/natansh/ptolemy/master/ptolemy.jpg)
2
+ [![Build Status](https://travis-ci.org/natansh/ptolemy.png?branch=master)](https://travis-ci.org/natansh/ptolemy)
3
+ =======
4
+
5
+ Ptolemy is a simple TOML parser.
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new
6
+
7
+ task :test => :spec
8
+
9
+ task :default => :spec
@@ -0,0 +1,7 @@
1
+ # encoding: UTF-8
2
+
3
+ module Ptolemy
4
+
5
+ class ParseError < StandardError; end
6
+
7
+ end
@@ -0,0 +1,142 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'set'
4
+
5
+ require 'ptolemy/exceptions'
6
+
7
+ module Ptolemy
8
+ module TOML
9
+
10
+ class Toml < Treetop:: Runtime::SyntaxNode
11
+ def to_value
12
+ result = {}
13
+ # Keep track of location under which all the key value pairs must be
14
+ # stored. It gets modified when a key group is encountered.
15
+ current = result
16
+ # Store all key groups detected so that duplicates can be discovered.
17
+ key_group_set = Set.new
18
+
19
+ list.elements.map do |item|
20
+ elem = item.elem
21
+ if elem.is_a? KeyGroup
22
+ # Reset current to root level. Key value groups always specify
23
+ # nesting from root
24
+ current = result
25
+ key_group = elem.to_value
26
+ if key_group_set.include? key_group
27
+ raise ParseError, "Already defined [#{key_group}] before."
28
+ end
29
+ key_group_set.add key_group
30
+ # If the key group is x.y.z.w, create the whole nested structure
31
+ # in case it doesn't exist already.
32
+ key_group.each do |key|
33
+ current[key] = {} if current[key].nil?
34
+ current = current[key]
35
+ end
36
+ else
37
+ key, value = elem.to_value
38
+ # Set value in hash, if it hasn't been set already.
39
+ if current[key].nil?
40
+ current[key] = value
41
+ else
42
+ raise ParseError, "Duplicate value for key:#{key}"
43
+ end
44
+ end
45
+ end
46
+ result
47
+ end
48
+ end
49
+
50
+ class KeyGroup < Treetop::Runtime::SyntaxNode
51
+ def to_value
52
+ result = [keys.key.to_value]
53
+ keys.remaining_keys.elements.each do |elem|
54
+ result << elem.key.to_value
55
+ end
56
+ result
57
+ end
58
+ end
59
+
60
+ class KeyValue < Treetop::Runtime::SyntaxNode
61
+ def to_value
62
+ [key.to_value, value.to_value]
63
+ end
64
+ end
65
+
66
+ class Key < Treetop::Runtime::SyntaxNode
67
+ def to_value
68
+ text_value
69
+ end
70
+ end
71
+
72
+ class Comment < Treetop::Runtime::SyntaxNode
73
+ end
74
+
75
+ class ArrayLiteral < Treetop::Runtime::SyntaxNode
76
+ def to_value
77
+ result = list.elements.map do|elem|
78
+ elem.item.to_value
79
+ end
80
+ unless last.empty?
81
+ result << last.item.to_value
82
+ end
83
+ result
84
+ end
85
+ end
86
+
87
+ class IntegerLiteral < Treetop::Runtime::SyntaxNode
88
+ def to_value
89
+ text_value.to_i
90
+ end
91
+ end
92
+
93
+ class FloatLiteral < Treetop::Runtime::SyntaxNode
94
+ def to_value
95
+ text_value.to_f
96
+ end
97
+ end
98
+
99
+ class BooleanLiteral < Treetop::Runtime::SyntaxNode
100
+ def to_value
101
+ text_value == 'true'
102
+ end
103
+ end
104
+
105
+ class StringLiteral < Treetop::Runtime::SyntaxNode
106
+
107
+ private
108
+
109
+ @@unescape = {
110
+ '\b' => "\b",
111
+ '\t' => "\t",
112
+ '\n' => "\n",
113
+ '\\' => "\\",
114
+ '\f' => "\f",
115
+ '\r' => "\r",
116
+ '\"' => "\"",
117
+ '\/' => "\/"
118
+ }
119
+
120
+ public
121
+
122
+ def to_value
123
+ elem = string.text_value
124
+ # Unescape unicode characters of the form \uXXXX
125
+ # Pack the XXXX string as hexadecimal ---> Unpack to array of integers
126
+ # ---> Pack back into unicode
127
+ elem.gsub!(/\\u([\da-fA-F]{4})/) {|m| [$1].pack("H*").unpack("n*").pack("U*")}
128
+ # Unescape known escape characters
129
+ elem.gsub!(/(\\[btn\\fr"\/])/) {|m| @@unescape[m]}
130
+ elem
131
+ end
132
+ end
133
+
134
+ class DateLiteral < Treetop::Runtime::SyntaxNode
135
+ def to_value
136
+ args = [year, month, day, hour, min, sec].map(&:text_value).map(&:to_i)
137
+ DateTime.new(*args)
138
+ end
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'treetop'
4
+
5
+ require 'ptolemy/nodes'
6
+ require 'ptolemy/exceptions'
7
+
8
+ module Ptolemy
9
+
10
+ class Parser
11
+
12
+ # Load TOML grammar into Treetop
13
+ base_path = File.expand_path File.dirname(__FILE__)
14
+ Treetop.load File.join(base_path, 'toml.tt')
15
+
16
+ # Treetop would've generated a parser for TOML based on the grammar
17
+ @@parser = TOMLParser.new
18
+
19
+ def self.parse data
20
+ # Data should be a valid UTF-8 encoded string.
21
+ if data.encoding != Encoding::UTF_8
22
+ raise ParseError, "Input is not UTF-8 encoded"
23
+ end
24
+ unless data.valid_encoding?
25
+ raise ParseError, "Input contains invalid UTF-8 byte sequence"
26
+ end
27
+
28
+ tree = @@parser.parse data
29
+
30
+ if tree.nil?
31
+ raise ParseError, "Parse error at offset: #{@@parser.index}"
32
+ end
33
+
34
+ tree.to_value
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,73 @@
1
+ module Ptolemy
2
+
3
+ grammar TOML
4
+
5
+ rule toml
6
+ list:((separator* elem:(key_group / key_value))*) separator* <Toml>
7
+ end
8
+
9
+ rule key_group
10
+ '[' keys:(key remaining_keys:('.' key)*) ']' <KeyGroup>
11
+ end
12
+
13
+ rule key_value
14
+ key space? '=' space? value <KeyValue>
15
+ end
16
+
17
+ rule key
18
+ [a-zA-Z_] [a-zA-Z0-9_?#!]* <Key>
19
+ end
20
+
21
+ rule value
22
+ (boolean / date / float / integer / string / array)
23
+ end
24
+
25
+ rule separator
26
+ comment / space / newline
27
+ end
28
+
29
+ rule newline
30
+ "\n" / "\r\n"
31
+ end
32
+
33
+ rule comment
34
+ '#' (!newline .)* <Comment>
35
+ end
36
+
37
+ rule array
38
+ '[' list:(separator* item:string separator* ',')* separator* last:(separator* item:string separator*)? ']' <ArrayLiteral> /
39
+ '[' list:(separator* item:date separator* ',')* separator* last:(separator* item:date separator*)? ']' <ArrayLiteral> /
40
+ '[' list:(separator* item:float separator* ',')* separator* last:(separator* item:float separator*)? ']' <ArrayLiteral> /
41
+ '[' list:(separator* item:integer separator* ',')* separator* last:(separator* item:integer separator*)? ']' <ArrayLiteral> /
42
+ '[' list:(separator* item:boolean separator* ',')* separator* last:(separator* item:boolean separator*)? ']' <ArrayLiteral> /
43
+ '[' list:(separator* item:array separator* ',')* separator* last:(separator* item:array separator*)? ']' <ArrayLiteral>
44
+ end
45
+
46
+ rule string
47
+ '"' string:([^"\\] / "\\" . )* '"' <StringLiteral>
48
+ end
49
+
50
+ rule date
51
+ year:([0-9] 4..4) '-' month:([0-9] 2..2) '-' day:([0-9] 2..2)
52
+ 'T' hour:([0-9] 2..2) ':' min:([0-9] 2..2) ':' sec:([0-9] 2..2) 'Z' <DateLiteral>
53
+ end
54
+
55
+ rule float
56
+ ('+' / '-')? [0-9]+ ('.' [0-9]+) <FloatLiteral>
57
+ end
58
+
59
+ rule integer
60
+ ('+' / '-')? [0-9]+ <IntegerLiteral>
61
+ end
62
+
63
+ rule boolean
64
+ ('true' <BooleanLiteral> / 'false' <BooleanLiteral>)
65
+ end
66
+
67
+ rule space
68
+ [ \t]+
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: UTF-8
2
+
3
+ module Ptolemy
4
+ VERSION = "0.0.1"
5
+ end
data/lib/ptolemy.rb ADDED
@@ -0,0 +1,19 @@
1
+ # encoding: UTF-8
2
+
3
+ require "ptolemy/version"
4
+ require "ptolemy/parser"
5
+
6
+ module Ptolemy
7
+
8
+ def self.parse data
9
+ Parser.parse data
10
+ end
11
+
12
+ def self.parse_file filename
13
+ File.open filename, 'r:utf-8' do |file|
14
+ # TODO: Should the check for valid UTF-8 be done over here?
15
+ return Parser.parse file.read
16
+ end
17
+ end
18
+
19
+ end
data/ptolemy.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ptolemy/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Natansh Verma"]
6
+ gem.email = ["natansh.verma@gmail.com"]
7
+ gem.description = %q{Ptolemy is a TOML parser.}
8
+ gem.summary = %q{Ptolemy is a TOML parser.}
9
+ gem.homepage = "http://www.github.com/natansh/ptolemy"
10
+
11
+ gem.files = `git ls-files`.split($\) - %w(.gitignore ptolemy.jpg)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "ptolemy"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Ptolemy::VERSION
17
+
18
+ gem.required_ruby_version = ">= 1.9.3"
19
+
20
+ gem.add_dependency "treetop"
21
+
22
+ gem.add_development_dependency "rspec"
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "guard"
25
+ gem.add_development_dependency "guard-rspec"
26
+ end
data/spec/example.toml ADDED
@@ -0,0 +1,36 @@
1
+ # By Tom Preston-Werner (@mojombo)
2
+ # This is a TOML document. Boom.
3
+
4
+ title = "TOML Example"
5
+
6
+ [owner]
7
+ name = "Tom Preston-Werner"
8
+ organization = "GitHub"
9
+ bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
10
+ dob = 1979-05-27T07:32:00Z # First class dates? Why not?
11
+
12
+ [database]
13
+ server = "192.168.1.1"
14
+ ports = [ 8001, 8001, 8002 ]
15
+ connection_max = 5000
16
+ enabled = true
17
+
18
+ [servers]
19
+
20
+ # You can indent as you please. Tabs or spaces. TOML don't care.
21
+ [servers.alpha]
22
+ ip = "10.0.0.1"
23
+ dc = "eqdc10"
24
+
25
+ [servers.beta]
26
+ ip = "10.0.0.2"
27
+ dc = "eqdc10"
28
+
29
+ [clients]
30
+ data = [ ["gamma", "delta"], [1, 2] ]
31
+
32
+ # Line breaks are OK when inside arrays
33
+ hosts = [
34
+ "alpha",
35
+ "omega"
36
+ ]
@@ -0,0 +1,21 @@
1
+ # Test file for TOML
2
+ # Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate
3
+ # This part you'll really hate
4
+
5
+ [the]
6
+ test_string = "You'll hate me after this - #" # " Annoying, isn't it?
7
+
8
+ [the.hard]
9
+ test_array = [ "] ", " # "] # ] There you go, parse this!
10
+ test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ]
11
+ # You didn't think it'd as easy as chucking out the last #, did you?
12
+ another_test_string = " Same thing, but with a string #"
13
+ harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
14
+ # Things will get harder
15
+
16
+ [the.hard.bit#]
17
+ what? = "You don't think some user won't do that?"
18
+ multi_line_array = [
19
+ "]",
20
+ # ] Oh yes I did
21
+ ]
@@ -0,0 +1,122 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe Ptolemy::Parser do
6
+
7
+ describe "#parse" do
8
+ describe 'Valid TOML input' do
9
+ it 'should parse TOML specification file' do
10
+ filename = 'example.toml'
11
+ path = File.expand_path(File.join(File.dirname(__FILE__), filename))
12
+ result = Ptolemy.parse_file(path)
13
+
14
+ result['title'].should eql('TOML Example')
15
+
16
+ owner = result['owner']
17
+ owner.should be_a_kind_of(Hash)
18
+ owner.should eql({
19
+ 'name' => 'Tom Preston-Werner',
20
+ 'organization' => 'GitHub',
21
+ 'bio' => "GitHub Cofounder & CEO\nLikes tater tots and beer.",
22
+ # Test for Dates
23
+ 'dob' => DateTime.parse('1979-05-27T07:32:00Z')
24
+ })
25
+
26
+ database = result['database']
27
+ database.should be_a_kind_of(Hash)
28
+ database['ports'].should be_a_kind_of(Array)
29
+ database.should eql({
30
+ 'server' => "192.168.1.1",
31
+ 'ports' => [ 8001, 8001, 8002 ],
32
+ 'connection_max' => 5000,
33
+ 'enabled' => true
34
+ })
35
+
36
+ servers = result['servers']
37
+ servers.should be_a_kind_of(Hash)
38
+ servers.keys.sort!.should eql ['alpha', 'beta']
39
+
40
+ alpha = servers['alpha']
41
+ alpha.should be_a_kind_of(Hash)
42
+
43
+ beta = servers['beta']
44
+ beta.should be_a_kind_of(Hash)
45
+
46
+ clients = result['clients']
47
+ clients['data'].should eql([["gamma", "delta"], [1, 2]])
48
+
49
+ hosts = clients['hosts']
50
+ hosts.should eql(["alpha", "omega"])
51
+ end
52
+
53
+ it 'should parse hard TOML example file' do
54
+ filename = 'hard_example.toml'
55
+ path = File.expand_path(File.join(File.dirname(__FILE__), filename))
56
+ result = Ptolemy.parse_file(path)
57
+
58
+ the = result['the']
59
+ the.should be_a_kind_of(Hash)
60
+ the['test_string'].should eql("You'll hate me after this - #")
61
+ hard = the['hard']
62
+ hard['test_array'].should eql(["] ", " # "])
63
+ hard['test_array2'].should eql([ "Test #11 ]proved that",
64
+ "Experiment #9 was a success" ])
65
+ hard['another_test_string'].should eql(" Same thing, but with a string #")
66
+ hard['harder_test_string'].should eql(
67
+ " And when \"'s are in the string, along with # \"")
68
+
69
+ bit = hard['bit#']
70
+ bit.should be_a_kind_of(Hash)
71
+ bit['what?'].should eql("You don't think some user won't do that?")
72
+ bit['multi_line_array'].should eql([ "]"])
73
+ end
74
+ end
75
+
76
+ describe 'Invalid TOML input' do
77
+ it 'should give error if anything other than tabs/space/nl/comment'\
78
+ 'is present after key group on line' do
79
+ input = "[error] if you didn't catch this, your parser is broken"
80
+ expect{Ptolemy.parse(input)}.to raise_error(Ptolemy::ParseError)
81
+ end
82
+
83
+ it 'should give error if anything other than tabs/space/nl/comment'\
84
+ 'is present after key value pair on line' do
85
+ input = 'string = "Hello World!" Have fun'
86
+ expect{Ptolemy.parse(input)}.to raise_error(Ptolemy::ParseError)
87
+ input = "number = 3.14 pi <--again forgot the #"
88
+ expect{Ptolemy.parse(input)}.to raise_error(Ptolemy::ParseError)
89
+ end
90
+
91
+ it 'should give an error for a malformed multiline array' do
92
+ input = <<END
93
+ array = [
94
+ "This might most likely happen in multiline arrays",
95
+ Like here,
96
+ "or here,
97
+ and here"
98
+ ] End of array comment, forgot the #
99
+ END
100
+ expect{Ptolemy.parse(input)}.to raise_error(Ptolemy::ParseError)
101
+ end
102
+ end
103
+
104
+ describe 'Encoding' do
105
+ it 'should give an error if input is not UTF-8 encoded' do
106
+ input = 'hello = "world"'
107
+ input.encode!(Encoding::ASCII)
108
+ input.encoding.name.should eql('US-ASCII')
109
+ expect{Ptolemy.parse(input)}.to \
110
+ raise_error(Ptolemy::ParseError, "Input is not UTF-8 encoded")
111
+ end
112
+
113
+ it 'should give an error if input has invalid byte sequence' do
114
+ input = "hello = 'hi \255'"
115
+ input.encoding.name.should eql('UTF-8')
116
+ expect{Ptolemy.parse(input)}.to \
117
+ raise_error(Ptolemy::ParseError, "Input contains invalid UTF-8 byte sequence")
118
+ end
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+
6
+ require 'ptolemy'
7
+
8
+ Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
@@ -0,0 +1,18 @@
1
+ RSpec::Matchers.define :have_value do |expected|
2
+ match do |actual|
3
+ actual.respond_to?(:to_value) && actual.to_value == expected
4
+ end
5
+
6
+ failure_message_for_should do |actual|
7
+ "Expected that #{actual.class}(text value: #{actual.text_value}, value: #{actual.to_value}) should have value #{expected}"
8
+ end
9
+
10
+ failure_message_for_should_not do |actual|
11
+ "Expected that #{actual.class}(text value: #{actual.text_value}, value: #{actual.to_value}) should not have value #{expected}"
12
+ end
13
+
14
+ description do
15
+ "have the value #{expected}"
16
+ end
17
+ end
18
+
@@ -0,0 +1,235 @@
1
+ # encoding: UTF-8
2
+
3
+ require_relative 'spec_helper'
4
+
5
+ describe Ptolemy::TOMLParser do
6
+
7
+ before :all do
8
+ @parser = Ptolemy::TOMLParser.new
9
+ end
10
+
11
+ describe 'Literals' do
12
+ describe 'String' do
13
+ it 'should parse an empty string' do
14
+ result = @parser.parse('""', root: :string)
15
+ result.should_not be_nil
16
+ result.should be_a_kind_of(Ptolemy::TOML::StringLiteral)
17
+ result.should have_value("")
18
+ end
19
+
20
+ it 'should parse a simple string' do
21
+ result = @parser.parse('"This is a string"', root: :string)
22
+ result.should_not be_nil
23
+ result.should be_a_kind_of(Ptolemy::TOML::StringLiteral)
24
+ result.should have_value("This is a string")
25
+ end
26
+
27
+ it 'should parse a string having escaped characters' do
28
+ result = @parser.parse('"This is an \n\t \bescaped string."', root: :string)
29
+ result.should_not be_nil
30
+ result.should be_a_kind_of(Ptolemy::TOML::StringLiteral)
31
+ result.should have_value("This is an \n\t \bescaped string.")
32
+ end
33
+
34
+ it 'should parse a string having unicode symbols' do
35
+ result = @parser.parse('"This is a string containing úƞĩƈōƌě symbols."', root: :string)
36
+ result.should_not be_nil
37
+ result.should be_a_kind_of(Ptolemy::TOML::StringLiteral)
38
+ result.should have_value( "This is a string containing úƞĩƈōƌě symbols.")
39
+ end
40
+
41
+ it 'should parse a string having escaped unicode characters' do
42
+ result = @parser.parse('"This is a unicode string containing linefeed as \u000A."', root: :string)
43
+ result.should_not be_nil
44
+ result.should be_a_kind_of(Ptolemy::TOML::StringLiteral)
45
+ result.should have_value( "This is a unicode string containing linefeed as \n.")
46
+ end
47
+
48
+ it 'should not parse a string with single quotes' do
49
+ result = @parser.parse("'Hello World!'", root: :string)
50
+ result.should be_nil
51
+ end
52
+ end
53
+
54
+ it 'should parse a correct float' do
55
+ ['-0.0', '1.4', '123.2', '+1.1', '+123.9' ].each do |float|
56
+ result = @parser.parse(float, root: :float)
57
+ result.should_not be_nil
58
+ result.should be_a_kind_of(Ptolemy::TOML::FloatLiteral)
59
+ result.should have_value(float.to_f)
60
+ end
61
+ end
62
+
63
+ it 'should parse a correct integers' do
64
+ ['-1', '1', '123', '+1', '+123' ].each do |integer|
65
+ result = @parser.parse(integer, root: :integer)
66
+ result.should_not be_nil
67
+ result.should be_a_kind_of(Ptolemy::TOML::IntegerLiteral)
68
+ result.should have_value(integer.to_i)
69
+ end
70
+ end
71
+
72
+ it 'should parse a correct boolean' do
73
+ ['true', 'false' ].each do |boolean|
74
+ result = @parser.parse(boolean, root: :boolean)
75
+ result.should_not be_nil
76
+ result.should be_a_kind_of(Ptolemy::TOML::BooleanLiteral)
77
+ result.should have_value((boolean == 'true'))
78
+ end
79
+ end
80
+
81
+ it 'should parse a correct date' do
82
+ result = @parser.parse('1979-05-27T07:32:00Z', root: :date)
83
+ result.should_not be_nil
84
+ result.should be_a_kind_of(Ptolemy::TOML::DateLiteral)
85
+ result.should have_value(DateTime.new(1979, 5, 27, 7, 32, 0))
86
+ end
87
+
88
+ it 'should not parse an incorrect date' do
89
+ result = @parser.parse('1979-05-27T07:32:00', root: :date)
90
+ result.should be_nil
91
+ end
92
+ end
93
+
94
+ describe 'Array' do
95
+ it 'should parse a simple array' do
96
+ array_string = '[1, 2, 3]'
97
+ result = @parser.parse(array_string, root: :array)
98
+ result.should_not be_nil
99
+ result.should be_a_kind_of(Ptolemy::TOML::ArrayLiteral)
100
+ result.should have_value([1, 2, 3])
101
+ end
102
+
103
+ it 'should parse a complex multi-line array' do
104
+ array_string = <<AS_END
105
+ [ # Evil, must say
106
+ 1,
107
+
108
+ 2 ,
109
+ # Wait, you can put comments anywhere?
110
+
111
+ 4 ,
112
+ # What the... is this right?
113
+ ]
114
+ AS_END
115
+ array_string.chomp! #Heredoc introduces a newline at the end
116
+
117
+ result = @parser.parse(array_string, root: :array)
118
+ result.should_not be_nil
119
+ result.should be_a_kind_of(Ptolemy::TOML::ArrayLiteral)
120
+ result.should have_value([1, 2, 4])
121
+ end
122
+
123
+ it 'should parse a nested array' do
124
+ array_string = <<AS_END
125
+ [
126
+ [1, 2,
127
+ # Nested comment, yeah!
128
+ 3 ],
129
+ ["hello", "world"
130
+ # Now this is a doozy!
131
+ ]
132
+ ]
133
+ AS_END
134
+ array_string.chomp! #Heredoc introduces a newline at the end
135
+
136
+ result = @parser.parse(array_string, root: :array)
137
+ result.should_not be_nil
138
+ result.should be_a_kind_of(Ptolemy::TOML::ArrayLiteral)
139
+ result.should have_value([[1, 2, 3], ["hello", "world"]])
140
+ end
141
+ end
142
+
143
+ describe 'comment' do
144
+ it 'should parse a correct comment' do
145
+ result = @parser.parse('# This is a comment', root: :comment)
146
+ result.should_not be_nil
147
+ result.text_value.should eql '# This is a comment'
148
+ result.should be_a_kind_of(Ptolemy::TOML::Comment)
149
+ end
150
+ end
151
+
152
+ describe 'Constructs' do
153
+ it 'should parse a key' do
154
+ result = @parser.parse('hello', root: :key)
155
+ result.should_not be_nil
156
+ result.should be_a_kind_of(Ptolemy::TOML::Key)
157
+ end
158
+
159
+ it 'should parse a value' do
160
+ examples = {
161
+ '"hello"' => [Ptolemy::TOML::StringLiteral, 'hello'],
162
+ '-1.0' => [Ptolemy::TOML::FloatLiteral, -1.0],
163
+ '1' => [Ptolemy::TOML::IntegerLiteral, 1],
164
+ 'true' => [Ptolemy::TOML::BooleanLiteral, true],
165
+ '1979-05-27T07:32:00Z' => [Ptolemy::TOML::DateLiteral, DateTime.new(1979, 5, 27, 7, 32, 0)]
166
+ }
167
+
168
+ examples.each do |input, details|
169
+ klass, value = details
170
+ result = @parser.parse(input, root: :value)
171
+ result.should_not be_nil
172
+ result.should be_a_kind_of(klass)
173
+ result.should have_value(value)
174
+ end
175
+ end
176
+
177
+ it 'should parse a correct key value' do
178
+ examples = {
179
+ 'string' => '"Hello"',
180
+ 'date' => '1979-05-27T07:32:00Z',
181
+ 'integer' => '114',
182
+ 'float' => '1.0'
183
+ }
184
+ examples.each do |key, value|
185
+ result = @parser.parse("#{key} = \t #{value}", root: :key_value)
186
+ result.should_not be_nil
187
+ result.key.text_value.should eql key
188
+ result.value.text_value.should eql value
189
+ result.should be_a_kind_of(Ptolemy::TOML::KeyValue)
190
+ result.should respond_to(:to_value)
191
+ result.to_value[0].should eql key
192
+ end
193
+ end
194
+
195
+ it 'should parse a correct key group' do
196
+ result = @parser.parse('[key.hello.while]', root: :key_group)
197
+ result.should_not be_nil
198
+ result.should be_a_kind_of(Ptolemy::TOML::KeyGroup)
199
+ result.should have_value(['key', 'hello', 'while'])
200
+ end
201
+
202
+ it 'should not parse a key group having empty key' do
203
+ result = @parser.parse('[key..while]', root: :key_group)
204
+ result.should be_nil
205
+ end
206
+
207
+ it 'should parse valid toml' do
208
+ toml_string = <<TS_END
209
+ description = "This is valid TOML."
210
+ [personal.details]
211
+ name = "John Doe"
212
+ height = 186
213
+ date_of_birth = 1990-04-12T05:30:00Z
214
+ favorites = ["sports", "gaming"]
215
+ TS_END
216
+ result = @parser.parse(toml_string)
217
+ result.should_not be_nil
218
+ result.should be_a_kind_of(Ptolemy::TOML::Toml)
219
+ result.should respond_to(:to_value)
220
+
221
+ toml = result.to_value
222
+ toml['description'].should eql('This is valid TOML.')
223
+ toml['personal'].should be_a_kind_of(Hash)
224
+ details = toml['personal']['details']
225
+ details.should_not be_nil
226
+ details.should be_a_kind_of(Hash)
227
+ details['name'].should eql('John Doe')
228
+ details['height'].should eql(186)
229
+ details['date_of_birth'].should eql(DateTime.new(1990, 4, 12, 5, 30, 0))
230
+ details['favorites'].should be_a_kind_of(Array)
231
+ details['favorites'].should eql(["sports", "gaming"])
232
+ end
233
+ end
234
+
235
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ptolemy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Natansh Verma
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-09-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: treetop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: guard
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Ptolemy is a TOML parser.
95
+ email:
96
+ - natansh.verma@gmail.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .travis.yml
102
+ - Gemfile
103
+ - Guardfile
104
+ - LICENSE
105
+ - README.md
106
+ - Rakefile
107
+ - lib/ptolemy.rb
108
+ - lib/ptolemy/exceptions.rb
109
+ - lib/ptolemy/nodes.rb
110
+ - lib/ptolemy/parser.rb
111
+ - lib/ptolemy/toml.tt
112
+ - lib/ptolemy/version.rb
113
+ - ptolemy.gemspec
114
+ - spec/example.toml
115
+ - spec/hard_example.toml
116
+ - spec/parser_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/support/matchers/have_value.rb
119
+ - spec/toml_parser_spec.rb
120
+ homepage: http://www.github.com/natansh/ptolemy
121
+ licenses: []
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: 1.9.3
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ segments:
139
+ - 0
140
+ hash: -1717741281203789886
141
+ requirements: []
142
+ rubyforge_project:
143
+ rubygems_version: 1.8.24
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Ptolemy is a TOML parser.
147
+ test_files:
148
+ - spec/example.toml
149
+ - spec/hard_example.toml
150
+ - spec/parser_spec.rb
151
+ - spec/spec_helper.rb
152
+ - spec/support/matchers/have_value.rb
153
+ - spec/toml_parser_spec.rb