jsgf 0 → 0.1

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: 99ebe95b6502d991b3aa978503305a4c6f2dca47
4
- data.tar.gz: b15f9944f56e9a289694836b0b84a6c9733a1770
3
+ metadata.gz: 66ac3ad9f73f24c8c44884964606916ad2495680
4
+ data.tar.gz: 1a67637a02d934eee7ee0c53f8586217f68a6567
5
5
  SHA512:
6
- metadata.gz: 129c77802146376e42ab2c72197807c1c0dc126a0561c542856c0cb9c6d5103fe749263ba7b5f168c3c95e18d68476287c59dabb1ef5883952f47fd0421ab032
7
- data.tar.gz: 3d312bf34c5b5041dda707f834194c669d5c467d4e6daea5b80fce2e73860fbec9d23463b8e689062ecf5ae68b3f61b2b5108a0ed922b632c924a724638e357a
6
+ metadata.gz: 6b42838dcefd7eb536f0dd61a9c6ca352fba12b60577960ef316c35dda5d9ce0e681a34ba8b2dfcd70d11b6981f6c22a70700a6cdf806dfd538671df35789a73
7
+ data.tar.gz: c04356733209a868672753df49626aa595f9b56c91e2a3dfd95493c75599df244ac3b4d93c09f94a54f2e582c51895f02396ec120605e9234830941868534953
data/Gemfile CHANGED
@@ -2,3 +2,7 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in jsgf.gemspec
4
4
  gemspec
5
+
6
+ group :development do
7
+ gem 'racc'
8
+ end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Jsgf
2
2
 
3
- TODO: Write a gem description
3
+ [![Gem Version](https://badge.fury.io/rb/jsgf.svg)](http://badge.fury.io/rb/jsgf)
4
4
 
5
5
  ## Installation
6
6
 
data/Rakefile CHANGED
@@ -1,2 +1,18 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
2
3
 
4
+ rule '.rb' => '.y' do |t|
5
+ `racc -l -o #{t.name} #{t.source}`
6
+ end
7
+
8
+ task :default => :test
9
+ task compile: 'lib/jsgf/parser.rb'
10
+
11
+ # Recompile the parser before building the gem package
12
+ task :build => :compile
13
+
14
+ Rake::TestTask.new(:test => :compile) do |t|
15
+ t.libs.push "lib"
16
+ t.test_files = FileList['test/**/*.rb']
17
+ t.verbose = true
18
+ end
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.1'
8
8
  spec.authors = ["Brandon Fosdick"]
9
9
  spec.email = ["bfoz@bfoz.net"]
10
10
  spec.summary = %q{Java Speech Grammar Format}
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = "http://github.com/bfoz/jsgf-ruby"
13
13
  spec.license = "BSD"
14
14
 
15
- spec.files = `git ls-files -z`.split("\x0")
15
+ spec.files = `git ls-files -z`.split("\x0").push('lib/jsgf/parser.rb').reject {|f| File.basename(f) == 'parser.y'}
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
17
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
18
  spec.require_paths = ["lib"]
@@ -0,0 +1,25 @@
1
+ module JSGF
2
+ class Alternation
3
+ attr_reader :elements
4
+ attr_writer :optional
5
+ attr_reader :tags
6
+
7
+ def initialize(*args)
8
+ @elements = args
9
+ @optional = false
10
+ @tags = []
11
+ end
12
+
13
+ def push(*args)
14
+ @elements.push *args
15
+ end
16
+
17
+ def size
18
+ @elements.size
19
+ end
20
+
21
+ def weights
22
+ @elements.map {|a| a[:weight]}
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module JSGF
2
+ class Grammar
3
+ attr_reader :character_encoding
4
+ attr_reader :locale
5
+ attr_reader :private_rules
6
+ attr_reader :public_rules
7
+ attr_reader :version
8
+
9
+ def initialize(character_encoding:nil, locale:nil, private_rules:{}, public_rules:{}, version:nil)
10
+ @character_encoding = character_encoding
11
+ @locale = locale
12
+ @private_rules = private_rules
13
+ @public_rules = public_rules
14
+ @version = version
15
+ end
16
+
17
+ # @!attribute rules
18
+ # @return [Hash] Returns a {Hash} of the public and private rules, keyed by rule name
19
+ def rules
20
+ @public_rules.merge(@private_rules)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module JSGF
2
+ class Optional
3
+ def initialize(*args)
4
+ @elements = args
5
+ end
6
+
7
+ def optional
8
+ true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,390 @@
1
+ #
2
+ # DO NOT MODIFY!!!!
3
+ # This file is automatically generated by Racc 1.4.12
4
+ # from Racc grammer file "".
5
+ #
6
+
7
+ require 'racc/parser.rb'
8
+
9
+
10
+ require_relative 'alternation'
11
+ require_relative 'grammar'
12
+ require_relative 'optional'
13
+ require_relative 'repetition'
14
+
15
+ module JSGF
16
+ class Parser < Racc::Parser
17
+
18
+
19
+ attr_reader :grammar_name
20
+ attr_reader :handler
21
+
22
+ def initialize(tokenizer)
23
+ @private_rules = {}
24
+ @public_rules = {}
25
+ tokenizer = JSGF::Tokenizer.new(tokenizer) if tokenizer.is_a?(String)
26
+ @tokenizer = tokenizer
27
+ super()
28
+ end
29
+
30
+ def define_alternation(*args)
31
+ if args.first.is_a?(JSGF::Alternation)
32
+ args.first.push *(args.drop(2))
33
+ args.first
34
+ else
35
+ JSGF::Alternation.new(*(args.each_slice(2).map {|a,b| a}))
36
+ end
37
+ end
38
+
39
+ def define_optional(*args)
40
+ if args.first.respond_to?(:optional)
41
+ args.first.optional = true
42
+ args.first
43
+ else
44
+ JSGF::Optional.new(*args)
45
+ end
46
+ end
47
+
48
+ def define_atom(atom, weight=1.0, tags=[])
49
+ {atom:atom, weight:weight, tags:[]}
50
+ end
51
+
52
+ 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
59
+ r
60
+ end
61
+
62
+ def next_token
63
+ @tokenizer.next_token
64
+ end
65
+
66
+ def parse
67
+ do_parse
68
+ JSGF::Grammar.new( character_encoding:@charset,
69
+ locale:@locale,
70
+ private_rules:@private_rules,
71
+ public_rules:@public_rules,
72
+ version:@version)
73
+ end
74
+ ##### State transition tables begin ###
75
+
76
+ 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 ]
86
+
87
+ 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 ]
97
+
98
+ 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 ]
106
+
107
+ 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 ]
115
+
116
+ 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 ]
120
+
121
+ 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 ]
125
+
126
+ racc_goto_pointer = [
127
+ nil, 4, 22, 3, 10, 11, 5, 12, 0, -3,
128
+ -23, -31, -32, nil, -14, nil, nil, nil ]
129
+
130
+ racc_goto_default = [
131
+ nil, nil, nil, nil, nil, nil, nil, nil, 41, 10,
132
+ nil, 32, 33, 34, 35, 36, 42, 43 ]
133
+
134
+ racc_reduce_table = [
135
+ 0, 0, :racc_error,
136
+ 1, 21, :_reduce_none,
137
+ 2, 21, :_reduce_none,
138
+ 3, 21, :_reduce_none,
139
+ 2, 22, :_reduce_none,
140
+ 2, 25, :_reduce_none,
141
+ 3, 25, :_reduce_6,
142
+ 4, 25, :_reduce_7,
143
+ 5, 25, :_reduce_8,
144
+ 3, 26, :_reduce_9,
145
+ 1, 24, :_reduce_none,
146
+ 2, 24, :_reduce_none,
147
+ 3, 27, :_reduce_none,
148
+ 1, 23, :_reduce_none,
149
+ 2, 23, :_reduce_none,
150
+ 3, 28, :_reduce_15,
151
+ 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,
157
+ 1, 32, :_reduce_none,
158
+ 2, 32, :_reduce_23,
159
+ 1, 33, :_reduce_none,
160
+ 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 ]
170
+
171
+ racc_reduce_n = 35
172
+
173
+ racc_shift_n = 63
174
+
175
+ racc_token_table = {
176
+ false => 0,
177
+ :error => 1,
178
+ :GRAMMAR => 2,
179
+ :HEADER => 3,
180
+ :IMPORT => 4,
181
+ :PUBLIC => 5,
182
+ :TAG => 6,
183
+ :TOKEN => 7,
184
+ :WEIGHT => 8,
185
+ ";" => 9,
186
+ "<" => 10,
187
+ ">" => 11,
188
+ "=" => 12,
189
+ "|" => 13,
190
+ "(" => 14,
191
+ ")" => 15,
192
+ "[" => 16,
193
+ "]" => 17,
194
+ "*" => 18,
195
+ "+" => 19 }
196
+
197
+ racc_nt_base = 20
198
+
199
+ racc_use_result_var = true
200
+
201
+ Racc_arg = [
202
+ racc_action_table,
203
+ racc_action_check,
204
+ racc_action_default,
205
+ racc_action_pointer,
206
+ racc_goto_table,
207
+ racc_goto_check,
208
+ racc_goto_default,
209
+ racc_goto_pointer,
210
+ racc_nt_base,
211
+ racc_reduce_table,
212
+ racc_token_table,
213
+ racc_shift_n,
214
+ racc_reduce_n,
215
+ racc_use_result_var ]
216
+
217
+ Racc_token_to_s_table = [
218
+ "$end",
219
+ "error",
220
+ "GRAMMAR",
221
+ "HEADER",
222
+ "IMPORT",
223
+ "PUBLIC",
224
+ "TAG",
225
+ "TOKEN",
226
+ "WEIGHT",
227
+ "\";\"",
228
+ "\"<\"",
229
+ "\">\"",
230
+ "\"=\"",
231
+ "\"|\"",
232
+ "\"(\"",
233
+ "\")\"",
234
+ "\"[\"",
235
+ "\"]\"",
236
+ "\"*\"",
237
+ "\"+\"",
238
+ "$start",
239
+ "grammar",
240
+ "header",
241
+ "rule_list",
242
+ "import_header",
243
+ "jsgf_header",
244
+ "grammar_header",
245
+ "import_statement",
246
+ "rule_name",
247
+ "rule",
248
+ "alternate_list",
249
+ "rule_expansion",
250
+ "tagged_rule_item",
251
+ "rule_item",
252
+ "rule_atom",
253
+ "weighted_item",
254
+ "rule_group",
255
+ "rule_optional" ]
256
+
257
+ Racc_debug_parser = false
258
+
259
+ ##### State transition tables end #####
260
+
261
+ # reduce 0 omitted
262
+
263
+ # reduce 1 omitted
264
+
265
+ # reduce 2 omitted
266
+
267
+ # reduce 3 omitted
268
+
269
+ # reduce 4 omitted
270
+
271
+ # reduce 5 omitted
272
+
273
+ def _reduce_6(val, _values, result)
274
+ @version = val[1];
275
+ result
276
+ end
277
+
278
+ def _reduce_7(val, _values, result)
279
+ @version = val[1]; @charset = val[2];
280
+ result
281
+ end
282
+
283
+ def _reduce_8(val, _values, result)
284
+ @version = val[1]; @charset = val[2]; @locale = val[3];
285
+ result
286
+ end
287
+
288
+ def _reduce_9(val, _values, result)
289
+ @grammar_name = val[1]
290
+ result
291
+ end
292
+
293
+ # reduce 10 omitted
294
+
295
+ # reduce 11 omitted
296
+
297
+ # reduce 12 omitted
298
+
299
+ # reduce 13 omitted
300
+
301
+ # reduce 14 omitted
302
+
303
+ def _reduce_15(val, _values, result)
304
+ result = val[1]
305
+ result
306
+ end
307
+
308
+ def _reduce_16(val, _values, result)
309
+ define_rule(val[0], :private, *(val[2..-2]))
310
+ result
311
+ end
312
+
313
+ def _reduce_17(val, _values, result)
314
+ define_rule(val[1], :public, val[3])
315
+ result
316
+ end
317
+
318
+ def _reduce_18(val, _values, result)
319
+ result = val.first
320
+ result
321
+ end
322
+
323
+ def _reduce_19(val, _values, result)
324
+ result = define_alternation(*val)
325
+ result
326
+ end
327
+
328
+ def _reduce_20(val, _values, result)
329
+ result = val.first
330
+ result
331
+ end
332
+
333
+ def _reduce_21(val, _values, result)
334
+ result = val
335
+ result
336
+ end
337
+
338
+ # reduce 22 omitted
339
+
340
+ def _reduce_23(val, _values, result)
341
+ val[0][:tags].push(val[1]); result = val[0]
342
+ result
343
+ end
344
+
345
+ # reduce 24 omitted
346
+
347
+ # reduce 25 omitted
348
+
349
+ def _reduce_26(val, _values, result)
350
+ val[1][:weight] = val.first[1..-2].to_f; result = val[1]
351
+ result
352
+ end
353
+
354
+ def _reduce_27(val, _values, result)
355
+ result = val[1]
356
+ result
357
+ end
358
+
359
+ def _reduce_28(val, _values, result)
360
+ result = define_optional(val[1])
361
+ result
362
+ end
363
+
364
+ def _reduce_29(val, _values, result)
365
+ result = define_atom(val.first)
366
+ result
367
+ end
368
+
369
+ # reduce 30 omitted
370
+
371
+ # reduce 31 omitted
372
+
373
+ # reduce 32 omitted
374
+
375
+ def _reduce_33(val, _values, result)
376
+ result = JSGF::Repetition.new(val[0], 0)
377
+ result
378
+ end
379
+
380
+ def _reduce_34(val, _values, result)
381
+ result = JSGF::Repetition.new(val[0], 1)
382
+ result
383
+ end
384
+
385
+ def _reduce_none(val, _values, result)
386
+ val[0]
387
+ end
388
+
389
+ end # class Parser
390
+ end # module JSGF
@@ -0,0 +1,10 @@
1
+ module JSGF
2
+ class Repetition
3
+ # @param atom [Hash] the atom to repeat
4
+ # @param minimum_count [Number] the minimum repetition count (0 or 1)
5
+ def initialize(atom, minimum_count)
6
+ @atom = atom
7
+ @count = minimum_count
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ require 'strscan'
2
+
3
+ module JSGF
4
+ class Tokenizer
5
+ TOKENS = {
6
+ /grammar/ => :GRAMMAR,
7
+ /#JSGF/ => :HEADER,
8
+ /import/ => :IMPORT,
9
+ /public/ => :PUBLIC,
10
+ /\{(\\.|[^\}]+)*\}/ => :TAG,
11
+ /[^ \t\n;=|*+<>\(\)\[\]{}*\/]+/ => :TOKEN,
12
+ %r{/\d*(\.\d+)?/} => :WEIGHT,
13
+ }
14
+
15
+ # @param io [IO] the {IO} stream to read from
16
+ def initialize(io)
17
+ io = io.read unless io.is_a?(String)
18
+ @scanner = StringScanner.new(io)
19
+ end
20
+
21
+ def next_token
22
+ return if @scanner.eos?
23
+
24
+ @scanner.skip(/\s+/) # Skip all leading whitespace
25
+ @scanner.skip(%r{//.*\n}) # Skip single-line comments
26
+
27
+ TOKENS.each do |(pattern, token)|
28
+ text = @scanner.scan(pattern)
29
+ return [token, text] if text
30
+ end
31
+ x = @scanner.getch
32
+ [x, x] unless x.nil?
33
+ end
34
+ end
35
+ end
data/lib/jsgf.rb CHANGED
@@ -1,3 +1,19 @@
1
- module Jsgf
2
- # Your code goes here...
1
+ require_relative 'jsgf/parser'
2
+ require_relative 'jsgf/tokenizer'
3
+
4
+ =begin
5
+ A parser for JSGF files.
6
+
7
+ http://www.w3.org/TR/jsgf/
8
+
9
+ # Usage
10
+ grammar = JSGF.read(filename)
11
+ =end
12
+ module JSGF
13
+ # @param filename [String] the file to parse
14
+ # @return [Grammar] the resulting {Grammar}
15
+ def self.read(filename)
16
+ tokenzier = JSGF::Tokenizer.new(File.read(filename))
17
+ JSGF::Parser.new(tokenzier).parse
18
+ end
3
19
  end
@@ -0,0 +1,109 @@
1
+ require 'minitest/autorun'
2
+ require 'jsgf/parser'
3
+
4
+ describe JSGF::Parser do
5
+ describe 'when parsing the header' do
6
+ it 'must parse the version' do
7
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;').parse
8
+ grammar.version.must_equal 'V1.0'
9
+ end
10
+
11
+ it 'must parse the character encoding' do
12
+ grammar = JSGF::Parser.new('#JSGF V1.0 CHARSET; grammar header_grammar;').parse
13
+ grammar.version.must_equal 'V1.0'
14
+ grammar.character_encoding.must_equal 'CHARSET'
15
+ end
16
+
17
+ it 'must parse the locale' do
18
+ grammar = JSGF::Parser.new('#JSGF V1.0 CHARSET locale; grammar header_grammar;').parse
19
+ grammar.version.must_equal 'V1.0'
20
+ grammar.character_encoding.must_equal 'CHARSET'
21
+ grammar.locale.must_equal 'locale'
22
+ end
23
+ end
24
+
25
+ it 'must parse a rule with a single atom' do
26
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one;').parse
27
+ grammar.rules.size.must_equal 1
28
+ grammar.rules.keys.must_equal ['rule']
29
+ grammar.rules['rule'][:atoms].size.must_equal 1
30
+ end
31
+
32
+ it 'must parse a rule with multiple atoms' do
33
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one two;').parse
34
+ grammar.rules.size.must_equal 1
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
38
+ end
39
+
40
+ it 'must parse a rule with multiple grouped atoms' do
41
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one two);').parse
42
+ grammar.rules.size.must_equal 1
43
+ 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
46
+ end
47
+
48
+ describe 'when parsing an alternation' do
49
+ it 'must parse a rule with an alternation' do
50
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one | two;').parse
51
+ grammar.rules.size.must_equal 1
52
+ grammar.rules.keys.must_equal ['rule']
53
+ grammar.rules['rule'][:atoms].size.must_equal 1
54
+
55
+ alternation = grammar.rules['rule'][:atoms].first
56
+ alternation.must_be_kind_of JSGF::Alternation
57
+ alternation.size.must_equal 2
58
+ end
59
+
60
+ it 'must parse a rule with a bigger alternation' do
61
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=one | two | three;').parse
62
+ grammar.rules.size.must_equal 1
63
+ grammar.rules.keys.must_equal ['rule']
64
+ grammar.rules['rule'][:atoms].size.must_equal 1
65
+
66
+ alternation = grammar.rules['rule'][:atoms].first
67
+ alternation.must_be_kind_of JSGF::Alternation
68
+ 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'
72
+ end
73
+
74
+ it 'must parse a weighted alternation' do
75
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>= /0.5/ one | /0.25/ two | /0.25/ three;').parse
76
+ grammar.rules.keys.must_equal ['rule']
77
+ grammar.rules['rule'][:atoms].size.must_equal 1
78
+
79
+ alternation = grammar.rules['rule'][:atoms].first
80
+ alternation.must_be_kind_of JSGF::Alternation
81
+ alternation.size.must_equal 3
82
+ alternation.weights.must_equal [0.5, 0.25, 0.25]
83
+ alternation.elements[0][:atom].must_equal 'one'
84
+ end
85
+
86
+ it 'must parse a grouped alternation' do
87
+ grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=(one | two | three);').parse
88
+ grammar.rules.size.must_equal 1
89
+ 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
93
+ end
94
+ end
95
+
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
101
+ end
102
+
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
108
+ end
109
+ end
@@ -0,0 +1,36 @@
1
+ require 'minitest/autorun'
2
+ require 'jsgf/tokenizer'
3
+
4
+ describe JSGF::Tokenizer do
5
+ it 'must pass individual characters' do
6
+ JSGF::Tokenizer.new(';').next_token.must_equal [';', ';']
7
+ end
8
+
9
+ it 'must recognize the grammar keyword' do
10
+ JSGF::Tokenizer.new('grammar').next_token.must_equal [:GRAMMAR, 'grammar']
11
+ end
12
+
13
+ it 'must skip leading whitespace' do
14
+ JSGF::Tokenizer.new(' grammar').next_token.must_equal [:GRAMMAR, 'grammar']
15
+ end
16
+
17
+ it 'must recognize the header string' do
18
+ JSGF::Tokenizer.new('#JSGF').next_token.must_equal [:HEADER, '#JSGF']
19
+ end
20
+
21
+ it 'must recognize the public keyword' do
22
+ JSGF::Tokenizer.new('public').next_token.must_equal [:PUBLIC, 'public']
23
+ end
24
+
25
+ it 'must recognize a tag block ' do
26
+ JSGF::Tokenizer.new('{ some stuff }').next_token.must_equal [:TAG, '{ some stuff }']
27
+ end
28
+
29
+ it 'must recognize a weight' do
30
+ JSGF::Tokenizer.new('/0.5/').next_token.must_equal [:WEIGHT, '/0.5/']
31
+ end
32
+
33
+ it 'must recognize a token' do
34
+ JSGF::Tokenizer.new(' some_token ').next_token.must_equal [:TOKEN, 'some_token']
35
+ end
36
+ 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.1'
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-02 00:00:00.000000000 Z
11
+ date: 2015-01-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -51,6 +51,14 @@ files:
51
51
  - Rakefile
52
52
  - jsgf.gemspec
53
53
  - lib/jsgf.rb
54
+ - lib/jsgf/alternation.rb
55
+ - lib/jsgf/grammar.rb
56
+ - lib/jsgf/optional.rb
57
+ - lib/jsgf/parser.rb
58
+ - lib/jsgf/repetition.rb
59
+ - lib/jsgf/tokenizer.rb
60
+ - test/jsgf/parser.rb
61
+ - test/jsgf/tokenizer.rb
54
62
  homepage: http://github.com/bfoz/jsgf-ruby
55
63
  licenses:
56
64
  - BSD
@@ -75,4 +83,6 @@ rubygems_version: 2.2.2
75
83
  signing_key:
76
84
  specification_version: 4
77
85
  summary: Java Speech Grammar Format
78
- test_files: []
86
+ test_files:
87
+ - test/jsgf/parser.rb
88
+ - test/jsgf/tokenizer.rb