jsgf 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/README.md +19 -12
- data/jsgf.gemspec +1 -1
- data/lib/jsgf/alternation.rb +1 -1
- data/lib/jsgf/grammar.rb +46 -1
- data/lib/jsgf/optional.rb +2 -0
- data/lib/jsgf/parser.rb +115 -90
- data/lib/jsgf/tokenizer.rb +6 -0
- data/test/jsgf/grammar.rb +66 -0
- data/test/jsgf/parser.rb +65 -29
- data/test/jsgf/tokenizer.rb +6 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fdf13c6642d0aa9a9faaa005d72563af8151ac68
|
4
|
+
data.tar.gz: 3014db9c26234c9744262e9c50a0198c9c81b466
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 140f2abe3f92727e630bf4aab4e4f33ba92afa09fc57e38f2e50d6669769a60fdc23ef0aa6c2565fa68b925874bffbec645507f6fd4afdc10500c3272293e907
|
7
|
+
data.tar.gz: aae7b624c87f88c923a2d6db6bb8e63ea6963860d50178cfb0b5128a74b4aee223abef8754343db1e725af6bff5ba93a6c124bbae0b08a0cad070d0069154825
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,8 +1,22 @@
|
|
1
|
-
|
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
|
-
|
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
|
-
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
|
-
|
25
|
-
## Contributing
|
35
|
+
License
|
36
|
+
-------
|
26
37
|
|
27
|
-
|
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.
|
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}
|
data/lib/jsgf/alternation.rb
CHANGED
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
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
|
-
|
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
|
-
|
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
|
-
|
78
|
-
40, 37,
|
79
|
-
40,
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
100
|
-
nil,
|
101
|
-
|
102
|
-
nil,
|
103
|
-
nil, nil, nil, nil,
|
104
|
-
|
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
|
-
-
|
109
|
-
-13, -
|
110
|
-
-3, -11, -
|
111
|
-
-15, -
|
112
|
-
-
|
113
|
-
-
|
114
|
-
-
|
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,
|
118
|
-
|
119
|
-
12,
|
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
|
-
|
123
|
-
|
124
|
-
8,
|
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,
|
128
|
-
|
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
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
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,
|
162
|
-
|
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 =
|
186
|
+
racc_reduce_n = 38
|
172
187
|
|
173
|
-
racc_shift_n =
|
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
|
-
"
|
249
|
-
"
|
250
|
-
"
|
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[
|
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
|
-
|
335
|
+
val[1][:visibility] = :public
|
320
336
|
result
|
321
337
|
end
|
322
338
|
|
323
339
|
def _reduce_19(val, _values, result)
|
324
|
-
result =
|
340
|
+
result = val.first
|
325
341
|
result
|
326
342
|
end
|
327
343
|
|
328
344
|
def _reduce_20(val, _values, result)
|
329
|
-
result = val
|
345
|
+
result = define_alternation(*val)
|
330
346
|
result
|
331
347
|
end
|
332
348
|
|
333
|
-
|
334
|
-
result = val
|
335
|
-
result
|
336
|
-
end
|
349
|
+
# reduce 21 omitted
|
337
350
|
|
338
351
|
# reduce 22 omitted
|
339
352
|
|
340
|
-
|
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
|
-
|
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
|
381
|
+
def _reduce_31(val, _values, result)
|
360
382
|
result = define_optional(val[1])
|
361
383
|
result
|
362
384
|
end
|
363
385
|
|
364
|
-
def
|
386
|
+
def _reduce_32(val, _values, result)
|
365
387
|
result = define_atom(val.first)
|
366
388
|
result
|
367
389
|
end
|
368
390
|
|
369
|
-
|
391
|
+
def _reduce_33(val, _values, result)
|
392
|
+
result = rule_reference(val[0])
|
393
|
+
result
|
394
|
+
end
|
370
395
|
|
371
|
-
# reduce
|
396
|
+
# reduce 34 omitted
|
372
397
|
|
373
|
-
# reduce
|
398
|
+
# reduce 35 omitted
|
374
399
|
|
375
|
-
def
|
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
|
405
|
+
def _reduce_37(val, _values, result)
|
381
406
|
result = JSGF::Repetition.new(val[0], 1)
|
382
407
|
result
|
383
408
|
end
|
data/lib/jsgf/tokenizer.rb
CHANGED
@@ -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']
|
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']
|
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']
|
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']
|
51
|
+
grammar.rules['rule'].size.must_equal 1
|
54
52
|
|
55
|
-
alternation = grammar.rules['rule']
|
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']
|
62
|
+
grammar.rules['rule'].size.must_equal 1
|
65
63
|
|
66
|
-
alternation = grammar.rules['rule']
|
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]
|
70
|
-
alternation.elements[1]
|
71
|
-
alternation.elements[2]
|
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']
|
75
|
+
grammar.rules['rule'].size.must_equal 1
|
78
76
|
|
79
|
-
alternation = grammar.rules['rule']
|
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]
|
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']
|
91
|
-
grammar.rules['rule']
|
92
|
-
grammar.rules['rule']
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
data/test/jsgf/tokenizer.rb
CHANGED
@@ -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.
|
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-
|
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
|