glaemscribe 1.1.14 → 1.3.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.
Files changed (62) hide show
  1. checksums.yaml +5 -5
  2. data/bin/glaemscribe +21 -17
  3. data/glaemresources/charsets/cirth_ds.cst +540 -0
  4. data/glaemresources/charsets/eldamar.cst +210 -0
  5. data/glaemresources/charsets/sarati_eldamar.cst +256 -0
  6. data/glaemresources/charsets/tengwar_ds_annatar.cst +2868 -0
  7. data/glaemresources/charsets/tengwar_ds_eldamar.cst +2729 -0
  8. data/glaemresources/charsets/tengwar_ds_elfica.cst +2742 -0
  9. data/glaemresources/charsets/tengwar_ds_parmaite.cst +2726 -0
  10. data/glaemresources/charsets/tengwar_ds_sindarin.cst +2722 -0
  11. data/glaemresources/charsets/tengwar_freemono.cst +217 -0
  12. data/glaemresources/charsets/tengwar_guni_annatar.cst +2948 -0
  13. data/glaemresources/charsets/tengwar_guni_eldamar.cst +2809 -0
  14. data/glaemresources/charsets/tengwar_guni_elfica.cst +2809 -0
  15. data/glaemresources/charsets/tengwar_guni_parmaite.cst +2813 -0
  16. data/glaemresources/charsets/tengwar_guni_sindarin.cst +2808 -0
  17. data/glaemresources/charsets/tengwar_telcontar.cst +225 -0
  18. data/glaemresources/charsets/unicode_gothic.cst +64 -0
  19. data/glaemresources/charsets/unicode_runes.cst +121 -0
  20. data/glaemresources/modes/{adunaic.glaem → adunaic-tengwar-glaemscrafu.glaem} +14 -2
  21. data/glaemresources/modes/{blackspeech.glaem → blackspeech-tengwar-general_use.glaem} +13 -3
  22. data/glaemresources/modes/english-cirth-espeak.glaem +687 -0
  23. data/glaemresources/modes/english-tengwar-espeak.glaem +814 -0
  24. data/glaemresources/modes/japanese-tengwar.glaem +776 -0
  25. data/glaemresources/modes/{khuzdul.glaem → khuzdul-cirth-moria.glaem} +4 -1
  26. data/glaemresources/modes/lang_belta-tengwar-dadef.glaem +248 -0
  27. data/glaemresources/modes/{futhorc.glaem → old_english-futhorc.glaem} +0 -0
  28. data/glaemresources/modes/{mercian.glaem → old_english-tengwar-mercian.glaem} +22 -12
  29. data/glaemresources/modes/{westsaxon.glaem → old_english-tengwar-westsaxon.glaem} +20 -11
  30. data/glaemresources/modes/{futhark-runicus.glaem → old_norse-futhark-runicus.glaem} +0 -0
  31. data/glaemresources/modes/{futhark-younger.glaem → old_norse-futhark-younger.glaem} +0 -0
  32. data/glaemresources/modes/{quenya.glaem → quenya-tengwar-classical.glaem} +32 -50
  33. data/glaemresources/modes/raw-cirth.glaem +154 -0
  34. data/glaemresources/modes/raw-tengwar.glaem +46 -23
  35. data/glaemresources/modes/{rlyehian.glaem → rlyehian-tengwar.glaem} +14 -3
  36. data/glaemresources/modes/{sindarin-daeron.glaem → sindarin-cirth-daeron.glaem} +55 -14
  37. data/glaemresources/modes/{sindarin-beleriand.glaem → sindarin-tengwar-beleriand.glaem} +154 -28
  38. data/glaemresources/modes/{sindarin.glaem → sindarin-tengwar-general_use.glaem} +86 -25
  39. data/glaemresources/modes/{telerin.glaem → telerin-tengwar-glaemscrafu.glaem} +16 -6
  40. data/glaemresources/modes/{westron.glaem → westron-tengwar-glaemscrafu.glaem} +18 -8
  41. data/lib/api/charset.rb +67 -7
  42. data/lib/api/charset_parser.rb +14 -1
  43. data/lib/api/constants.rb +3 -4
  44. data/lib/api/fragment.rb +26 -5
  45. data/lib/api/if_tree.rb +70 -8
  46. data/lib/api/macro.rb +40 -0
  47. data/lib/api/mode.rb +66 -19
  48. data/lib/api/mode_parser.rb +117 -14
  49. data/lib/api/object_additions.rb +23 -1
  50. data/lib/api/option.rb +17 -2
  51. data/lib/api/post_processor/outspace.rb +44 -0
  52. data/lib/api/post_processor/resolve_virtuals.rb +25 -9
  53. data/lib/api/resource_manager.rb +1 -0
  54. data/lib/api/rule_group.rb +170 -26
  55. data/lib/api/sheaf_chain_iterator.rb +1 -1
  56. data/lib/api/transcription_pre_post_processor.rb +8 -5
  57. data/lib/api/transcription_processor.rb +15 -12
  58. data/lib/api/tts.rb +51 -0
  59. data/lib/glaemscribe.rb +36 -31
  60. data/lib_espeak/espeakng.for.glaemscribe.nowasm.sync.js +35 -0
  61. data/lib_espeak/glaemscribe_tts.js +505 -0
  62. metadata +76 -24
data/lib/api/charset.rb CHANGED
@@ -30,11 +30,11 @@ module Glaemscribe
30
30
  attr_reader :virtual_chars
31
31
 
32
32
  class Char
33
- attr_accessor :line
34
- attr_accessor :code
35
- attr_accessor :names
36
- attr_accessor :str
37
- attr_accessor :charset
33
+ attr_accessor :line # Line num in the sourcecode
34
+ attr_accessor :code # Position in unicode
35
+ attr_accessor :names # Names
36
+ attr_accessor :str # How does this char resolve as a string
37
+ attr_accessor :charset # Pointer to parent charset
38
38
 
39
39
  def initialize
40
40
  @names = {}
@@ -43,9 +43,13 @@ module Glaemscribe
43
43
  def virtual?
44
44
  false
45
45
  end
46
+
47
+ def sequence?
48
+ false
49
+ end
46
50
  end
47
51
 
48
- class VirtualChar
52
+ class VirtualChar # Could have had inheritance here ...
49
53
  attr_accessor :line
50
54
  attr_accessor :names
51
55
  attr_accessor :classes
@@ -121,6 +125,45 @@ module Glaemscribe
121
125
  def virtual?
122
126
  true
123
127
  end
128
+
129
+ def sequence?
130
+ false
131
+ end
132
+ end
133
+
134
+ class SequenceChar
135
+ attr_accessor :line # Line of code
136
+ attr_accessor :names # Names
137
+ attr_accessor :sequence # The sequence of chars
138
+ attr_accessor :charset # Pointer to parent charset
139
+
140
+ def virtual?
141
+ false
142
+ end
143
+
144
+ def sequence?
145
+ true
146
+ end
147
+
148
+ def str
149
+ # A sequence char should never arrive unreplaced
150
+ VIRTUAL_CHAR_OUTPUT
151
+ end
152
+
153
+ def finalize
154
+ if @sequence.count == 0
155
+ @charset.errors << Glaeml::Error.new(@line, "Sequence for sequence char is empty.")
156
+ end
157
+
158
+ @sequence.each{ |symbol|
159
+ # Check that the sequence is correct
160
+ found = @charset[symbol]
161
+ if !found
162
+ @charset.errors << Glaeml::Error.new(@line, "Sequence char #{symbol} cannot be found in the charset.")
163
+ end
164
+ }
165
+ end
166
+
124
167
  end
125
168
 
126
169
  def initialize(name)
@@ -156,10 +199,21 @@ module Glaemscribe
156
199
  @chars << c
157
200
  end
158
201
 
202
+ def add_sequence_char(line, names, seq)
203
+ return if names.empty? || names.include?("?") # Ignore characters with '?'
204
+
205
+ c = SequenceChar.new
206
+ c.line = line
207
+ c.names = names
208
+ c.sequence = seq.split.reject{|token| token.empty? }
209
+ c.charset = self
210
+ @chars << c
211
+ end
212
+
159
213
  def finalize
160
214
  @errors = []
161
215
  @lookup_table = {}
162
- @virtual_chars = []
216
+ @virtual_chars = [] # A convenient filtered array
163
217
 
164
218
  @chars.each { |c|
165
219
  c.names.each { |cname|
@@ -179,6 +233,12 @@ module Glaemscribe
179
233
  end
180
234
  }
181
235
 
236
+ @chars.each{|c|
237
+ if c.class == SequenceChar
238
+ c.finalize
239
+ end
240
+ }
241
+
182
242
  API::Debug::log("Finalized charset '#{@name}', #{@lookup_table.count} symbols loaded.")
183
243
  end
184
244
 
@@ -47,6 +47,13 @@ module Glaemscribe
47
47
  names = char_element.args[1..-1].map{|cname| cname.strip }.reject{ |cname| cname.empty? }
48
48
  @charset.add_char(char_element.line,code,names)
49
49
  }
50
+
51
+ doc.root_node.gpath("seq").each{ |seq_elemnt|
52
+ names = seq_elemnt.args
53
+ child_node = seq_elemnt.children.first
54
+ seq = (child_node && child_node.text?)?(child_node.args.first):("")
55
+ @charset.add_sequence_char(seq_elemnt.line,names,seq)
56
+ }
50
57
 
51
58
  doc.root_node.gpath("virtual").each { |virtual_element|
52
59
  names = virtual_element.args
@@ -57,7 +64,13 @@ module Glaemscribe
57
64
  virtual_element.gpath("class").each { |class_element|
58
65
  vc = Charset::VirtualChar::VirtualClass.new
59
66
  vc.target = class_element.args[0]
60
- vc.triggers = class_element.args[1..-1].map{|cname| cname.strip }.reject{ |cname| cname.empty? }
67
+ vc.triggers = class_element.args[1..-1].map{|cname| cname.strip }.reject{ |cname| cname.empty? }
68
+
69
+ # Allow triggers to be defined inside the body of the class element
70
+ text_lines = class_element.children.select { |c| c.text? }.map{ |c| c.args.first}
71
+ inner_triggers = text_lines.join(" ").split(/\s/).select{ |e| e != '' }
72
+ vc.triggers += inner_triggers
73
+
61
74
  classes << vc
62
75
  }
63
76
  virtual_element.gpath("reversed").each { |reversed_element|
data/lib/api/constants.rb CHANGED
@@ -23,11 +23,10 @@
23
23
  module Glaemscribe
24
24
  module API
25
25
  WORD_BREAKER = "|"
26
- WORD_BOUNDARY = "_"
27
-
28
- SPECIAL_CHAR_UNDERSCORE = '➊'
29
- SPECIAL_CHAR_NBSP = '➋'
30
26
 
27
+ WORD_BOUNDARY_LANG = "_"
28
+ WORD_BOUNDARY_TREE = "\u0000"
29
+
31
30
  UNKNOWN_CHAR_OUTPUT = "☠"
32
31
  VIRTUAL_CHAR_OUTPUT = "☢" # When transcribing a virtual char...
33
32
  end
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)(i)"
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
- end
55
-
56
- class CodeLine
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
@@ -22,6 +22,23 @@
22
22
 
23
23
  module Glaemscribe
24
24
  module API
25
+
26
+ class ModeDebugContext
27
+ attr_accessor :preprocessor_output,
28
+ :processor_pathes,
29
+ :processor_output,
30
+ :postprocessor_output,
31
+ :tts_output
32
+
33
+ def initialize
34
+ @preprocessor_output = ""
35
+ @processor_pathes = []
36
+ @processor_output = []
37
+ @postprocessor_output = ""
38
+ @tts_output = ""
39
+ end
40
+ end
41
+
25
42
  class Mode
26
43
 
27
44
  attr_accessor :errors
@@ -41,6 +58,9 @@ module Glaemscribe
41
58
 
42
59
  attr_accessor :world, :invention
43
60
 
61
+ attr_accessor :has_tts
62
+ attr_reader :current_tts_voice
63
+
44
64
  attr_reader :latest_option_values
45
65
 
46
66
  def initialize(name)
@@ -50,6 +70,8 @@ module Glaemscribe
50
70
  @supported_charsets = {}
51
71
  @options = {}
52
72
  @last_raw_options = nil
73
+ @has_tts = false
74
+ @current_tts_voice = nil
53
75
 
54
76
  @pre_processor = TranscriptionPreProcessor.new(self)
55
77
  @processor = TranscriptionProcessor.new(self)
@@ -95,7 +117,7 @@ module Glaemscribe
95
117
 
96
118
  trans_options_converted = {}
97
119
 
98
- # Do a conversion to values space
120
+ # Do a conversion from names to values space
99
121
  trans_options.each{ |oname,valname|
100
122
  trans_options_converted[oname] = @options[oname].value_for_value_name(valname)
101
123
  }
@@ -117,7 +139,13 @@ module Glaemscribe
117
139
  @processor.finalize(@latest_option_values)
118
140
 
119
141
  raw_mode.finalize options if raw_mode
120
-
142
+
143
+ # Update the current espeak voice
144
+ if @has_tts
145
+ espeak_option = @options['espeak_voice'].value_name_for_value(@latest_option_values['espeak_voice'])
146
+ @current_tts_voice = TTS.option_name_to_voice(espeak_option)
147
+ end
148
+
121
149
  self
122
150
  end
123
151
 
@@ -128,16 +156,19 @@ module Glaemscribe
128
156
  @raw_mode = loaded_raw_mode.deep_clone
129
157
  end
130
158
 
131
- def replace_specials(l)
132
- l.
133
- gsub("_",SPECIAL_CHAR_UNDERSCORE).
134
- gsub("\u00a0",SPECIAL_CHAR_NBSP)
135
- end
136
-
137
- def strict_transcribe(content, charset = nil)
159
+ def strict_transcribe(content, charset, debug_context)
138
160
  charset = default_charset if !charset
139
161
  return false, "*** No charset usable for transcription. Failed!" if !charset
140
162
 
163
+ if has_tts
164
+ begin
165
+ content = TTS.ipa(content, @current_tts_voice, (raw_mode != nil) )['ipa']
166
+ debug_context.tts_output += content
167
+ rescue StandardError => e
168
+ return false, "TTS pre-transcription failed : #{e}."
169
+ end
170
+ end
171
+
141
172
  # Parser works line by line
142
173
  ret = content.lines.map{ |l|
143
174
  restore_lf = false
@@ -145,10 +176,16 @@ module Glaemscribe
145
176
  l[-1] = ""
146
177
  restore_lf = true
147
178
  end
179
+
148
180
  l = @pre_processor.apply(l)
149
- l = replace_specials(l)
150
- l = @processor.apply(l)
181
+ debug_context.preprocessor_output += l + "\n"
182
+
183
+ l = @processor.apply(l, debug_context)
184
+ debug_context.processor_output += l
185
+
151
186
  l = @post_processor.apply(l, charset)
187
+ debug_context.postprocessor_output += l + "\n"
188
+
152
189
  l += "\n" if restore_lf
153
190
  l
154
191
  }.join
@@ -156,24 +193,34 @@ module Glaemscribe
156
193
  end
157
194
 
158
195
  def transcribe(content, charset = nil)
196
+ debug_context = ModeDebugContext.new
159
197
  if raw_mode
160
198
  chunks = content.split(/({{.*?}})/m)
161
199
  ret = ''
162
200
  res = true
163
201
  chunks.each{ |c|
164
202
  if c =~ /{{(.*?)}}/m
165
- succ, r = raw_mode.strict_transcribe($1,charset)
166
- res = res && succ
167
- ret += r if succ
203
+ succ, r = raw_mode.strict_transcribe($1, charset, debug_context)
204
+
205
+ if !succ
206
+ return false, r, debug_context # Propagate error
207
+ end
208
+
209
+ ret += r
168
210
  else
169
- succ, r = strict_transcribe(c,charset)
170
- res = res && succ
171
- ret += r if succ
211
+ succ, r = strict_transcribe(c,charset,debug_context)
212
+
213
+ if !succ
214
+ return false, r, debug_context # Propagate error
215
+ end
216
+
217
+ ret += r
172
218
  end
173
219
  }
174
- return res,ret
220
+ return res, ret, debug_context
175
221
  else
176
- strict_transcribe(content,charset)
222
+ succ, r = strict_transcribe(content, charset, debug_context)
223
+ return succ, r, debug_context
177
224
  end
178
225
  end
179
226