jsgf 0.1 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 66ac3ad9f73f24c8c44884964606916ad2495680
4
- data.tar.gz: 1a67637a02d934eee7ee0c53f8586217f68a6567
3
+ metadata.gz: fdf13c6642d0aa9a9faaa005d72563af8151ac68
4
+ data.tar.gz: 3014db9c26234c9744262e9c50a0198c9c81b466
5
5
  SHA512:
6
- metadata.gz: 6b42838dcefd7eb536f0dd61a9c6ca352fba12b60577960ef316c35dda5d9ce0e681a34ba8b2dfcd70d11b6981f6c22a70700a6cdf806dfd538671df35789a73
7
- data.tar.gz: c04356733209a868672753df49626aa595f9b56c91e2a3dfd95493c75599df244ac3b4d93c09f94a54f2e582c51895f02396ec120605e9234830941868534953
6
+ metadata.gz: 140f2abe3f92727e630bf4aab4e4f33ba92afa09fc57e38f2e50d6669769a60fdc23ef0aa6c2565fa68b925874bffbec645507f6fd4afdc10500c3272293e907
7
+ data.tar.gz: aae7b624c87f88c923a2d6db6bb8e63ea6963860d50178cfb0b5128a74b4aee223abef8754343db1e725af6bff5ba93a6c124bbae0b08a0cad070d0069154825
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.1
data/README.md CHANGED
@@ -1,8 +1,22 @@
1
- # Jsgf
1
+ Jsgf
2
+ ====
2
3
 
4
+ [![Build Status](https://travis-ci.org/bfoz/jsgf-ruby.png)](https://travis-ci.org/bfoz/jsgf-ruby)
3
5
  [![Gem Version](https://badge.fury.io/rb/jsgf.svg)](http://badge.fury.io/rb/jsgf)
4
6
 
5
- ## Installation
7
+ For all of your [Java Speech Grammar Format](http://www.w3.org/TR/jsgf/) parsing needs.
8
+
9
+ Usage
10
+ -----
11
+
12
+ ```ruby
13
+ require 'jsgf'
14
+
15
+ grammar = JSGF.read('example.gram')
16
+ ```
17
+
18
+ Installation
19
+ ------------
6
20
 
7
21
  Add this line to your application's Gemfile:
8
22
 
@@ -18,14 +32,7 @@ Or install it yourself as:
18
32
 
19
33
  $ gem install jsgf
20
34
 
21
- ## Usage
22
-
23
- TODO: Write usage instructions here
24
-
25
- ## Contributing
35
+ License
36
+ -------
26
37
 
27
- 1. Fork it ( https://github.com/[my-github-username]/jsgf/fork )
28
- 2. Create your feature branch (`git checkout -b my-new-feature`)
29
- 3. Commit your changes (`git commit -am 'Add some feature'`)
30
- 4. Push to the branch (`git push origin my-new-feature`)
31
- 5. Create a new Pull Request
38
+ Copyright 2015 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
data/jsgf.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "jsgf"
7
- spec.version = '0.1'
7
+ spec.version = '0.2'
8
8
  spec.authors = ["Brandon Fosdick"]
9
9
  spec.email = ["bfoz@bfoz.net"]
10
10
  spec.summary = %q{Java Speech Grammar Format}
@@ -1,7 +1,7 @@
1
1
  module JSGF
2
2
  class Alternation
3
3
  attr_reader :elements
4
- attr_writer :optional
4
+ attr_accessor :optional
5
5
  attr_reader :tags
6
6
 
7
7
  def initialize(*args)
data/lib/jsgf/grammar.rb CHANGED
@@ -2,13 +2,15 @@ module JSGF
2
2
  class Grammar
3
3
  attr_reader :character_encoding
4
4
  attr_reader :locale
5
+ attr_reader :grammar_name
5
6
  attr_reader :private_rules
6
7
  attr_reader :public_rules
7
8
  attr_reader :version
8
9
 
9
- def initialize(character_encoding:nil, locale:nil, private_rules:{}, public_rules:{}, version:nil)
10
+ def initialize(name:nil, character_encoding:nil, locale:nil, private_rules:{}, public_rules:{}, version:nil)
10
11
  @character_encoding = character_encoding
11
12
  @locale = locale
13
+ @grammar_name = name
12
14
  @private_rules = private_rules
13
15
  @public_rules = public_rules
14
16
  @version = version
@@ -19,5 +21,48 @@ module JSGF
19
21
  def rules
20
22
  @public_rules.merge(@private_rules)
21
23
  end
24
+
25
+ def to_s
26
+ private_rule_array = @private_rules.map do |(name, rule)|
27
+ atoms = rule.map {|a| unparse_atom(a) }.join(' ')
28
+ "<#{name}> = #{atoms};"
29
+ end
30
+ public_rule_array = @public_rules.map do |(name, rule)|
31
+ atoms = rule.map {|a| unparse_atom(a) }.join(' ')
32
+ "public <#{name}> = #{atoms};"
33
+ end
34
+
35
+ [header, grammar_header, *public_rule_array, *private_rule_array].join('\n')
36
+ end
37
+
38
+ private
39
+ # Generate the grammar name header line
40
+ def grammar_header
41
+ "grammar #{grammar_name};"
42
+ end
43
+
44
+ # Generate the JSGF header line
45
+ def header
46
+ ['#JSGF', version, character_encoding, locale].compact.join(' ') + ';'
47
+ end
48
+
49
+ def unparse_atom(atom, nested:false)
50
+ case atom
51
+ when Array
52
+ s = atom.map {|a| unparse_atom(a, nested:nested)}.join(' ')
53
+ nested ? ('(' + s + ')') : s
54
+ when Alternation
55
+ s = atom.elements.map {|a| unparse_atom(a, nested:true)}.join(' | ')
56
+ atom.optional ? ('[' + s + ']') : s
57
+ when Optional then '[' + atom.elements.map {|a| unparse_atom(a, nested:nested)}.join(' | ') + ']'
58
+ else
59
+ weight = (atom[:weight] != 1.0) ? "/#{atom[:weight]}/" : nil
60
+ if atom[:name]
61
+ [weight, '<' + atom[:name] + '>'].compact.join(' ')
62
+ else
63
+ [weight, atom[:atom], *atom[:tags]].compact.join(' ')
64
+ end
65
+ end
66
+ end
22
67
  end
23
68
  end
data/lib/jsgf/optional.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  module JSGF
2
2
  class Optional
3
+ attr_reader :elements
4
+
3
5
  def initialize(*args)
4
6
  @elements = args
5
7
  end
data/lib/jsgf/parser.rb CHANGED
@@ -11,6 +11,7 @@ require_relative 'alternation'
11
11
  require_relative 'grammar'
12
12
  require_relative 'optional'
13
13
  require_relative 'repetition'
14
+ require_relative 'tokenizer'
14
15
 
15
16
  module JSGF
16
17
  class Parser < Racc::Parser
@@ -22,6 +23,7 @@ attr_reader :handler
22
23
  def initialize(tokenizer)
23
24
  @private_rules = {}
24
25
  @public_rules = {}
26
+ @rules = {}
25
27
  tokenizer = JSGF::Tokenizer.new(tokenizer) if tokenizer.is_a?(String)
26
28
  @tokenizer = tokenizer
27
29
  super()
@@ -50,22 +52,32 @@ def define_atom(atom, weight=1.0, tags=[])
50
52
  end
51
53
 
52
54
  def define_rule(name, visibility=:private, *args)
53
- r = {name:name, visibility:visibility, atoms:args}
54
- if visibility == :private
55
- @private_rules[name] = r
56
- else
57
- @public_rules[name] = r
58
- end
55
+ r = {name: name, visibility:visibility, atoms:args.flatten}
56
+ @rules[name] = r
59
57
  r
60
58
  end
61
59
 
60
+ def rule_reference(name)
61
+ {name:name, weight:1.0, tags:[]}
62
+ end
63
+
62
64
  def next_token
63
65
  @tokenizer.next_token
64
- end
66
+ end
65
67
 
66
68
  def parse
67
69
  do_parse
68
- JSGF::Grammar.new( character_encoding:@charset,
70
+
71
+ @rules.each do |(k,v)|
72
+ if v[:visibility] == :private
73
+ @private_rules[k] = v[:atoms]
74
+ else
75
+ @public_rules[k] = v[:atoms]
76
+ end
77
+ end
78
+
79
+ JSGF::Grammar.new( name:@grammar_name,
80
+ character_encoding:@charset,
69
81
  locale:@locale,
70
82
  private_rules:@private_rules,
71
83
  public_rules:@public_rules,
@@ -74,62 +86,62 @@ end
74
86
  ##### State transition tables begin ###
75
87
 
76
88
  racc_action_table = [
77
- 40, 37, 17, 11, 16, 40, 37, 38, 11, 39,
78
- 40, 37, 38, 11, 39, 40, 44, 38, 11, 39,
79
- 40, 37, 38, 11, 39, 40, 37, 38, 11, 39,
80
- 40, 37, 38, 11, 39, 40, 37, 38, 11, 39,
81
- 9, 13, 38, 45, 39, 13, 11, 9, 13, 13,
82
- 11, 49, 48, 11, 11, 61, 49, 62, 49, 51,
83
- 60, 49, 28, 47, 27, 46, 52, 53, 52, 53,
84
- 30, 29, 26, 11, 24, 58, 23, 51, 11, 18,
85
- 15, 5, 4 ]
89
+ -21, 9, 13, 28, -21, 27, -21, 11, -21, 52,
90
+ 53, 40, 37, 17, 11, 16, 40, 37, 38, 11,
91
+ 39, 40, 13, 38, 11, 39, 44, 11, 38, 40,
92
+ 39, 49, 11, 30, 40, 37, 38, 11, 39, 40,
93
+ 29, 38, 11, 39, 40, 37, 38, 11, 39, 9,
94
+ 13, 38, 13, 39, 13, 11, 47, 11, 54, 11,
95
+ 48, 46, 26, 45, 52, 53, 52, 53, 52, 53,
96
+ 24, 23, 60, 11, 18, 54, 15, 48, 5, 63,
97
+ 64, 4 ]
86
98
 
87
99
  racc_action_check = [
88
- 59, 59, 4, 59, 4, 32, 32, 59, 32, 59,
89
- 24, 24, 32, 24, 32, 37, 25, 24, 37, 24,
90
- 38, 38, 37, 38, 37, 39, 39, 38, 39, 38,
91
- 49, 49, 39, 49, 39, 44, 44, 49, 44, 49,
92
- 2, 2, 44, 26, 44, 6, 2, 7, 7, 20,
93
- 6, 56, 31, 7, 20, 56, 31, 57, 55, 33,
94
- 55, 57, 17, 28, 17, 28, 35, 35, 54, 54,
95
- 23, 22, 15, 13, 12, 47, 11, 50, 9, 5,
96
- 3, 1, 0 ]
100
+ 34, 7, 7, 17, 34, 17, 34, 7, 34, 34,
101
+ 34, 48, 48, 4, 48, 4, 38, 38, 48, 38,
102
+ 48, 37, 20, 38, 37, 38, 26, 20, 37, 32,
103
+ 37, 32, 32, 23, 24, 24, 32, 24, 32, 57,
104
+ 22, 24, 57, 24, 39, 39, 57, 39, 57, 2,
105
+ 2, 39, 13, 39, 6, 2, 31, 13, 36, 6,
106
+ 31, 28, 15, 28, 50, 50, 55, 55, 62, 62,
107
+ 12, 11, 46, 9, 5, 51, 3, 56, 1, 58,
108
+ 59, 0 ]
97
109
 
98
110
  racc_action_pointer = [
99
- 79, 81, 36, 78, -5, 79, 40, 43, nil, 68,
100
- nil, 69, 62, 63, nil, 65, nil, 55, nil, nil,
101
- 44, nil, 62, 59, 3, 4, 34, nil, 56, nil,
102
- nil, 43, -2, 53, nil, 48, nil, 8, 13, 18,
103
- nil, nil, nil, nil, 28, nil, nil, 66, nil, 23,
104
- 71, nil, nil, nil, 50, 45, 38, 48, nil, -7,
105
- nil, nil, nil ]
111
+ 78, 78, 45, 74, 6, 74, 49, -3, nil, 63,
112
+ nil, 64, 58, 47, nil, 55, nil, -4, nil, nil,
113
+ 17, nil, 31, 22, 27, nil, 17, nil, 54, nil,
114
+ nil, 47, 22, nil, -9, nil, 52, 14, 9, 37,
115
+ nil, nil, nil, nil, nil, nil, 63, nil, 4, nil,
116
+ 46, 69, nil, nil, nil, 48, 64, 32, 64, 63,
117
+ nil, nil, 50, nil, nil ]
106
118
 
107
119
  racc_action_default = [
108
- -35, -35, -1, -35, -35, -35, -2, -35, -10, -35,
109
- -13, -35, -35, -35, -4, -35, -5, -35, 63, -14,
110
- -3, -11, -35, -35, -35, -35, -35, -6, -35, -12,
111
- -15, -35, -18, -20, -22, -24, -25, -35, -35, -35,
112
- -29, -30, -31, -32, -35, -9, -7, -35, -16, -35,
113
- -21, -23, -33, -34, -26, -35, -35, -35, -8, -19,
114
- -27, -28, -17 ]
120
+ -38, -38, -1, -38, -38, -38, -2, -38, -10, -38,
121
+ -13, -38, -38, -38, -4, -38, -5, -38, 65, -14,
122
+ -3, -11, -38, -38, -38, -18, -38, -6, -38, -12,
123
+ -15, -38, -38, -19, -23, -22, -25, -38, -38, -38,
124
+ -32, -33, -34, -35, -9, -7, -38, -16, -38, -17,
125
+ -23, -26, -36, -37, -24, -27, -28, -29, -38, -38,
126
+ -8, -20, -21, -30, -31 ]
115
127
 
116
128
  racc_goto_table = [
117
- 50, 31, 12, 19, 1, 6, 12, 12, 14, 22,
118
- 20, 3, 7, 25, 8, 55, 56, 19, 59, 21,
119
- 12, 57, 2, 54, nil, nil, nil, 50 ]
129
+ 50, 51, 12, 19, 14, 55, 12, 12, 3, 22,
130
+ 25, 58, 59, 12, 6, 31, 62, 19, 8, 20,
131
+ 12, 32, 61, 21, 7, 50, 51, 2, 1 ]
120
132
 
121
133
  racc_goto_check = [
122
- 12, 10, 8, 9, 1, 3, 8, 8, 6, 8,
123
- 3, 5, 4, 8, 7, 10, 10, 9, 11, 7,
124
- 8, 10, 2, 14, nil, nil, nil, 12 ]
134
+ 13, 15, 8, 9, 6, 13, 8, 8, 5, 8,
135
+ 9, 16, 16, 8, 3, 10, 13, 9, 7, 3,
136
+ 8, 11, 12, 7, 4, 13, 15, 2, 1 ]
125
137
 
126
138
  racc_goto_pointer = [
127
- nil, 4, 22, 3, 10, 11, 5, 12, 0, -3,
128
- -23, -31, -32, nil, -14, nil, nil, nil ]
139
+ nil, 28, 27, 12, 22, 8, 1, 16, 0, -3,
140
+ -9, -3, -26, -32, nil, -31, -27, nil, nil ]
129
141
 
130
142
  racc_goto_default = [
131
143
  nil, nil, nil, nil, nil, nil, nil, nil, 41, 10,
132
- nil, 32, 33, 34, 35, 36, 42, 43 ]
144
+ 56, 57, 33, 34, 35, 36, nil, 42, 43 ]
133
145
 
134
146
  racc_reduce_table = [
135
147
  0, 0, :racc_error,
@@ -149,28 +161,31 @@ racc_reduce_table = [
149
161
  2, 23, :_reduce_none,
150
162
  3, 28, :_reduce_15,
151
163
  4, 29, :_reduce_16,
152
- 5, 29, :_reduce_17,
153
- 1, 30, :_reduce_18,
154
- 3, 30, :_reduce_19,
155
- 1, 31, :_reduce_20,
156
- 2, 31, :_reduce_21,
164
+ 4, 29, :_reduce_17,
165
+ 2, 29, :_reduce_18,
166
+ 1, 30, :_reduce_19,
167
+ 3, 30, :_reduce_20,
157
168
  1, 32, :_reduce_none,
158
- 2, 32, :_reduce_23,
169
+ 1, 32, :_reduce_none,
170
+ 1, 35, :_reduce_none,
171
+ 2, 35, :_reduce_24,
172
+ 1, 31, :_reduce_none,
173
+ 2, 31, :_reduce_26,
174
+ 2, 34, :_reduce_27,
175
+ 1, 36, :_reduce_none,
176
+ 1, 36, :_reduce_none,
177
+ 3, 37, :_reduce_30,
178
+ 3, 38, :_reduce_31,
179
+ 1, 33, :_reduce_32,
180
+ 1, 33, :_reduce_33,
159
181
  1, 33, :_reduce_none,
160
182
  1, 33, :_reduce_none,
161
- 2, 35, :_reduce_26,
162
- 3, 36, :_reduce_27,
163
- 3, 37, :_reduce_28,
164
- 1, 34, :_reduce_29,
165
- 1, 34, :_reduce_none,
166
- 1, 34, :_reduce_none,
167
- 1, 34, :_reduce_none,
168
- 2, 34, :_reduce_33,
169
- 2, 34, :_reduce_34 ]
183
+ 2, 33, :_reduce_36,
184
+ 2, 33, :_reduce_37 ]
170
185
 
171
- racc_reduce_n = 35
186
+ racc_reduce_n = 38
172
187
 
173
- racc_shift_n = 63
188
+ racc_shift_n = 65
174
189
 
175
190
  racc_token_table = {
176
191
  false => 0,
@@ -245,12 +260,13 @@ Racc_token_to_s_table = [
245
260
  "import_statement",
246
261
  "rule_name",
247
262
  "rule",
248
- "alternate_list",
249
- "rule_expansion",
250
- "tagged_rule_item",
251
- "rule_item",
263
+ "alternation",
264
+ "atom_list",
265
+ "alternation_item",
252
266
  "rule_atom",
253
267
  "weighted_item",
268
+ "tagged_atom",
269
+ "group_list",
254
270
  "rule_group",
255
271
  "rule_optional" ]
256
272
 
@@ -306,78 +322,87 @@ def _reduce_15(val, _values, result)
306
322
  end
307
323
 
308
324
  def _reduce_16(val, _values, result)
309
- define_rule(val[0], :private, *(val[2..-2]))
325
+ result = define_rule(val[0], :private, *(val[2..-2]))
310
326
  result
311
327
  end
312
328
 
313
329
  def _reduce_17(val, _values, result)
314
- define_rule(val[1], :public, val[3])
330
+ result = define_rule(val[0], :private, *(val[2..-2]))
315
331
  result
316
332
  end
317
333
 
318
334
  def _reduce_18(val, _values, result)
319
- result = val.first
335
+ val[1][:visibility] = :public
320
336
  result
321
337
  end
322
338
 
323
339
  def _reduce_19(val, _values, result)
324
- result = define_alternation(*val)
340
+ result = val.first
325
341
  result
326
342
  end
327
343
 
328
344
  def _reduce_20(val, _values, result)
329
- result = val.first
345
+ result = define_alternation(*val)
330
346
  result
331
347
  end
332
348
 
333
- def _reduce_21(val, _values, result)
334
- result = val
335
- result
336
- end
349
+ # reduce 21 omitted
337
350
 
338
351
  # reduce 22 omitted
339
352
 
340
- def _reduce_23(val, _values, result)
353
+ # reduce 23 omitted
354
+
355
+ def _reduce_24(val, _values, result)
341
356
  val[0][:tags].push(val[1]); result = val[0]
342
357
  result
343
358
  end
344
359
 
345
- # reduce 24 omitted
346
-
347
360
  # reduce 25 omitted
348
361
 
349
362
  def _reduce_26(val, _values, result)
350
- val[1][:weight] = val.first[1..-2].to_f; result = val[1]
363
+ result = val;
351
364
  result
352
365
  end
353
366
 
354
367
  def _reduce_27(val, _values, result)
368
+ val[1][:weight] = val.first[1..-2].to_f; result = val[1]
369
+ result
370
+ end
371
+
372
+ # reduce 28 omitted
373
+
374
+ # reduce 29 omitted
375
+
376
+ def _reduce_30(val, _values, result)
355
377
  result = val[1]
356
378
  result
357
379
  end
358
380
 
359
- def _reduce_28(val, _values, result)
381
+ def _reduce_31(val, _values, result)
360
382
  result = define_optional(val[1])
361
383
  result
362
384
  end
363
385
 
364
- def _reduce_29(val, _values, result)
386
+ def _reduce_32(val, _values, result)
365
387
  result = define_atom(val.first)
366
388
  result
367
389
  end
368
390
 
369
- # reduce 30 omitted
391
+ def _reduce_33(val, _values, result)
392
+ result = rule_reference(val[0])
393
+ result
394
+ end
370
395
 
371
- # reduce 31 omitted
396
+ # reduce 34 omitted
372
397
 
373
- # reduce 32 omitted
398
+ # reduce 35 omitted
374
399
 
375
- def _reduce_33(val, _values, result)
400
+ def _reduce_36(val, _values, result)
376
401
  result = JSGF::Repetition.new(val[0], 0)
377
402
  result
378
403
  end
379
404
 
380
- def _reduce_34(val, _values, result)
405
+ def _reduce_37(val, _values, result)
381
406
  result = JSGF::Repetition.new(val[0], 1)
382
407
  result
383
408
  end
@@ -24,6 +24,12 @@ module JSGF
24
24
  @scanner.skip(/\s+/) # Skip all leading whitespace
25
25
  @scanner.skip(%r{//.*\n}) # Skip single-line comments
26
26
 
27
+ # Skip C-style comments
28
+ if @scanner.scan(%r{/\*})
29
+ # Look for the end of the block, and skip any trailing whitespace
30
+ @scanner.skip_until(%r{\*/\s*})
31
+ end
32
+
27
33
  TOKENS.each do |(pattern, token)|
28
34
  text = @scanner.scan(pattern)
29
35
  return [token, text] if text
@@ -0,0 +1,66 @@
1
+ require 'minitest/autorun'
2
+ require 'jsgf/parser'
3
+
4
+ describe JSGF::Grammar do
5
+ it 'must unparse a header' do
6
+ grammar = JSGF::Parser.new('#JSGF; grammar header_grammar;').parse
7
+ grammar.to_s.must_equal '#JSGF;\ngrammar header_grammar;'
8
+ end
9
+
10
+ it 'must unparse a header with a version' do
11
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;').parse
12
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;'
13
+ end
14
+
15
+ it 'must unparse a header with a version and a character encoding' do
16
+ grammar = JSGF::Parser.new('#JSGF V1.0 ENCODING; grammar header_grammar;').parse
17
+ grammar.to_s.must_equal '#JSGF V1.0 ENCODING;\ngrammar header_grammar;'
18
+ end
19
+
20
+ it 'must unparse a simple rule' do
21
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one;').parse
22
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = one;'
23
+ end
24
+
25
+ it 'must unparse a public rule' do
26
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;public <rule>=one;').parse
27
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\npublic <rule> = one;'
28
+ end
29
+
30
+ it 'must unparse a multi-atom rule' do
31
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one two;').parse
32
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = one two;'
33
+ end
34
+
35
+ it 'must unparse a multi-atom optional' do
36
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one two];').parse
37
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = [one two];'
38
+ end
39
+
40
+ it 'must unparse an alternation' do
41
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one | two;').parse
42
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = one | two;'
43
+ end
44
+
45
+ it 'must unparse an optional alternation' do
46
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one | two];').parse
47
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = [one | two];'
48
+ end
49
+
50
+ it 'must unparse an alternation with a nested group' do
51
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one two) | three;').parse
52
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = (one two) | three;'
53
+ end
54
+
55
+ describe 'when unparsing rule references' do
56
+ it 'must unparse a rule reference' do
57
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one <rule2>) | three; <rule2>=two;').parse
58
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = (one <rule2>) | three;\n<rule2> = two;'
59
+ end
60
+
61
+ it 'must unparse a weighted rule reference' do
62
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>= /0.5/ one | /0.5/ <rule2>; <rule2>=two;').parse
63
+ grammar.to_s.must_equal '#JSGF V1.0;\ngrammar header_grammar;\n<rule> = /0.5/ one | /0.5/ <rule2>;\n<rule2> = two;'
64
+ end
65
+ end
66
+ end
data/test/jsgf/parser.rb CHANGED
@@ -26,23 +26,21 @@ describe JSGF::Parser do
26
26
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one;').parse
27
27
  grammar.rules.size.must_equal 1
28
28
  grammar.rules.keys.must_equal ['rule']
29
- grammar.rules['rule'][:atoms].size.must_equal 1
29
+ grammar.rules['rule'].size.must_equal 1
30
30
  end
31
31
 
32
32
  it 'must parse a rule with multiple atoms' do
33
33
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one two;').parse
34
34
  grammar.rules.size.must_equal 1
35
35
  grammar.rules.keys.must_equal ['rule']
36
- grammar.rules['rule'][:atoms].size.must_equal 1
37
- grammar.rules['rule'][:atoms].first.size.must_equal 2
36
+ grammar.rules['rule'].size.must_equal 2
38
37
  end
39
38
 
40
39
  it 'must parse a rule with multiple grouped atoms' do
41
40
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one two);').parse
42
41
  grammar.rules.size.must_equal 1
43
42
  grammar.rules.keys.must_equal ['rule']
44
- grammar.rules['rule'][:atoms].size.must_equal 1
45
- grammar.rules['rule'][:atoms].first.size.must_equal 2
43
+ grammar.rules['rule'].size.must_equal 2
46
44
  end
47
45
 
48
46
  describe 'when parsing an alternation' do
@@ -50,60 +48,98 @@ describe JSGF::Parser do
50
48
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one | two;').parse
51
49
  grammar.rules.size.must_equal 1
52
50
  grammar.rules.keys.must_equal ['rule']
53
- grammar.rules['rule'][:atoms].size.must_equal 1
51
+ grammar.rules['rule'].size.must_equal 1
54
52
 
55
- alternation = grammar.rules['rule'][:atoms].first
53
+ alternation = grammar.rules['rule'].first
56
54
  alternation.must_be_kind_of JSGF::Alternation
57
55
  alternation.size.must_equal 2
58
56
  end
59
-
57
+
60
58
  it 'must parse a rule with a bigger alternation' do
61
59
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one | two | three;').parse
62
60
  grammar.rules.size.must_equal 1
63
61
  grammar.rules.keys.must_equal ['rule']
64
- grammar.rules['rule'][:atoms].size.must_equal 1
62
+ grammar.rules['rule'].size.must_equal 1
65
63
 
66
- alternation = grammar.rules['rule'][:atoms].first
64
+ alternation = grammar.rules['rule'].first
67
65
  alternation.must_be_kind_of JSGF::Alternation
68
66
  alternation.size.must_equal 3
69
- alternation.elements[0][:atom].must_equal 'one'
70
- alternation.elements[1][:atom].must_equal 'two'
71
- alternation.elements[2][:atom].must_equal 'three'
67
+ alternation.elements[0].must_equal Hash[atom:'one', weight:1.0, tags:[]]
68
+ alternation.elements[1].must_equal Hash[atom:'two', weight:1.0, tags:[]]
69
+ alternation.elements[2].must_equal Hash[atom:'three', weight:1.0, tags:[]]
72
70
  end
73
71
 
74
72
  it 'must parse a weighted alternation' do
75
73
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>= /0.5/ one | /0.25/ two | /0.25/ three;').parse
76
74
  grammar.rules.keys.must_equal ['rule']
77
- grammar.rules['rule'][:atoms].size.must_equal 1
75
+ grammar.rules['rule'].size.must_equal 1
78
76
 
79
- alternation = grammar.rules['rule'][:atoms].first
77
+ alternation = grammar.rules['rule'].first
80
78
  alternation.must_be_kind_of JSGF::Alternation
81
79
  alternation.size.must_equal 3
82
80
  alternation.weights.must_equal [0.5, 0.25, 0.25]
83
- alternation.elements[0][:atom].must_equal 'one'
81
+ alternation.elements[0].must_equal Hash[atom:'one', weight:0.5, tags:[]]
84
82
  end
85
83
 
86
84
  it 'must parse a grouped alternation' do
87
85
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one | two | three);').parse
88
86
  grammar.rules.size.must_equal 1
89
87
  grammar.rules.keys.must_equal ['rule']
90
- grammar.rules['rule'][:atoms].size.must_equal 1
91
- grammar.rules['rule'][:atoms].first.must_be_kind_of JSGF::Alternation
92
- grammar.rules['rule'][:atoms].first.size.must_equal 3
88
+ grammar.rules['rule'].size.must_equal 1
89
+ grammar.rules['rule'].first.must_be_kind_of JSGF::Alternation
90
+ grammar.rules['rule'].first.size.must_equal 3
93
91
  end
94
92
  end
95
93
 
96
- it 'must parse an optional group of a single atom' do
97
- grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one];').parse
98
- grammar.rules.size.must_equal 1
99
- grammar.rules.keys.must_equal ['rule']
100
- grammar.rules['rule'][:atoms].size.must_equal 1
94
+ describe 'when parsing optionals' do
95
+ it 'must parse an optional group of a single atom' do
96
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one];').parse
97
+ grammar.rules.size.must_equal 1
98
+ grammar.rules.keys.must_equal ['rule']
99
+ grammar.rules['rule'].size.must_equal 1
100
+ end
101
+
102
+ it 'must parse an optional alternation' do
103
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one | two];').parse
104
+ grammar.rules.size.must_equal 1
105
+ grammar.rules.keys.must_equal ['rule']
106
+ grammar.rules['rule'].size.must_equal 1
107
+
108
+ rule = grammar.rules['rule']
109
+ rule.first.must_be_kind_of JSGF::Alternation
110
+ rule.first.optional.must_equal true
111
+ end
112
+
113
+ it 'must parse a multi-atom optional alternation' do
114
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one two [three | four];').parse
115
+ grammar.rules.size.must_equal 1
116
+ grammar.rules.keys.must_equal ['rule']
117
+ grammar.rules['rule'].size.must_equal 3
118
+
119
+ rule = grammar.rules['rule']
120
+ rule.size.must_equal 3
121
+ end
101
122
  end
102
123
 
103
- it 'must parse an optional alternation' do
104
- grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=[one | two];').parse
105
- grammar.rules.size.must_equal 1
106
- grammar.rules.keys.must_equal ['rule']
107
- grammar.rules['rule'][:atoms].size.must_equal 1
124
+ describe 'when parsing rule references' do
125
+ it 'must parse a rule reference' do
126
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=<rule1>;<rule1>=one;').parse
127
+ grammar.rules.size.must_equal 2
128
+ grammar.rules.keys.must_equal ['rule', 'rule1']
129
+ grammar.rules['rule'].must_equal [{name:'rule1', weight:1.0, tags:[]}]
130
+ grammar.rules['rule1'].must_equal [{atom:'one', weight:1.0, tags:[]}]
131
+ end
132
+
133
+ it 'must parse a weighted rule reference' do
134
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>= /0.5/ <rule1> | /0.5/ two; <rule1>=one;').parse
135
+ grammar.rules.size.must_equal 2
136
+ grammar.rules.keys.must_equal ['rule', 'rule1']
137
+
138
+ grammar.rules['rule'].size.must_equal 1
139
+ grammar.rules['rule'].first.must_be_kind_of JSGF::Alternation
140
+ grammar.rules['rule'].first.elements.must_equal [{name:'rule1', weight:0.5, tags:[]}, {atom:'two', weight:0.5, tags:[]}]
141
+
142
+ grammar.rules['rule1'].must_equal [{atom:'one', weight:1.0, tags:[]}]
143
+ end
108
144
  end
109
145
  end
@@ -33,4 +33,10 @@ describe JSGF::Tokenizer do
33
33
  it 'must recognize a token' do
34
34
  JSGF::Tokenizer.new(' some_token ').next_token.must_equal [:TOKEN, 'some_token']
35
35
  end
36
+
37
+ describe 'when given comments' do
38
+ it 'must skip C-style comments' do
39
+ JSGF::Tokenizer.new(' /* a comment */ some_token ').next_token.must_equal [:TOKEN, 'some_token']
40
+ end
41
+ end
36
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsgf
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Fosdick
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-04 00:00:00.000000000 Z
11
+ date: 2015-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -46,6 +46,7 @@ extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
48
  - .gitignore
49
+ - .travis.yml
49
50
  - Gemfile
50
51
  - README.md
51
52
  - Rakefile
@@ -57,6 +58,7 @@ files:
57
58
  - lib/jsgf/parser.rb
58
59
  - lib/jsgf/repetition.rb
59
60
  - lib/jsgf/tokenizer.rb
61
+ - test/jsgf/grammar.rb
60
62
  - test/jsgf/parser.rb
61
63
  - test/jsgf/tokenizer.rb
62
64
  homepage: http://github.com/bfoz/jsgf-ruby
@@ -84,5 +86,6 @@ signing_key:
84
86
  specification_version: 4
85
87
  summary: Java Speech Grammar Format
86
88
  test_files:
89
+ - test/jsgf/grammar.rb
87
90
  - test/jsgf/parser.rb
88
91
  - test/jsgf/tokenizer.rb