vdf4r 0.1.1 → 0.1.2
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/README.md +2 -2
- data/lib/vdf.tt +86 -20
- data/lib/vdf4r/parser.rb +58 -30
- data/lib/vdf4r/version.rb +1 -1
- data/spec/fixtures/bad/ungrammatical_content.txt +1 -1
- data/spec/vdf4r/parser_spec.rb +58 -39
- metadata +8 -2
data/README.md
CHANGED
@@ -19,7 +19,7 @@ As normal:
|
|
19
19
|
|
20
20
|
Or in your Gemfile:
|
21
21
|
|
22
|
-
gem 'vdf4r', '~>0.1.
|
22
|
+
gem 'vdf4r', '~>0.1.1'
|
23
23
|
|
24
24
|
|
25
25
|
## Usage
|
@@ -62,5 +62,5 @@ change is on a topic branch accompanied by new tests; all behaviors must pass.
|
|
62
62
|
|
63
63
|
## License
|
64
64
|
|
65
|
-
VDF4R is offered under the MIT license. See [LICENSE](https://github.com/skadistats/vdf4r/blob/master/
|
65
|
+
VDF4R is offered under the MIT license. See [LICENSE](https://github.com/skadistats/vdf4r/blob/master/LICENSE)
|
66
66
|
for the license itself.
|
data/lib/vdf.tt
CHANGED
@@ -1,19 +1,39 @@
|
|
1
1
|
module VDF4R
|
2
2
|
grammar KeyValues
|
3
3
|
rule line
|
4
|
-
(enter_object / exit_object /
|
4
|
+
(enter_object / exit_object /
|
5
|
+
comment /
|
6
|
+
key_enter_value / key_continue_value / key_exit_value / key_value / key /
|
7
|
+
blank) endline?
|
5
8
|
{
|
6
9
|
def value
|
7
|
-
elements[0].value
|
10
|
+
[:line, elements[0].value]
|
8
11
|
end
|
9
12
|
}
|
10
13
|
end
|
11
14
|
|
12
|
-
rule
|
13
|
-
whitespace*
|
15
|
+
rule enter_object
|
16
|
+
whitespace* '{' whitespace* comment?
|
17
|
+
{
|
18
|
+
def value
|
19
|
+
[:enter_object, nil]
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
rule exit_object
|
25
|
+
whitespace* '}' whitespace* comment?
|
14
26
|
{
|
15
27
|
def value
|
16
|
-
:
|
28
|
+
[:exit_object, nil]
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
rule comment
|
34
|
+
whitespace* '//' [^\n]* {
|
35
|
+
def value
|
36
|
+
[:comment, nil]
|
17
37
|
end
|
18
38
|
}
|
19
39
|
end
|
@@ -22,7 +42,7 @@ module VDF4R
|
|
22
42
|
whitespace* token whitespace* token whitespace* comment?
|
23
43
|
{
|
24
44
|
def value
|
25
|
-
[elements[1].value, elements[3].value]
|
45
|
+
[:key_value, [elements[1].value, elements[3].value]]
|
26
46
|
end
|
27
47
|
}
|
28
48
|
end
|
@@ -31,50 +51,96 @@ module VDF4R
|
|
31
51
|
whitespace* token whitespace* comment?
|
32
52
|
{
|
33
53
|
def value
|
34
|
-
[elements[1].value]
|
54
|
+
[:key, [elements[1].value]]
|
35
55
|
end
|
36
56
|
}
|
37
57
|
end
|
38
58
|
|
39
|
-
rule
|
40
|
-
|
59
|
+
rule key_enter_value
|
60
|
+
whitespace* token whitespace* token_delimiter token_content+ &endline
|
41
61
|
{
|
42
62
|
def value
|
43
|
-
elements[1
|
63
|
+
key = elements[1].value
|
64
|
+
partial_value = elements[4...-1].collect { |e| e.text_value }.join
|
65
|
+
|
66
|
+
[:key_enter_value, [elements[1].value, partial_value]]
|
44
67
|
end
|
45
68
|
}
|
46
69
|
end
|
47
70
|
|
48
|
-
rule
|
49
|
-
|
71
|
+
rule key_continue_value
|
72
|
+
token_content+ &endline
|
73
|
+
{
|
50
74
|
def value
|
51
|
-
|
75
|
+
partial_value = elements[1...-1].collect { |e| e.text_value }.join
|
76
|
+
|
77
|
+
[:key_enter_value, [partial_value]]
|
52
78
|
end
|
53
79
|
}
|
54
80
|
end
|
55
81
|
|
56
|
-
rule
|
57
|
-
|
82
|
+
rule key_exit_value
|
83
|
+
token_content* token_delimiter whitespace* &endline
|
84
|
+
{
|
58
85
|
def value
|
59
|
-
|
86
|
+
partial_value = elements[1...-3].collect { |e| e.text_value }.join
|
87
|
+
|
88
|
+
[:key_enter_value, [partial_value]]
|
60
89
|
end
|
61
90
|
}
|
62
91
|
end
|
63
92
|
|
64
|
-
rule
|
65
|
-
|
93
|
+
rule continue_value
|
94
|
+
[^"]+
|
95
|
+
end
|
96
|
+
|
97
|
+
rule exit_value
|
98
|
+
[^"]+ delimiter_token
|
99
|
+
end
|
100
|
+
|
101
|
+
rule blank
|
102
|
+
whitespace*
|
103
|
+
{
|
104
|
+
def value
|
105
|
+
[:blank, nil]
|
106
|
+
end
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
rule token
|
111
|
+
token_delimiter token_content* token_delimiter
|
112
|
+
{
|
66
113
|
def value
|
67
|
-
|
114
|
+
elements[1..-2].collect { |e| e.text_value }.join
|
115
|
+
end
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
rule token_delimiter
|
120
|
+
'"'
|
121
|
+
end
|
122
|
+
|
123
|
+
rule token_content
|
124
|
+
[^"\r\n{}]
|
125
|
+
{
|
126
|
+
def value
|
127
|
+
elements[0].text_value
|
68
128
|
end
|
69
129
|
}
|
70
130
|
end
|
71
131
|
|
72
132
|
rule whitespace
|
73
133
|
[\t ]
|
134
|
+
{
|
135
|
+
[:whitespace, nil]
|
136
|
+
}
|
74
137
|
end
|
75
138
|
|
76
139
|
rule endline
|
77
140
|
[\r]? [\n]
|
141
|
+
{
|
142
|
+
[:endline, nil]
|
143
|
+
}
|
78
144
|
end
|
79
145
|
end
|
80
|
-
end
|
146
|
+
end
|
data/lib/vdf4r/parser.rb
CHANGED
@@ -8,6 +8,16 @@ Treetop.load GRAMMAR_PATH
|
|
8
8
|
|
9
9
|
module VDF4R
|
10
10
|
class Parser
|
11
|
+
class << self
|
12
|
+
def clean(input)
|
13
|
+
input.gsub(/\\"/, '"')
|
14
|
+
end
|
15
|
+
|
16
|
+
def dirty(input)
|
17
|
+
input.gsub('"', '\"')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
11
21
|
def initialize(input)
|
12
22
|
case
|
13
23
|
when input.respond_to?(:each_line)
|
@@ -20,36 +30,54 @@ module VDF4R
|
|
20
30
|
end
|
21
31
|
|
22
32
|
def parse
|
23
|
-
parser
|
24
|
-
store
|
25
|
-
key
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
33
|
+
parser = VDF4R::KeyValuesParser.new
|
34
|
+
store = Store.new
|
35
|
+
key = nil
|
36
|
+
partial_value = nil
|
37
|
+
path = []
|
38
|
+
|
39
|
+
@input.each_with_index do |line, index|
|
40
|
+
node = parser.parse(Parser.clean(line))
|
41
|
+
|
42
|
+
if node.nil?
|
43
|
+
raise "ungrammatical content at line #{index+1}: '#{line}'" if node.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
begin
|
47
|
+
_, (encounter, context) = node.value
|
48
|
+
rescue NoMethodError => e
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
case encounter
|
53
|
+
when :blank, :comment
|
54
|
+
# do nothing
|
55
|
+
when :enter_object
|
56
|
+
raise 'no preceding key for object' unless key
|
57
|
+
raise 'too recursive' if path.length > MAX_RECURSION
|
58
|
+
raise 'enter during multi-line value' if partial_value
|
59
|
+
path.push key
|
60
|
+
key = nil
|
61
|
+
when :exit_object
|
62
|
+
raise 'nesting unbalanced (excessive exit)' if path.empty?
|
63
|
+
raise 'exit during multi-line value' if partial_value
|
64
|
+
path.pop
|
65
|
+
when :key_value
|
66
|
+
k, v = context
|
67
|
+
store.traverse(path)[k] = Parser.dirty(v)
|
68
|
+
when :key
|
69
|
+
key = context[0]
|
70
|
+
when :key_enter_value
|
71
|
+
key, partial_value = context
|
72
|
+
when :key_continue_value
|
73
|
+
# raise unless value is non-nil
|
74
|
+
partial_value += "\n#{context[0]}"
|
75
|
+
when :key_exit_value
|
76
|
+
v = partial_value + "\n#{context[0]}"
|
77
|
+
partial_value = nil
|
78
|
+
store.traverse(path)[k] = Parser.dirty(v)
|
79
|
+
else
|
80
|
+
raise 'unknown node value'
|
53
81
|
end
|
54
82
|
end
|
55
83
|
|
data/lib/vdf4r/version.rb
CHANGED
data/spec/vdf4r/parser_spec.rb
CHANGED
@@ -7,6 +7,25 @@ module VDF4R
|
|
7
7
|
describe Parser do
|
8
8
|
subject { VDF4R::Parser }
|
9
9
|
|
10
|
+
describe 'class methods' do
|
11
|
+
context 'input safety' do
|
12
|
+
let(:dirty) { '"foo\"\"bar\""' }
|
13
|
+
let(:clean) { '"foo""bar""' }
|
14
|
+
|
15
|
+
describe '#clean' do
|
16
|
+
it 'replaces escaped quotes with token' do
|
17
|
+
expect(subject.clean(dirty)).to eq(clean)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#dirty' do
|
22
|
+
it 'replaces tokens with escaped quote' do
|
23
|
+
expect(subject.dirty(clean)).to eq(dirty)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
10
29
|
describe 'instantiation' do
|
11
30
|
shared_examples_for "doesn't raise" do
|
12
31
|
it 'does not raise' do
|
@@ -50,7 +69,7 @@ module VDF4R
|
|
50
69
|
end
|
51
70
|
end
|
52
71
|
|
53
|
-
describe 'items.txt
|
72
|
+
describe 'usage example (items.txt)' do
|
54
73
|
let(:result) do
|
55
74
|
with_fixture('items') do |fixture|
|
56
75
|
subject.new(fixture).parse
|
@@ -75,54 +94,54 @@ module VDF4R
|
|
75
94
|
end
|
76
95
|
end
|
77
96
|
|
78
|
-
describe '
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
with_fixture('bad/insufficient_exit') do |fixture|
|
83
|
-
subject.new(fixture).parse
|
84
|
-
end
|
85
|
-
}.to raise_error /insufficient exit/
|
97
|
+
describe 'usage example (dota_english.txt)' do
|
98
|
+
let(:result) do
|
99
|
+
with_fixture('dota_english') do |fixture|
|
100
|
+
subject.new(fixture).parse
|
86
101
|
end
|
87
102
|
end
|
88
103
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
it "doesn't raise" do
|
105
|
+
expect {
|
106
|
+
result
|
107
|
+
}.not_to raise_error
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe 'bad input' do
|
112
|
+
def parse_fixture(fixture_name)
|
113
|
+
with_fixture(fixture_name) do |fixture|
|
114
|
+
subject.new(fixture).parse
|
96
115
|
end
|
97
116
|
end
|
117
|
+
|
118
|
+
it 'raises on unbalanced nesting (insufficient exit)' do
|
119
|
+
expect { parse_fixture('bad/insufficient_exit') }.to raise_error /insufficient exit/
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'raises on unbalanced nesting (excessive exit)' do
|
123
|
+
expect { parse_fixture('bad/excessive_exit') }.to raise_error /excessive exit/
|
124
|
+
end
|
98
125
|
|
99
|
-
|
100
|
-
|
101
|
-
expect {
|
102
|
-
with_fixture('bad/ungrammatical_content') do |fixture|
|
103
|
-
subject.new(fixture).parse
|
104
|
-
end
|
105
|
-
}.to raise_error /ungrammatical content/
|
106
|
-
end
|
126
|
+
it 'raises on ungrammatical content' do
|
127
|
+
expect { parse_fixture('bad/ungrammatical_content') }.to raise_error /ungrammatical content/
|
107
128
|
end
|
108
129
|
|
109
|
-
|
110
|
-
|
111
|
-
expect {
|
112
|
-
with_fixture('bad/too_recursive') do |fixture|
|
113
|
-
subject.new(fixture).parse
|
114
|
-
end
|
115
|
-
}.to raise_error /too recursive/
|
116
|
-
end
|
130
|
+
it 'raises when too recursive' do
|
131
|
+
expect { parse_fixture('bad/too_recursive') }.to raise_error /too recursive/
|
117
132
|
end
|
118
133
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
}.to raise_error /
|
134
|
+
it 'raises on no preceding key' do
|
135
|
+
expect { parse_fixture('bad/no_preceding_key') }.to raise_error /no preceding key/
|
136
|
+
end
|
137
|
+
|
138
|
+
describe 'multi-line values' do
|
139
|
+
it 'raises on enter' do
|
140
|
+
expect { parse_fixture('bad/multi_line_enter') }.to raise_error /enter/
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'raises on exit' do
|
144
|
+
expect { parse_fixture('bad/multi_line_exit') }.to raise_error /exit/
|
126
145
|
end
|
127
146
|
end
|
128
147
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vdf4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-04-
|
12
|
+
date: 2014-04-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: treetop
|
@@ -78,12 +78,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
78
|
- - ! '>='
|
79
79
|
- !ruby/object:Gem::Version
|
80
80
|
version: '0'
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
hash: 1856681965695248353
|
81
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
85
|
none: false
|
83
86
|
requirements:
|
84
87
|
- - ! '>='
|
85
88
|
- !ruby/object:Gem::Version
|
86
89
|
version: '0'
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
hash: 1856681965695248353
|
87
93
|
requirements: []
|
88
94
|
rubyforge_project:
|
89
95
|
rubygems_version: 1.8.23.2
|