jsgf 0.4.1 → 0.5

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: ae316ae11c63ab1272f04970e188ac162df75dd5
4
- data.tar.gz: 4e960c23772bc117c4b7955144b72eeffc2ac3b5
3
+ metadata.gz: dc08df8d2819a5f14a5c0a037dc0cf1b6c337e0b
4
+ data.tar.gz: d605f5c4211fce201299b52fb824e90d7148034e
5
5
  SHA512:
6
- metadata.gz: 42d12240171fd99346507d7850f73e880c321337bf50d421cf4411e5a1d300badc2c4459491fcacc2c97716f739616488bb74c4c95a79912bae51dbfa3617536
7
- data.tar.gz: 4e34672b1e1cd91993f55d069270e692fddb16590610934cce1136b04c9c10b21c77c369afb55269802b547d5e562e5cc7c36a305d9678803468e5221b7c014e
6
+ metadata.gz: 09e2f534bf4bfeff71a197b21b83f816b7311a8c2242f7815bf3e5ace6ae158da5ec95af03c5a8995aceb53c3440e8275d3a3531ae8295d1159c6aa3eefada3d
7
+ data.tar.gz: d90cf4b5c10039c20282d533393ffb2e9eb8e138a4c8cb98bb6c5b4e08f9e29642b133cd1cd24f1ced87b08da44fda4a3cbb88e4db4c90ac8f188f73ec7b1e40
data/README.md CHANGED
@@ -3,6 +3,7 @@ JSGF
3
3
 
4
4
  [![Build Status](https://travis-ci.org/bfoz/jsgf-ruby.png)](https://travis-ci.org/bfoz/jsgf-ruby)
5
5
  [![Gem Version](https://badge.fury.io/rb/jsgf.svg)](http://badge.fury.io/rb/jsgf)
6
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg?style=flat)](http://www.rubydoc.info/gems/jsgf/frames)
6
7
 
7
8
  For all of your [Java Speech Grammar Format](http://www.w3.org/TR/jsgf/) parsing needs.
8
9
 
@@ -37,7 +38,7 @@ The JSGF gem includes a simple DSL for generating new grammars. The syntax follo
37
38
  the [JSGF](http://www.w3.org/TR/jsgf/) syntax, but with a few differences.
38
39
 
39
40
  - Rule names can be either Symbols or Strings (they're converted to Strings internally)
40
- - Rules can be referenced using symbols in addition to the angle-bracket syntax used by JSGF.
41
+ - Rules can be referenced using symbols in addition to the angle-bracket syntax used by JSGF
41
42
  - Alternations are created using arrays
42
43
  - Rules are private by default, however the root rules are automatically made public
43
44
 
@@ -51,6 +52,15 @@ grammar = JSGF.grammar 'Turtle' do
51
52
  end
52
53
  ```
53
54
 
55
+ Atoms can be made optional using the JSGF square-bracket syntax...
56
+
57
+ ```ruby
58
+ grammar = JSGF.grammar 'PoliteTurtle' do
59
+ rule move: '[please] go :direction :distance'
60
+ ...
61
+ end
62
+ ```
63
+
54
64
  Installation
55
65
  ------------
56
66
 
@@ -71,4 +81,4 @@ Or install it yourself as:
71
81
  License
72
82
  -------
73
83
 
74
- Copyright 2015 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
84
+ Copyright 2015-2016 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
@@ -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.4.1'
7
+ spec.version = '0.5'
8
8
  spec.authors = ["Brandon Fosdick"]
9
9
  spec.email = ["bfoz@bfoz.net"]
10
10
  spec.summary = %q{Java Speech Grammar Format}
@@ -5,14 +5,19 @@ module JSGF
5
5
  include Enumerable
6
6
 
7
7
  attr_reader :elements
8
+
9
+ # @!attribute
10
+ # @return [Bool] Sometimes an {Alternation} is optional
8
11
  attr_accessor :optional
12
+ alias optional? optional
13
+
9
14
  attr_reader :tags
10
15
 
11
16
  def initialize(*args)
12
17
  @elements = args.map do |a|
13
18
  case a
14
19
  when String then Rule.parse_atom(a)
15
- when Symbol then {name:a.to_s, weight:1.0, tags:[]}
20
+ when Symbol then JSGF::Atom.new(a.to_s, reference:true)
16
21
  else
17
22
  a
18
23
  end
@@ -4,6 +4,16 @@ module JSGF
4
4
  # @return [String] the atom of the {Atom}
5
5
  attr_accessor :atom
6
6
 
7
+ # @!attribute
8
+ # @return [Bool] Sometimes an {Atom} is optional
9
+ attr_accessor :optional
10
+ alias optional? optional
11
+
12
+ # @!attribute reference
13
+ # @return [Bool] The {Atom} is a reference to a {Rule}
14
+ attr_accessor :reference
15
+ alias reference? reference
16
+
7
17
  # @!attribute weight
8
18
  # @return [Number] the {Atom}'s weight, when part of an {Alternation}. Defaults to 1.0.
9
19
  attr_accessor :weight
@@ -15,8 +25,10 @@ module JSGF
15
25
  # @param atom [String] the atom of the {Atom}
16
26
  # @param weight [Number] the weight to be used when part of an {Alternation}. Valid values are 0..1.0.
17
27
  # @param tags [Array] any tags to be stored with the {Atom}
18
- def initialize(atom, *tags, weight:nil)
28
+ def initialize(atom, *tags, weight:nil, optional:nil, reference:nil)
19
29
  @atom = atom
30
+ @optional = optional
31
+ @reference = reference
20
32
  @tags = tags
21
33
  @weight = (weight && (weight != 1.0)) ? weight : nil
22
34
  end
@@ -28,7 +40,9 @@ module JSGF
28
40
 
29
41
  # Stringify in a manner suitable for output to a JSGF file
30
42
  def to_s
31
- [(weight && (weight != 1.0)) ? "/#{weight}/" : nil, atom, *tags].compact.join(' ')
43
+ s = [(weight && (weight != 1.0)) ? "/#{weight}/" : nil, reference? ? '<'+atom+'>' : atom, *tags].compact.join(' ')
44
+ s = '[' + s + ']' if optional?
45
+ s
32
46
  end
33
47
  end
34
48
  end
@@ -38,9 +38,33 @@ module JSGF
38
38
  options.each do |name, v|
39
39
  @rules[name.to_s] = case v
40
40
  when Array then Rule.new [Alternation.new(*v)]
41
- when Symbol then Rule.new [{name:v.to_s, weight:1.0, tags:[]}]
41
+ when Symbol then Rule.new [Rule.parse_atom(v.to_s).tap {|a| a.reference=true}]
42
42
  else
43
- v.split(' ').map {|a| Rule.parse_atom(a) }
43
+ stack = nil
44
+ v.split(' ').map do |a|
45
+ if stack
46
+ if a == ']'
47
+ next if stack.empty?
48
+
49
+ if stack.length == 1
50
+ stack.first.optional = true
51
+ stack.first
52
+ else
53
+ Optional.new(*stack)
54
+ end.tap do
55
+ stack = nil
56
+ end
57
+ else
58
+ stack.push(Rule.parse_atom(a))
59
+ next
60
+ end
61
+ elsif a == '['
62
+ stack = []
63
+ next
64
+ else
65
+ Rule.parse_atom(a)
66
+ end
67
+ end.compact
44
68
  end
45
69
  end
46
70
  end
@@ -27,11 +27,9 @@ module JSGF
27
27
  case rule
28
28
  when Alternation, Array, Optional
29
29
  rule.flat_map {|a| find_rule_names(a) }
30
- when Atom then []
31
- when Hash
32
- rule[:name]
30
+ when Atom then rule.reference ? rule.atom : []
33
31
  else
34
- raise StandardError, "Unkown atom #{rule.class}"
32
+ raise StandardError, "Unkown atom #{rule.class}: #{rule}"
35
33
  end
36
34
  end
37
35
 
@@ -55,7 +55,7 @@ def define_rule(name, visibility=:private, *args)
55
55
  end
56
56
 
57
57
  def rule_reference(name)
58
- {name:name, weight:1.0, tags:[]}
58
+ JSGF::Atom.new(name, reference:true)
59
59
  end
60
60
 
61
61
  def next_token
@@ -2,11 +2,13 @@ module JSGF
2
2
  class Rule < Array
3
3
  # Convert a string containing a single atom into an {Atom} or a rule reference
4
4
  # @param atom [String] the text to parse
5
- def self.parse_atom(atom)
5
+ def self.parse_atom(atom, optional:nil)
6
6
  case atom
7
- when /\<(.*)\>/, /:(.*)/ then {name:$1, weight:1.0, tags:[]}
7
+ # Parse optionals first to prevent the reference-regex from grabbing it first
8
+ when /\[(.*)\]/ then parse_atom($1, optional:true)
9
+ when /\<(.*)\>/, /:(.*)/ then Atom.new($1, optional:optional, reference:true)
8
10
  else
9
- Atom.new(atom)
11
+ Atom.new(atom, optional:optional)
10
12
  end
11
13
  end
12
14
  end
@@ -20,13 +20,13 @@ describe JSGF::Builder do
20
20
  end
21
21
 
22
22
  grammar.rules.size.must_equal 1
23
- grammar.rules['rule1'].must_equal [Atom.new('one')]
23
+ grammar.rules['rule1'].must_equal [JSGF::Atom.new('one')]
24
24
  end
25
25
 
26
26
  it 'must build a multi-atom rule' do
27
27
  grammar = JSGF::Builder.build {rule rule1: 'one two' }
28
28
  grammar.rules.size.must_equal 1
29
- grammar.rules['rule1'].must_equal [Atom.new('one'), Atom.new('two')]
29
+ grammar.rules['rule1'].must_equal [JSGF::Atom.new('one'), JSGF::Atom.new('two')]
30
30
  end
31
31
 
32
32
  it 'must build a rule with a rule reference as a Symbol' do
@@ -36,7 +36,7 @@ describe JSGF::Builder do
36
36
  end
37
37
 
38
38
  grammar.rules.size.must_equal 2
39
- grammar.rules['rule1'].must_equal [{name:'one', weight:1.0, tags:[]}]
39
+ grammar.rules['rule1'].must_equal [JSGF::Atom.new('one', reference:true)]
40
40
  end
41
41
 
42
42
  it 'must build a rule with a JSGF-style rule reference embedded in a string' do
@@ -46,7 +46,7 @@ describe JSGF::Builder do
46
46
  end
47
47
 
48
48
  grammar.rules.size.must_equal 2
49
- grammar.rules['rule1'].must_equal [{name:'one', weight:1.0, tags:[]}]
49
+ grammar.rules['rule1'].must_equal [JSGF::Atom.new('one', reference:true)]
50
50
  end
51
51
 
52
52
  it 'must build a rule with a rule reference symbol embedded in a string' do
@@ -56,7 +56,7 @@ describe JSGF::Builder do
56
56
  end
57
57
 
58
58
  grammar.rules.size.must_equal 2
59
- grammar.rules['rule1'].must_equal [{name:'one', weight:1.0, tags:[]}]
59
+ grammar.rules['rule1'].must_equal [JSGF::Atom.new('one', reference:true)]
60
60
  end
61
61
 
62
62
  describe 'alternation' do
@@ -66,7 +66,7 @@ describe JSGF::Builder do
66
66
  end
67
67
  grammar.rules.size.must_equal 1
68
68
  grammar.rules['rule1'].first.must_be_kind_of JSGF::Alternation
69
- grammar.rules['rule1'].first.elements.must_equal [Atom.new('one'), Atom.new('two')]
69
+ grammar.rules['rule1'].first.elements.must_equal [JSGF::Atom.new('one'), JSGF::Atom.new('two')]
70
70
  end
71
71
 
72
72
  it 'must build an alternation from an array of rule reference symbols' do
@@ -78,7 +78,7 @@ describe JSGF::Builder do
78
78
 
79
79
  grammar.rules.size.must_equal 3
80
80
  grammar.rules['rule1'].first.must_be_kind_of JSGF::Alternation
81
- grammar.rules['rule1'].first.elements.must_equal [{name:'one', weight:1.0, tags:[]}, {name:'two', weight:1.0, tags:[]}]
81
+ grammar.rules['rule1'].first.elements.must_equal [JSGF::Atom.new('one', reference:true), JSGF::Atom.new('two', reference:true)]
82
82
  end
83
83
 
84
84
  it 'must build an alternation from an array of strings containing embedded rule reference symbols' do
@@ -90,7 +90,7 @@ describe JSGF::Builder do
90
90
 
91
91
  grammar.rules.size.must_equal 3
92
92
  grammar.rules['rule1'].first.must_be_kind_of JSGF::Alternation
93
- grammar.rules['rule1'].first.elements.must_equal [{name:'one', weight:1.0, tags:[]}, {name:'two', weight:1.0, tags:[]}]
93
+ grammar.rules['rule1'].first.elements.must_equal [JSGF::Atom.new('one', reference:true), JSGF::Atom.new('two', reference:true)]
94
94
  end
95
95
 
96
96
  it 'must build an alternation from an array of strings containing embedded JSGF-style rule reference names' do
@@ -102,7 +102,113 @@ describe JSGF::Builder do
102
102
 
103
103
  grammar.rules.size.must_equal 3
104
104
  grammar.rules['rule1'].first.must_be_kind_of JSGF::Alternation
105
- grammar.rules['rule1'].first.elements.must_equal [{name:'one', weight:1.0, tags:[]}, {name:'two', weight:1.0, tags:[]}]
105
+ grammar.rules['rule1'].first.elements.must_equal [JSGF::Atom.new('one', reference:true), JSGF::Atom.new('two', reference:true)]
106
+ end
107
+ end
108
+
109
+ describe 'optional' do
110
+ it 'must parse a rule with an optional word' do
111
+ grammar = JSGF::Builder.build do
112
+ rule rule1: "this [is] optional"
113
+ end
114
+
115
+ grammar.rules.size.must_equal 1
116
+ rule = grammar.rules['rule1']
117
+ rule.size.must_equal 3
118
+
119
+ rule.first.atom.must_equal 'this'
120
+ rule.first.wont_be :optional?
121
+
122
+ rule[1].atom.must_equal 'is'
123
+ rule[1].must_be :optional?
124
+
125
+ rule.last.atom.must_equal 'optional'
126
+ rule.last.wont_be :optional?
127
+ end
128
+
129
+ it 'must parse a rule with an optional reference' do
130
+ grammar = JSGF::Builder.build do
131
+ rule rule1: "this [:is] optional"
132
+ end
133
+
134
+ grammar.rules.size.must_equal 1
135
+ rule = grammar.rules['rule1']
136
+ rule.size.must_equal 3
137
+
138
+ rule.first.atom.must_equal 'this'
139
+ rule.first.wont_be :optional?
140
+
141
+ rule[1].atom.must_equal 'is'
142
+ rule[1].must_be :optional?
143
+ rule[1].must_be :reference?
144
+
145
+ rule.last.atom.must_equal 'optional'
146
+ rule.last.wont_be :optional?
147
+ end
148
+
149
+ it 'must parse a rule with an optional word surrounded by whitespace' do
150
+ grammar = JSGF::Builder.build do
151
+ rule rule1: "this [ is ] optional"
152
+ end
153
+
154
+ grammar.rules.size.must_equal 1
155
+ rule = grammar.rules['rule1']
156
+ rule.size.must_equal 3
157
+ rule.map(&:atom).must_equal ['this', 'is', 'optional']
158
+
159
+ rule.first.wont_be :optional?
160
+ rule[1].must_be :optional?
161
+ rule.last.wont_be :optional?
162
+ end
163
+
164
+ it 'must parse a rule with an optional reference surrounded by whitespace' do
165
+ grammar = JSGF::Builder.build do
166
+ rule rule1: "this [ :is ] optional"
167
+ end
168
+
169
+ grammar.rules.size.must_equal 1
170
+ rule = grammar.rules['rule1']
171
+ rule.size.must_equal 3
172
+ rule.map(&:atom).must_equal ['this', 'is', 'optional']
173
+
174
+ rule.first.wont_be :optional?
175
+
176
+ rule[1].must_be :optional?
177
+ rule[1].must_be :reference?
178
+
179
+ rule.last.wont_be :optional?
180
+ end
181
+
182
+ it 'must parse a rule with an optional words surrounded by whitespace' do
183
+ grammar = JSGF::Builder.build do
184
+ rule rule1: "this [ is optional ]"
185
+ end
186
+
187
+ grammar.rules.size.must_equal 1
188
+ rule = grammar.rules['rule1']
189
+ rule.size.must_equal 2
190
+
191
+ rule.first.atom.must_equal 'this'
192
+ rule.first.wont_be :optional?
193
+
194
+ rule[1].must_be_kind_of JSGF::Optional
195
+ rule[1].elements.map(&:atom).must_equal ['is', 'optional']
196
+ end
197
+
198
+ it 'must parse a rule with an optional references surrounded by whitespace' do
199
+ grammar = JSGF::Builder.build do
200
+ rule rule1: "this [ :is :optional ]"
201
+ end
202
+
203
+ grammar.rules.size.must_equal 1
204
+ rule = grammar.rules['rule1']
205
+ rule.size.must_equal 2
206
+
207
+ rule.first.atom.must_equal 'this'
208
+ rule.first.wont_be :optional?
209
+
210
+ rule.last.must_be_kind_of JSGF::Optional
211
+ rule.last.elements.map(&:atom).must_equal ['is', 'optional']
106
212
  end
107
213
  end
108
214
  end
@@ -128,7 +128,7 @@ describe JSGF::Parser do
128
128
  grammar = JSGF::Parser.new('#JSGF V1.0; grammar header_grammar;<rule>=<rule1>;<rule1>=one;').parse
129
129
  grammar.rules.size.must_equal 2
130
130
  grammar.rules.keys.must_equal ['rule', 'rule1']
131
- grammar.rules['rule'].must_equal [{name:'rule1', weight:1.0, tags:[]}]
131
+ grammar.rules['rule'].must_equal [JSGF::Atom.new('rule1', reference:true)]
132
132
  grammar.rules['rule1'].must_equal [Atom.new('one')]
133
133
  end
134
134
 
@@ -139,7 +139,7 @@ describe JSGF::Parser do
139
139
 
140
140
  grammar.rules['rule'].size.must_equal 1
141
141
  grammar.rules['rule'].first.must_be_kind_of JSGF::Alternation
142
- grammar.rules['rule'].first.elements.must_equal [{name:'rule1', weight:0.5, tags:[]}, Atom.new('two', weight:0.5)]
142
+ grammar.rules['rule'].first.elements.must_equal [JSGF::Atom.new('rule1', weight:0.5, reference:true), Atom.new('two', weight:0.5)]
143
143
 
144
144
  grammar.rules['rule1'].must_equal [Atom.new('one')]
145
145
  end
metadata CHANGED
@@ -1,41 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsgf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: '0.5'
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-07-05 00:00:00.000000000 Z
11
+ date: 2016-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.7'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '10.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
41
  description: A parser and DSL for JSGF files
@@ -45,8 +45,8 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
- - .gitignore
49
- - .travis.yml
48
+ - ".gitignore"
49
+ - ".travis.yml"
50
50
  - Gemfile
51
51
  - README.md
52
52
  - Rakefile
@@ -78,17 +78,17 @@ require_paths:
78
78
  - lib
79
79
  required_ruby_version: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - '>='
81
+ - - ">="
82
82
  - !ruby/object:Gem::Version
83
83
  version: '0'
84
84
  required_rubygems_version: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - '>='
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  requirements: []
90
90
  rubyforge_project:
91
- rubygems_version: 2.4.5
91
+ rubygems_version: 2.4.3
92
92
  signing_key:
93
93
  specification_version: 4
94
94
  summary: Java Speech Grammar Format