Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #47 and #70 #71

Merged
merged 15 commits into from
Jun 9, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gems
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
citrus -v 3.0.2
minitest -v 5.7.0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you say that minitest is better ?
Honestly I didn't use minitest because I wanted to keep simple the gems dependencies even for dev.

Anyways, it's ok, I just wonder why did you need it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you look on the unit::test documentation http://ruby-doc.org/stdlib-2.1.1/libdoc/test/unit/rdoc/Test/Unit.html it state If you are writing new test code, please use MiniTest instead of Test::Unit.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a good point. 👍

7 changes: 4 additions & 3 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
task :test do
Dir['test/*_test.rb'].each { |file| load file }
end
require 'rake/testtask'

Rake::TestTask.new do |t|
t.pattern = 'test/*_test.rb'
end
task default: :test
1 change: 1 addition & 0 deletions init.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

ROOT = File.dirname(File.expand_path(__FILE__))

require "#{ROOT}/lib/toml/errors"
require "#{ROOT}/lib/toml/string"
require "#{ROOT}/lib/toml/table_array"
require "#{ROOT}/lib/toml/inline_table"
Expand Down
17 changes: 17 additions & 0 deletions lib/toml/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module TOML
# Parent class for all TOML errors
Error = Class.new(StandardError)

# Error related to parsing.
ParseError = Class.new(Error)

# Overwrite error
class ValueOverwriteError < Error
attr_accessor :key

def initialize(key)
self.key = key
super "Key #{key.inspect} is defined more than once"
end
end
end
4 changes: 2 additions & 2 deletions lib/toml/grammars/document.citrus
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ grammar TOML::Document
end

rule table_array
(space? '[[' (space? key space? "."?)+ ']]' space? comment?) <TOML::TableArrayParser>
(space? '[[' ((key "."?)+|'"'(space? key space? "."?)+'"') ']]' space? comment?) <TOML::TableArrayParser>
end

rule keygroup
(space? '[' (space? key space? "."?)+ ']' space? comment?) <TOML::KeygroupParser>
(space? '[' ((key "."?)+|'"'(space? key space? "."?)+'"') ']' space? comment?) <TOML::KeygroupParser>
end

rule keyvalue
Expand Down
4 changes: 2 additions & 2 deletions lib/toml/grammars/primitive.citrus
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ grammar TOML::Primitive
end

rule multiline_string
('"""' line_break* text:~'"""' '"""' space) <TOML::MultilineString>
('"""' line_break* (text:~'"""'|'') '"""' space) <TOML::MultilineString>
end

rule multiline_literal
("'''" line_break* text:~"'''" "'''" space) <TOML::MultilineLiteral>
("'''" line_break* (text:~"'''"|'') "'''" space) <TOML::MultilineLiteral>
end

##
Expand Down
21 changes: 13 additions & 8 deletions lib/toml/keygroup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,32 @@ def initialize(nested_keys)
@nested_keys = nested_keys
end

def navigate_keys(hash, symbolize_keys = false)
last_index = @nested_keys.length - 1
@nested_keys.each_with_index do |key, i|
def navigate_keys(hash, visited_keys, symbolize_keys = false)
ensure_key_not_defined(visited_keys)
@nested_keys.each do |key|
key = symbolize_keys ? key.to_sym : key
# do not allow to define more than once just the last key
if i == last_index && hash.key?(key)
fail ValueOverwriteError.new(key)
end
hash[key] = {} unless hash.key?(key)
element = hash[key]
hash = element.is_a?(Array) ? element.last : element
# check that key has not been defined before as a scalar value
fail ValueOverwriteError.new(key) unless hash.is_a?(Hash)
end

hash
end

# Fail if the key was already defined with a ValueOverwriteError
def ensure_key_not_defined(visited_keys)
fail ValueOverwriteError.new(full_key) if visited_keys.include?(full_key)
visited_keys << full_key
end

def accept_visitor(parser)
parser.visit_keygroup self
end

def full_key
@nested_keys.join('.')
end
end
# Used in document.citrus
module KeygroupParser
Expand Down
9 changes: 0 additions & 9 deletions lib/toml/keyvalue.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
module TOML
class ValueOverwriteError < StandardError
attr_accessor :key

def initialize(key)
self.key = key
super "Key #{key.inspect} is defined more than once"
end
end

class Keyvalue
attr_reader :value, :symbolize_keys

Expand Down
7 changes: 3 additions & 4 deletions lib/toml/parser.rb
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
module TOML
class ParseError < StandardError; end

class Parser
attr_reader :hash

def initialize(content, options = {})
@hash = {}
@visited_keys = Set.new
@current = @hash
@symbolize_keys = options[:symbolize_keys]

begin
parsed = TOML::Document.parse(content)
parsed.matches.map(&:value).compact.each { |m| m.accept_visitor(self) }
rescue Citrus::ParseError => e
raise ParseError.new(e.message)
raise TOML::ParseError.new(e.message)
end
end

Expand All @@ -24,7 +23,7 @@ def visit_table_array(table_array)
end

def visit_keygroup(keygroup)
@current = keygroup.navigate_keys @hash, @symbolize_keys
@current = keygroup.navigate_keys @hash, @visited_keys, @symbolize_keys
end

def visit_keyvalue(keyvalue)
Expand Down
22 changes: 20 additions & 2 deletions lib/toml/string.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,29 @@ def value
aux[1...-1]
end

def self.transform_escaped_chars(str)
# Replace the unicode escaped characters with the corresponding character
# e.g. \u03B4 => ?
def self.decode_unicode(str)
str.gsub(/([^\\](?:\\\\)*\\u[\da-f]{4})/i) do |m|
m[0...-6] + [m[-4..-1].to_i(16)].pack('U')
end
end

# Replace special characters such as line feed and tabs.
def self.decode_special_char(str)
str.gsub(/\\0/, "\0")
.gsub(/\\t/, "\t")
.gsub(/\\b/, "\b")
.gsub(/\\f/, "\f")
.gsub(/\\n/, "\n")
.gsub(/\\\"/, '"')
.gsub(/\\r/, "\r")
.gsub(/\\\\/, '\\')
end

def self.transform_escaped_chars(str)
str = decode_special_char(str)
str = decode_unicode(str)
str.gsub(/\\\\/, '\\').encode('utf-8')
end
end

Expand All @@ -25,6 +41,7 @@ def value

module MultilineString
def value
return '' if captures[:text].empty?
aux = captures[:text].first.value

# Remove spaces on multilined Singleline strings
Expand All @@ -36,6 +53,7 @@ def value

module MultilineLiteral
def value
return '' if captures[:text].empty?
aux = captures[:text].first.value

aux.gsub(/\\\r?\n[\n\t\r ]*/, '')
Expand Down
4 changes: 4 additions & 0 deletions lib/toml/table_array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def navigate_keys(hash, symbolize_keys = false)
end

# Define Table Array
if hash[last_key].is_a? Hash
fail TOML::ParseError,
"#{last_key} was defined as hash but is now redefined as a table!"
end
hash[last_key] = [] unless hash[last_key]
hash[last_key] << {}

Expand Down
2 changes: 1 addition & 1 deletion test/dumper_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require_relative 'helper'

class DumperTest < Test::Unit::TestCase
class DumperTest < Minitest::Test
def test_dump_empty
dumped = TOML.dump({})
assert_equal('', dumped)
Expand Down
4 changes: 2 additions & 2 deletions test/errors_test.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
require_relative 'helper'

class ErrorsTest < Test::Unit::TestCase
class ErrorsTest < Minitest::Test
def test_text_after_keygroup
str = "[error] if you didn't catch this, your parser is broken"
assert_raises(TOML::ParseError) { TOML.parse(str) }
end

def test_text_after_string
str = 'string = "Anything other than tabs, spaces and newline after a '
str = 'string = "Anything other than tabs, spaces and newline after a '
str += 'keygroup or key value pair has ended should produce an error '
str += 'unless it is a comment" like this'

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
arrays-and-ints = [1, ["Arrays are not integers."]]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ints-and-floats = [1, 1.1]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
strings-and-ints = ["hi", 42]
1 change: 1 addition & 0 deletions test/examples/invalid/datetime-malformed-no-leads.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-leads = 1987-7-05T17:45:00Z
1 change: 1 addition & 0 deletions test/examples/invalid/datetime-malformed-no-secs.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-secs = 1987-07-05T17:45Z
1 change: 1 addition & 0 deletions test/examples/invalid/datetime-malformed-no-t.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-t = 1987-07-0517:45:00Z
1 change: 1 addition & 0 deletions test/examples/invalid/datetime-malformed-no-z.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-z = 1987-07-05T17:45:00
1 change: 1 addition & 0 deletions test/examples/invalid/datetime-malformed-with-milli.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
with-milli = 1987-07-5T17:45:00.12Z
5 changes: 5 additions & 0 deletions test/examples/invalid/duplicate-key-table.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[fruit]
type = "apple"

[fruit.type]
apple = "yes"
2 changes: 2 additions & 0 deletions test/examples/invalid/duplicate-keys.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dupe = false
dupe = true
2 changes: 2 additions & 0 deletions test/examples/invalid/duplicate-tables.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[a]
[a]
1 change: 1 addition & 0 deletions test/examples/invalid/empty-implicit-table.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[naughty..naughty]
1 change: 1 addition & 0 deletions test/examples/invalid/empty-table.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
2 changes: 2 additions & 0 deletions test/examples/invalid/float-no-leading-zero.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
answer = .12345
neganswer = -.12345
2 changes: 2 additions & 0 deletions test/examples/invalid/float-no-trailing-digits.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
answer = 1.
neganswer = -1.
1 change: 1 addition & 0 deletions test/examples/invalid/key-empty.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
= 1
1 change: 1 addition & 0 deletions test/examples/invalid/key-hash.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a# = 1
2 changes: 2 additions & 0 deletions test/examples/invalid/key-newline.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
a
= 1
1 change: 1 addition & 0 deletions test/examples/invalid/key-open-bracket.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[abc = 1
1 change: 1 addition & 0 deletions test/examples/invalid/key-single-open-bracket.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[
1 change: 1 addition & 0 deletions test/examples/invalid/key-space.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a b = 1
3 changes: 3 additions & 0 deletions test/examples/invalid/key-start-bracket.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[a]
[xyz = 5
[b]
1 change: 1 addition & 0 deletions test/examples/invalid/key-two-equals.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
key= = 1
1 change: 1 addition & 0 deletions test/examples/invalid/string-no-close.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no-ending-quote = "One time, at band camp
14 changes: 14 additions & 0 deletions test/examples/invalid/table-array-implicit.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# This test is a bit tricky. It should fail because the first use of
# `[[albums.songs]]` without first declaring `albums` implies that `albums`
# must be a table. The alternative would be quite weird. Namely, it wouldn't
# comply with the TOML spec: "Each double-bracketed sub-table will belong to
# the most *recently* defined table element *above* it."
#
# This is in contrast to the *valid* test, table-array-implicit where
# `[[albums.songs]]` works by itself, so long as `[[albums]]` isn't declared
# later. (Although, `[albums]` could be.)
[[albums.songs]]
name = "Glory Days"

[[albums]]
name = "Born in the USA"
2 changes: 2 additions & 0 deletions test/examples/invalid/table-array-malformed-bracket.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[albums]
name = "Born to Run"
2 changes: 2 additions & 0 deletions test/examples/invalid/table-array-malformed-empty.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[[]]
name = "Born to Run"
1 change: 1 addition & 0 deletions test/examples/invalid/table-empty.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
2 changes: 2 additions & 0 deletions test/examples/invalid/table-nested-brackets-close.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[a]b]
zyx = 42
2 changes: 2 additions & 0 deletions test/examples/invalid/table-nested-brackets-open.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[a[b]
zyx = 42
1 change: 1 addition & 0 deletions test/examples/invalid/table-whitespace.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[invalid key]
2 changes: 2 additions & 0 deletions test/examples/invalid/table-with-pound.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[key#group]
answer = 42
4 changes: 4 additions & 0 deletions test/examples/invalid/text-after-array-entries.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
array = [
"Is there life after an array separator?", No
"Entry"
]
1 change: 1 addition & 0 deletions test/examples/invalid/text-after-integer.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
answer = 42 the ultimate answer?
1 change: 1 addition & 0 deletions test/examples/invalid/text-after-string.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
string = "Is there life after strings?" No.
1 change: 1 addition & 0 deletions test/examples/invalid/text-after-table.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[error] this shouldn't be here
4 changes: 4 additions & 0 deletions test/examples/invalid/text-before-array-separator.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
array = [
"Is there life before an array separator?" No,
"Entry"
]
5 changes: 5 additions & 0 deletions test/examples/invalid/text-in-array.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
array = [
"Entry 1",
I don't belong,
"Entry 2",
]
3 changes: 3 additions & 0 deletions test/examples/valid/array-empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"thevoid": [[[[[]]]]]
}
1 change: 1 addition & 0 deletions test/examples/valid/array-empty.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
thevoid = [[[[[]]]]]
3 changes: 3 additions & 0 deletions test/examples/valid/array-nospaces.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"ints": [1,2,3]
}
1 change: 1 addition & 0 deletions test/examples/valid/array-nospaces.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ints = [1,2,3]
3 changes: 3 additions & 0 deletions test/examples/valid/arrays-hetergeneous.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mixed": [[1, 2], ["a", "b"], [1.1, 2.1]]
}
1 change: 1 addition & 0 deletions test/examples/valid/arrays-hetergeneous.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mixed = [[1, 2], ["a", "b"], [1.1, 2.1]]
3 changes: 3 additions & 0 deletions test/examples/valid/arrays-nested.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"nest": [["a"], ["b"]]
}
1 change: 1 addition & 0 deletions test/examples/valid/arrays-nested.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nest = [["a"], ["b"]]
5 changes: 5 additions & 0 deletions test/examples/valid/arrays.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ints": [1, 2, 3],
"floats": [1.1, 2.1, 3.1],
"strings": ["a", "b", "c"]
}
3 changes: 3 additions & 0 deletions test/examples/valid/arrays.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ints = [1, 2, 3]
floats = [1.1, 2.1, 3.1]
strings = ["a", "b", "c"]
4 changes: 4 additions & 0 deletions test/examples/valid/bool.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"f": false,
"t": true
}
2 changes: 2 additions & 0 deletions test/examples/valid/bool.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
t = true
f = false
Loading