glaemscribe 1.1.14 → 1.2.0
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 +5 -5
- data/bin/glaemscribe +19 -15
- data/glaemresources/charsets/cirth_ds.cst +205 -0
- data/glaemresources/charsets/sarati_eldamar.cst +256 -0
- data/glaemresources/charsets/tengwar_ds_annatar.cst +546 -0
- data/glaemresources/charsets/tengwar_ds_eldamar.cst +535 -0
- data/glaemresources/charsets/tengwar_ds_elfica.cst +551 -0
- data/glaemresources/charsets/tengwar_ds_parmaite.cst +534 -0
- data/glaemresources/charsets/tengwar_ds_sindarin.cst +531 -0
- data/glaemresources/charsets/tengwar_freemono.cst +217 -0
- data/glaemresources/charsets/tengwar_guni_annatar.cst +628 -0
- data/glaemresources/charsets/tengwar_guni_eldamar.cst +618 -0
- data/glaemresources/charsets/tengwar_guni_elfica.cst +620 -0
- data/glaemresources/charsets/tengwar_guni_parmaite.cst +621 -0
- data/glaemresources/charsets/tengwar_guni_sindarin.cst +617 -0
- data/glaemresources/charsets/tengwar_telcontar.cst +218 -0
- data/glaemresources/charsets/unicode_gothic.cst +64 -0
- data/glaemresources/charsets/unicode_runes.cst +121 -0
- data/glaemresources/modes/{adunaic.glaem → adunaic-tengwar-glaemscrafu.glaem} +14 -2
- data/glaemresources/modes/{blackspeech.glaem → blackspeech-tengwar-general_use.glaem} +12 -2
- data/glaemresources/modes/japanese-tengwar.glaem +771 -0
- data/glaemresources/modes/{khuzdul.glaem → khuzdul-cirth-moria.glaem} +4 -1
- data/glaemresources/modes/{futhorc.glaem → old_english-futhorc.glaem} +0 -0
- data/glaemresources/modes/{mercian.glaem → old_english-tengwar-mercian.glaem} +22 -12
- data/glaemresources/modes/{westsaxon.glaem → old_english-tengwar-westsaxon.glaem} +20 -11
- data/glaemresources/modes/{futhark-runicus.glaem → old_norse-futhark-runicus.glaem} +0 -0
- data/glaemresources/modes/{futhark-younger.glaem → old_norse-futhark-younger.glaem} +0 -0
- data/glaemresources/modes/{quenya.glaem → quenya-tengwar-classical.glaem} +32 -50
- data/glaemresources/modes/raw-tengwar.glaem +46 -23
- data/glaemresources/modes/{rlyehian.glaem → rlyehian-tengwar.glaem} +14 -3
- data/glaemresources/modes/{sindarin-daeron.glaem → sindarin-cirth-daeron.glaem} +55 -14
- data/glaemresources/modes/{sindarin-beleriand.glaem → sindarin-tengwar-beleriand.glaem} +154 -28
- data/glaemresources/modes/{sindarin.glaem → sindarin-tengwar-general_use.glaem} +86 -25
- data/glaemresources/modes/{telerin.glaem → telerin-tengwar-glaemscrafu.glaem} +16 -6
- data/glaemresources/modes/{westron.glaem → westron-tengwar-glaemscrafu.glaem} +18 -8
- data/lib/api/charset.rb +67 -7
- data/lib/api/charset_parser.rb +7 -0
- data/lib/api/constants.rb +3 -4
- data/lib/api/fragment.rb +26 -5
- data/lib/api/if_tree.rb +70 -8
- data/lib/api/macro.rb +40 -0
- data/lib/api/mode.rb +35 -13
- data/lib/api/mode_parser.rb +106 -12
- data/lib/api/object_additions.rb +23 -1
- data/lib/api/option.rb +17 -2
- data/lib/api/post_processor/resolve_virtuals.rb +25 -9
- data/lib/api/resource_manager.rb +1 -0
- data/lib/api/rule_group.rb +170 -26
- data/lib/api/sheaf_chain_iterator.rb +1 -1
- data/lib/api/transcription_processor.rb +3 -3
- data/lib/api/tts.rb +51 -0
- data/lib/glaemscribe.rb +34 -31
- data/lib_espeak/espeakng.for.glaemscribe.nowasm.sync.js +21 -0
- data/lib_espeak/glaemscribe_tts.js +365 -0
- metadata +67 -21
data/lib/api/fragment.rb
CHANGED
@@ -41,7 +41,7 @@ module Glaemscribe
|
|
41
41
|
EQUIVALENCE_RX_OUT = /(\(.*?\))/
|
42
42
|
EQUIVALENCE_RX_IN = /\((.*?)\)/
|
43
43
|
|
44
|
-
# Should pass a fragment expression, e.g. : "h(a
|
44
|
+
# Should pass a fragment expression, e.g. : "h(a,ä)(i,ï)"
|
45
45
|
def initialize(sheaf, expression)
|
46
46
|
@sheaf = sheaf
|
47
47
|
@mode = sheaf.mode
|
@@ -49,16 +49,16 @@ module Glaemscribe
|
|
49
49
|
@expression = expression
|
50
50
|
|
51
51
|
# Split the fragment, turn it into an array of arrays, e.g. [[h],[a,ä],[i,ï]]
|
52
|
-
equivalences = expression.split(EQUIVALENCE_RX_OUT).map{ |eq| eq.strip }
|
52
|
+
equivalences = expression.split(EQUIVALENCE_RX_OUT).map{ |eq| eq.strip }.reject{ |eq| eq == '' }
|
53
53
|
equivalences = equivalences.map{ |eq|
|
54
54
|
eq =~ EQUIVALENCE_RX_IN
|
55
55
|
if $1
|
56
56
|
eq = $1.split(EQUIVALENCE_SEPARATOR,-1).map{ |elt|
|
57
57
|
elt = elt.strip
|
58
|
-
elt.split(/\s/)
|
59
|
-
}
|
58
|
+
elt.split(/\s/).map{ |leaf| finalize_fragment_leaf(leaf) }
|
59
|
+
}
|
60
60
|
else
|
61
|
-
eq = [eq.split(/\s/)] # This equivalence has only one possibility
|
61
|
+
eq = [eq.split(/\s/).map{ |leaf| finalize_fragment_leaf(leaf) }] # This equivalence has only one possibility
|
62
62
|
end
|
63
63
|
}
|
64
64
|
|
@@ -87,6 +87,7 @@ module Glaemscribe
|
|
87
87
|
# Calculate all combinations for this fragment (productize the array of arrays)
|
88
88
|
res = equivalences[0]
|
89
89
|
|
90
|
+
# ((eq0 x eq1) x eq2) x eq3 ) ... )))))
|
90
91
|
(equivalences.length-1).times { |i|
|
91
92
|
prod = res.product(equivalences[i+1]).map{ |x,y| x+y}
|
92
93
|
res = prod
|
@@ -95,6 +96,26 @@ module Glaemscribe
|
|
95
96
|
@combinations = res
|
96
97
|
end
|
97
98
|
|
99
|
+
def finalize_fragment_leaf(leaf)
|
100
|
+
if src?
|
101
|
+
|
102
|
+
# Replace {UNI_XXXX} by its value to allow any unicode char to be found in the transcription tree
|
103
|
+
leaf = leaf.gsub(RuleGroup::UNICODE_VAR_NAME_REGEXP_OUT) { |cap_var|
|
104
|
+
unival = $1
|
105
|
+
new_char = [unival.hex].pack("U")
|
106
|
+
new_char = "\u0001" if new_char == '_'
|
107
|
+
new_char
|
108
|
+
}
|
109
|
+
|
110
|
+
# Replace '_' (word boundary) by '\u0000' to allow
|
111
|
+
# the real underscore to be used in the transcription tree
|
112
|
+
# (Do it after replacing the uni_xxx vars because they have underscores inside)
|
113
|
+
leaf = leaf.gsub(WORD_BOUNDARY_LANG, WORD_BOUNDARY_TREE)
|
114
|
+
leaf = leaf.gsub("\u0001","_")
|
115
|
+
end
|
116
|
+
|
117
|
+
leaf
|
118
|
+
end
|
98
119
|
|
99
120
|
def p
|
100
121
|
ret = "---- " + @expression + "\n"
|
data/lib/api/if_tree.rb
CHANGED
@@ -24,14 +24,36 @@ module Glaemscribe
|
|
24
24
|
module API
|
25
25
|
module IfTree
|
26
26
|
|
27
|
+
# A branching if condition
|
27
28
|
class IfCond
|
28
29
|
attr_accessor :line, :expression, :parent_if_term, :child_code_block
|
29
30
|
def initialize(line, parent_if_term, expression)
|
30
31
|
@parent_if_term = parent_if_term
|
31
32
|
@expression = expression
|
32
33
|
end
|
34
|
+
def offset
|
35
|
+
parent_if_term.offset + " "
|
36
|
+
end
|
37
|
+
def prefix
|
38
|
+
offset + "|-"
|
39
|
+
end
|
40
|
+
def inspect
|
41
|
+
"#{prefix} IF #{expression}\n" +
|
42
|
+
"#{child_code_block.inspect}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# A line of code
|
47
|
+
class CodeLine
|
48
|
+
attr_accessor :expression, :line
|
49
|
+
def initialize(expression, line)
|
50
|
+
@expression = expression
|
51
|
+
@line = line
|
52
|
+
end
|
33
53
|
end
|
34
54
|
|
55
|
+
# A node (code lines / preprocessor operators / ... )
|
56
|
+
# A node may have children or not depending on their nature
|
35
57
|
class Term
|
36
58
|
attr_accessor :parent_code_block
|
37
59
|
def initialize(parent_code_block)
|
@@ -43,24 +65,30 @@ module Glaemscribe
|
|
43
65
|
def is_pre_post_processor_operators?
|
44
66
|
false
|
45
67
|
end
|
68
|
+
def is_macro_deploy?
|
69
|
+
false
|
70
|
+
end
|
71
|
+
def offset
|
72
|
+
parent_code_block.offset + " "
|
73
|
+
end
|
74
|
+
def prefix
|
75
|
+
offset + "|- "
|
76
|
+
end
|
46
77
|
end
|
47
78
|
|
79
|
+
# A ifterm may have multiple ifconds (if,elsif,elsif,...,else)
|
48
80
|
class IfTerm < Term
|
49
81
|
attr_accessor :if_conds
|
50
82
|
def initialize(parent_code_block)
|
51
83
|
super(parent_code_block)
|
52
84
|
@if_conds = []
|
53
85
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
attr_accessor :expression, :line
|
58
|
-
def initialize(expression, line)
|
59
|
-
@expression = expression
|
60
|
-
@line = line
|
86
|
+
def inspect
|
87
|
+
"#{prefix} CONDITIONAL BLOCK\n" +
|
88
|
+
@if_conds.map{ |c| c.inspect }.join("\n")
|
61
89
|
end
|
62
90
|
end
|
63
|
-
|
91
|
+
|
64
92
|
class PrePostProcessorOperatorsTerm < Term
|
65
93
|
attr_accessor :operators
|
66
94
|
def initialize(parent_code_block)
|
@@ -70,6 +98,9 @@ module Glaemscribe
|
|
70
98
|
def is_pre_post_processor_operators?
|
71
99
|
true
|
72
100
|
end
|
101
|
+
def inspect
|
102
|
+
"#{prefix} OPERATORS (#{@operators.count})"
|
103
|
+
end
|
73
104
|
end
|
74
105
|
|
75
106
|
class CodeLinesTerm < Term
|
@@ -81,6 +112,25 @@ module Glaemscribe
|
|
81
112
|
def is_code_lines?
|
82
113
|
true
|
83
114
|
end
|
115
|
+
def inspect
|
116
|
+
"#{prefix} CODE LINES (#{@code_lines.count})"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class MacroDeployTerm < Term
|
121
|
+
attr_accessor :macro, :line, :arg_value_expressions
|
122
|
+
def initialize(macro, line, parent_code_block, arg_value_expressions)
|
123
|
+
super(parent_code_block)
|
124
|
+
@line = line
|
125
|
+
@macro = macro
|
126
|
+
@arg_value_expressions = arg_value_expressions
|
127
|
+
end
|
128
|
+
def is_macro_deploy?
|
129
|
+
true
|
130
|
+
end
|
131
|
+
def inspect
|
132
|
+
"#{prefix} MACRO DEPLOY (#{macro.name})"
|
133
|
+
end
|
84
134
|
end
|
85
135
|
|
86
136
|
class CodeBlock
|
@@ -89,6 +139,18 @@ module Glaemscribe
|
|
89
139
|
@parent_if_cond = parent_if_cond
|
90
140
|
@terms = []
|
91
141
|
end
|
142
|
+
def offset
|
143
|
+
((parent_if_cond)?(parent_if_cond.offset):("")) + " "
|
144
|
+
end
|
145
|
+
def prefix
|
146
|
+
offset + "|- "
|
147
|
+
end
|
148
|
+
def inspect
|
149
|
+
ret = ""
|
150
|
+
ret += "|-ROOT\n" if !parent_if_cond
|
151
|
+
ret += "#{prefix} Code block\n" +
|
152
|
+
@terms.map{|t| t.inspect}.join("\n")
|
153
|
+
end
|
92
154
|
end
|
93
155
|
|
94
156
|
end
|
data/lib/api/macro.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Glǽmscribe (also written Glaemscribe) is a software dedicated to
|
4
|
+
# the transcription of texts between writing systems, and more
|
5
|
+
# specifically dedicated to the transcription of J.R.R. Tolkien's
|
6
|
+
# invented languages to some of his devised writing systems.
|
7
|
+
#
|
8
|
+
# Copyright (C) 2015 Benjamin Babut (Talagan).
|
9
|
+
#
|
10
|
+
# This program is free software: you can redistribute it and/or modify
|
11
|
+
# it under the terms of the GNU Affero General Public License as published by
|
12
|
+
# the Free Software Foundation, either version 3 of the License, or
|
13
|
+
# any later version.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful,
|
16
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
+
# GNU Affero General Public License for more details.
|
19
|
+
#
|
20
|
+
# You should have received a copy of the GNU Affero General Public License
|
21
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
22
|
+
|
23
|
+
module Glaemscribe
|
24
|
+
module API
|
25
|
+
class Macro
|
26
|
+
attr_reader :name, :rule_group, :mode, :arg_names
|
27
|
+
|
28
|
+
attr_reader :root_code_block
|
29
|
+
|
30
|
+
def initialize(rule_group,name,arg_names)
|
31
|
+
@rule_group = rule_group
|
32
|
+
@mode = rule_group.mode
|
33
|
+
@name = name
|
34
|
+
@arg_names = arg_names
|
35
|
+
@root_code_block = IfTree::CodeBlock.new
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/api/mode.rb
CHANGED
@@ -41,8 +41,13 @@ module Glaemscribe
|
|
41
41
|
|
42
42
|
attr_accessor :world, :invention
|
43
43
|
|
44
|
+
attr_accessor :has_tts
|
45
|
+
attr_reader :current_tts_voice
|
46
|
+
|
44
47
|
attr_reader :latest_option_values
|
45
48
|
|
49
|
+
|
50
|
+
|
46
51
|
def initialize(name)
|
47
52
|
@name = name
|
48
53
|
@errors = []
|
@@ -50,6 +55,8 @@ module Glaemscribe
|
|
50
55
|
@supported_charsets = {}
|
51
56
|
@options = {}
|
52
57
|
@last_raw_options = nil
|
58
|
+
@has_tts = false
|
59
|
+
@current_tts_voice = nil
|
53
60
|
|
54
61
|
@pre_processor = TranscriptionPreProcessor.new(self)
|
55
62
|
@processor = TranscriptionProcessor.new(self)
|
@@ -95,7 +102,7 @@ module Glaemscribe
|
|
95
102
|
|
96
103
|
trans_options_converted = {}
|
97
104
|
|
98
|
-
# Do a conversion to values space
|
105
|
+
# Do a conversion from names to values space
|
99
106
|
trans_options.each{ |oname,valname|
|
100
107
|
trans_options_converted[oname] = @options[oname].value_for_value_name(valname)
|
101
108
|
}
|
@@ -117,7 +124,13 @@ module Glaemscribe
|
|
117
124
|
@processor.finalize(@latest_option_values)
|
118
125
|
|
119
126
|
raw_mode.finalize options if raw_mode
|
120
|
-
|
127
|
+
|
128
|
+
# Update the current espeak voice
|
129
|
+
if @has_tts
|
130
|
+
espeak_option = @options['espeak_voice'].value_name_for_value(@latest_option_values['espeak_voice'])
|
131
|
+
@current_tts_voice = TTS.option_name_to_voice(espeak_option)
|
132
|
+
end
|
133
|
+
|
121
134
|
self
|
122
135
|
end
|
123
136
|
|
@@ -128,16 +141,18 @@ module Glaemscribe
|
|
128
141
|
@raw_mode = loaded_raw_mode.deep_clone
|
129
142
|
end
|
130
143
|
|
131
|
-
def replace_specials(l)
|
132
|
-
l.
|
133
|
-
gsub("_",SPECIAL_CHAR_UNDERSCORE).
|
134
|
-
gsub("\u00a0",SPECIAL_CHAR_NBSP)
|
135
|
-
end
|
136
|
-
|
137
144
|
def strict_transcribe(content, charset = nil)
|
138
145
|
charset = default_charset if !charset
|
139
146
|
return false, "*** No charset usable for transcription. Failed!" if !charset
|
140
147
|
|
148
|
+
if has_tts
|
149
|
+
begin
|
150
|
+
content = TTS.ipa(content, @current_tts_voice, (raw_mode != nil) )['ipa']
|
151
|
+
rescue StandardError => e
|
152
|
+
return false, "TTS pre-transcription failed : #{e}."
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
141
156
|
# Parser works line by line
|
142
157
|
ret = content.lines.map{ |l|
|
143
158
|
restore_lf = false
|
@@ -146,7 +161,6 @@ module Glaemscribe
|
|
146
161
|
restore_lf = true
|
147
162
|
end
|
148
163
|
l = @pre_processor.apply(l)
|
149
|
-
l = replace_specials(l)
|
150
164
|
l = @processor.apply(l)
|
151
165
|
l = @post_processor.apply(l, charset)
|
152
166
|
l += "\n" if restore_lf
|
@@ -163,12 +177,20 @@ module Glaemscribe
|
|
163
177
|
chunks.each{ |c|
|
164
178
|
if c =~ /{{(.*?)}}/m
|
165
179
|
succ, r = raw_mode.strict_transcribe($1,charset)
|
166
|
-
|
167
|
-
|
180
|
+
|
181
|
+
if !succ
|
182
|
+
return false, r # Propagate error
|
183
|
+
end
|
184
|
+
|
185
|
+
ret += r
|
168
186
|
else
|
169
187
|
succ, r = strict_transcribe(c,charset)
|
170
|
-
|
171
|
-
|
188
|
+
|
189
|
+
if !succ
|
190
|
+
return false, r # Propagate error
|
191
|
+
end
|
192
|
+
|
193
|
+
ret += r
|
172
194
|
end
|
173
195
|
}
|
174
196
|
return res,ret
|
data/lib/api/mode_parser.rb
CHANGED
@@ -94,7 +94,13 @@ module Glaemscribe
|
|
94
94
|
ifcond
|
95
95
|
end
|
96
96
|
|
97
|
-
def traverse_if_tree(
|
97
|
+
def traverse_if_tree(context, text_procedure, element_procedure)
|
98
|
+
|
99
|
+
owner = context[:owner] # The root object of the if tree
|
100
|
+
root_element = context[:root_element] # The glaeml root_element of that if tree
|
101
|
+
rule_group = context[:rule_group] # The rule group in which this traversal happens (may be null for pre/post processors)
|
102
|
+
|
103
|
+
root_code_block = owner.root_code_block
|
98
104
|
current_parent_code_block = root_code_block
|
99
105
|
|
100
106
|
root_element.children.each{ |child|
|
@@ -145,7 +151,65 @@ module Glaemscribe
|
|
145
151
|
end
|
146
152
|
|
147
153
|
current_parent_code_block = if_term.parent_code_block
|
154
|
+
when 'macro'
|
155
|
+
|
156
|
+
# Macro definition, cannot be defined in conditional blocks
|
157
|
+
if current_parent_code_block.parent_if_cond || root_element.name != "rules"
|
158
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macros can only defined in the 'rules' scope, not in a conditional block (because they are replaced and used at parsing time) or a macro block (local macros are not handled).")
|
159
|
+
return
|
160
|
+
end
|
161
|
+
|
162
|
+
if !child.args || child.args.count == 0
|
163
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macro misses a name.")
|
164
|
+
return
|
165
|
+
end
|
166
|
+
|
167
|
+
macro_args = child.args.clone
|
168
|
+
macro_name = macro_args.shift
|
169
|
+
macro_args.each{ |arg|
|
170
|
+
if(!arg =~ /[0-9A-Z_]+/)
|
171
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macro argument name #{arg} has wrong format.")
|
172
|
+
return
|
173
|
+
end
|
174
|
+
}
|
175
|
+
|
176
|
+
if rule_group.macros[macro_name]
|
177
|
+
@mode.errors << Glaeml::Error.new(child.line, "Redefining macro #{macro_name}.")
|
178
|
+
return
|
179
|
+
end
|
148
180
|
|
181
|
+
macro = Macro.new(rule_group,macro_name,macro_args)
|
182
|
+
macro_context = {:owner => macro, :root_element => child, :rule_group => rule_group}
|
183
|
+
traverse_if_tree(macro_context, text_procedure, element_procedure)
|
184
|
+
|
185
|
+
rule_group.macros[macro_name] = macro
|
186
|
+
|
187
|
+
when 'deploy'
|
188
|
+
|
189
|
+
if !rule_group
|
190
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macros can only be deployed in a rule group.")
|
191
|
+
return
|
192
|
+
end
|
193
|
+
|
194
|
+
macro_args = child.args.clone
|
195
|
+
macro_name = macro_args.shift
|
196
|
+
macro = rule_group.macros[macro_name]
|
197
|
+
|
198
|
+
if !macro
|
199
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macro '#{macro_name}' not found in rule group '#{rule_group.name}'.")
|
200
|
+
return
|
201
|
+
end
|
202
|
+
|
203
|
+
wanted_argcount = macro.arg_names.count
|
204
|
+
given_argcount = macro_args.count
|
205
|
+
if wanted_argcount != given_argcount
|
206
|
+
@mode.errors << Glaeml::Error.new(child.line, "Macro '#{macro_name}' takes #{wanted_argcount} arguments, not #{given_argcount}.")
|
207
|
+
return
|
208
|
+
end
|
209
|
+
|
210
|
+
macro_node = IfTree::MacroDeployTerm.new(macro, child.line, current_parent_code_block, macro_args)
|
211
|
+
current_parent_code_block.terms << macro_node
|
212
|
+
|
149
213
|
else
|
150
214
|
# Do something with this child element
|
151
215
|
element_procedure.call(current_parent_code_block, child)
|
@@ -184,10 +248,13 @@ module Glaemscribe
|
|
184
248
|
term.operators << operator_class.new(element.clone)
|
185
249
|
end
|
186
250
|
}
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
251
|
+
|
252
|
+
processor_context = {
|
253
|
+
owner: ((pre_not_post)?(@mode.pre_processor):(@mode.post_processor)),
|
254
|
+
root_element: processor_element,
|
255
|
+
rule_group: nil
|
256
|
+
}
|
257
|
+
traverse_if_tree(processor_context, text_procedure, element_procedure )
|
191
258
|
end
|
192
259
|
|
193
260
|
def parse(file_path, mode_options = {})
|
@@ -219,6 +286,7 @@ module Glaemscribe
|
|
219
286
|
doc.root_node.gpath("options.option").each{ |option_element|
|
220
287
|
values = {}
|
221
288
|
visibility = nil
|
289
|
+
is_radio = false
|
222
290
|
|
223
291
|
option_element.gpath("value").each{ |value_element|
|
224
292
|
value_name = value_element.args.first
|
@@ -227,6 +295,8 @@ module Glaemscribe
|
|
227
295
|
option_element.gpath("visible_when").each{ |visible_element|
|
228
296
|
visibility = visible_element.args.first
|
229
297
|
}
|
298
|
+
|
299
|
+
option_element.gpath('radio').each{|e| is_radio = true}
|
230
300
|
|
231
301
|
option_name_at = option_element.args[0]
|
232
302
|
option_default_val_at = option_element.args[1]
|
@@ -236,8 +306,9 @@ module Glaemscribe
|
|
236
306
|
@mode.errors << Glaeml::Error.new(option_element.line, "Missing option default value.")
|
237
307
|
end
|
238
308
|
|
239
|
-
option
|
240
|
-
|
309
|
+
option = Option.new(@mode, option_name_at, option_default_val_at, values, option_element.line, visibility)
|
310
|
+
option.is_radio = is_radio
|
311
|
+
@mode.options[option.name] = option
|
241
312
|
}
|
242
313
|
|
243
314
|
# Read the supported font list
|
@@ -301,6 +372,7 @@ module Glaemscribe
|
|
301
372
|
|
302
373
|
lcount = element.line
|
303
374
|
element.args[0].lines.to_a.each{ |l|
|
375
|
+
# Split into lines of code and count the lines
|
304
376
|
l = l.strip
|
305
377
|
term.code_lines << IfTree::CodeLine.new(l, lcount)
|
306
378
|
lcount += 1
|
@@ -310,13 +382,35 @@ module Glaemscribe
|
|
310
382
|
element_procedure = Proc.new { |current_parent_code_block, element|
|
311
383
|
# This is fatal.
|
312
384
|
@mode.errors << Glaeml::Error.new(element.line, "Unknown directive #{element.name}.")
|
313
|
-
}
|
314
|
-
|
315
|
-
|
385
|
+
}
|
386
|
+
|
387
|
+
processor_context = {
|
388
|
+
owner: rule_group,
|
389
|
+
root_element: rules_element,
|
390
|
+
rule_group: rule_group
|
391
|
+
}
|
392
|
+
traverse_if_tree(processor_context, text_procedure, element_procedure )
|
316
393
|
}
|
317
|
-
|
318
|
-
|
394
|
+
|
395
|
+
|
396
|
+
espeak_option = @mode.options['espeak_voice']
|
397
|
+
if espeak_option
|
398
|
+
# Singleton lazy load the TTS engine
|
399
|
+
# If the mode relies on espeak
|
400
|
+
TTS::load_engine
|
401
|
+
@mode.has_tts = true
|
402
|
+
|
403
|
+
# Check if all voices are supported
|
404
|
+
espeak_option.values.keys.each { |vname|
|
405
|
+
voice = TTS::option_name_to_voice(vname)
|
406
|
+
if !(TTS::voice_list.include? voice)
|
407
|
+
@mode.errors << Glaeml::Error.new(espeak_option.line, "Option has unhandled voice #{voice}.")
|
408
|
+
end
|
409
|
+
}
|
410
|
+
end
|
319
411
|
|
412
|
+
@mode.finalize(mode_options) if !@mode.errors.any?
|
413
|
+
|
320
414
|
@mode
|
321
415
|
end
|
322
416
|
end
|