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 CHANGED
@@ -19,7 +19,7 @@ As normal:
19
19
 
20
20
  Or in your Gemfile:
21
21
 
22
- gem 'vdf4r', '~>0.1.0'
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/README.md)
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 / comment / key_value / key / blank) endline?
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 blank
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
- :blank
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 token
40
- '"' [^"]* '"'
59
+ rule key_enter_value
60
+ whitespace* token whitespace* token_delimiter token_content+ &endline
41
61
  {
42
62
  def value
43
- elements[1..-2].collect { |e| e.text_value }.join
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 enter_object
49
- whitespace* '{' whitespace* comment? {
71
+ rule key_continue_value
72
+ token_content+ &endline
73
+ {
50
74
  def value
51
- :enter_object
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 exit_object
57
- whitespace* '}' whitespace* comment? {
82
+ rule key_exit_value
83
+ token_content* token_delimiter whitespace* &endline
84
+ {
58
85
  def value
59
- :exit_object
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 comment
65
- whitespace* [/]? [/] [^\n]* {
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
- :comment
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(/\\"/, '&quot;')
14
+ end
15
+
16
+ def dirty(input)
17
+ input.gsub('&quot;', '\"')
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 = VDF4R::KeyValuesParser.new
24
- store = Store.new
25
- key = nil
26
- path = []
27
-
28
- @input.each do |line|
29
- node = parser.parse(line)
30
- raise "ungrammatical content: '#{line}'" if node.nil?
31
-
32
- next if [:blank, :comment].include?(node.value)
33
-
34
- if node.value.respond_to?(:to_ary)
35
- case node.value.length
36
- when 1
37
- key = node.value.first
38
- when 2
39
- k, v = node.value
40
- store.traverse(path)[k] = v
41
- end
42
- elsif node.value.kind_of?(Symbol)
43
- case node.value
44
- when :enter_object
45
- raise 'no preceding key for object' unless key
46
- raise 'too recursive' if path.length > MAX_RECURSION
47
- path.push key
48
- key = nil
49
- when :exit_object
50
- raise 'nesting unbalanced (excessive exit)' if path.empty?
51
- path.pop
52
- end
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
@@ -1,3 +1,3 @@
1
1
  module VDF4R
2
- VERSION = '0.1.1'
2
+ VERSION = '0.1.2'
3
3
  end
@@ -1,4 +1,4 @@
1
1
  "DOTAAbilities"
2
2
  {
3
- The parser won't understand this line.
3
+ {The parser won't understand this line since it uses reserved characters.}
4
4
  }
@@ -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&quot;&quot;bar&quot;"' }
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 fixture translation output' do
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 'bad input' do
79
- context 'unbalanced nesting (insufficient exit)' do
80
- it 'raises TranslationError' do
81
- expect {
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
- context 'unbalanced nesting (excessive exit)' do
90
- it 'raises TranslationError' do
91
- expect {
92
- with_fixture('bad/excessive_exit') do |fixture|
93
- subject.new(fixture).parse
94
- end
95
- }.to raise_error /excessive exit/
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
- context 'ungrammatical content' do
100
- it 'raises TranslationError' do
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
- context 'too recursive' do
110
- it 'raises TranslationError' do
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
- context 'no preceding key' do
120
- it 'raises TranslationError' do
121
- expect {
122
- with_fixture('bad/no_preceding_key') do |fixture|
123
- subject.new(fixture).parse
124
- end
125
- }.to raise_error /no preceding key/
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.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-24 00:00:00.000000000 Z
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