glaemscribe 1.1.14 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|