jsgf 0.4.1 → 0.5

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: 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