vdf4r 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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