jsgf 0.1 → 0.2

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