math_ml 0.14 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/math_ml/util.rb CHANGED
@@ -6,345 +6,356 @@
6
6
  # You can redistribute it and/or modify it under GPL2.
7
7
  #
8
8
 
9
- require "math_ml"
9
+ require 'math_ml'
10
10
 
11
11
  module MathML::Util
12
- ESCAPES = {"<"=>"lt",
13
- ">"=>"gt",
14
- "&"=>"amp",
15
- "\""=>"quot",
16
- "'"=>"apos"
17
- }
18
- INVALID_RE = /(?!)/
19
- EQNARRAY_RE = /\\begin\s*\{eqnarray\}(#{MathML::LaTeX::MBEC}*?)\\end\s*\{eqnarray\}/
20
- SINGLE_COMMAND_RE = /(\\([a-zA-Z]+))[ \t]?/
21
-
22
- def self.escapeXML(s, br=false)
23
- r = s.gsub(/[<>&"']/){|m| "&#{ESCAPES[m]};"}
24
- br ? r.gsub(/\n/, "<br />\n") : r
25
- end
26
-
27
- def escapeXML(s, br=false)
28
- MathML::Util.escapeXML(s, br)
29
- end
30
-
31
- def self.collect_regexp(a)
32
- if a
33
- a = [a].flatten
34
- a.size>0 ? Regexp.new(a.inject(""){|r, i| i.is_a?(Regexp) ? "#{r}#{i.to_s}|" : r}.chop) : INVALID_RE
35
- else
36
- INVALID_RE
37
- end
38
- end
39
-
40
- def collect_regexp(a)
41
- MathML::Util.collect_regexp(a)
42
- end
43
-
44
- class MathData
45
- attr_reader :math_list, :msrc_list, :dmath_list, :dsrc_list, :escape_list, :esrc_list, :user_list, :usrc_list
46
- def initialize
47
- @math_list = []
48
- @msrc_list = []
49
- @dmath_list = []
50
- @dsrc_list = []
51
- @escape_list = []
52
- @esrc_list = []
53
- @user_list = []
54
- @usrc_list = []
55
- end
56
-
57
- def update(s)
58
- @math_list.concat(s.math_list)
59
- @msrc_list.concat(s.msrc_list)
60
- @dmath_list.concat(s.dmath_list)
61
- @dsrc_list.concat(s.dsrc_list)
62
- @escape_list.concat(s.escape_list)
63
- @esrc_list.concat(s.esrc_list)
64
- @user_list.concat(s.user_list)
65
- @usrc_list.concat(s.usrc_list)
66
- end
67
- end
68
-
69
- class SimpleLaTeX
70
- include MathML::Util
71
- @@default_latex = nil
72
- DEFAULT = {
73
- :delimiter=>"\001",
74
- :math_env_list=>[
75
- /\$((?:\\.|[^\\\$])#{MathML::LaTeX::MBEC}*?)\$/m,
76
- /\\\((#{MathML::LaTeX::MBEC}*?)\\\)/m
77
- ],
78
- :dmath_env_list=>[
79
- /\$\$(#{MathML::LaTeX::MBEC}*?)\$\$/m,
80
- /\\\[(#{MathML::LaTeX::MBEC}*?)\\\]/m
81
- ],
82
- :escape_list=>[
83
- /\\(.)/m
84
- ],
85
- :through_list=>[
86
- ],
87
- :escape_any=> false,
88
- :without_parse=>false
89
- }
90
-
91
- def initialize(options = {})
92
- @params = DEFAULT.merge(options)
93
- @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser] || @params[:without_parse]
94
-
95
- @params[:math_envs] = collect_regexp(@params[:math_env_list])
96
- @params[:dmath_envs] = collect_regexp(@params[:dmath_env_list])
97
- @params[:escapes] = collect_regexp(@params[:escape_list])
98
- @params[:throughs] = collect_regexp(@params[:through_list])
99
- reset_encode_proc
100
- reset_rescue_proc
101
- reset_decode_proc
102
- reset_unencode_proc
103
- end
104
-
105
- def reset_encode_proc
106
- @encode_proc_re = INVALID_RE
107
- @encode_proc = nil
108
- end
109
-
110
- def set_encode_proc(*re, &proc)
111
- @encode_proc_re = collect_regexp(re)
112
- @encode_proc = proc
113
- end
114
-
115
- def reset_rescue_proc
116
- @rescue_proc = nil
117
- end
118
-
119
- def set_rescue_proc(&proc)
120
- @rescue_proc = proc
121
- end
122
-
123
- def reset_decode_proc
124
- @decode_proc = nil
125
- end
126
-
127
- def set_decode_proc(&proc)
128
- @decode_proc = proc
129
- end
130
-
131
- def set_unencode_proc(&proc)
132
- @unencode_proc = proc
133
- end
134
-
135
- def reset_unencode_proc
136
- @unencode_proc = nil
137
- end
138
-
139
- def encode(src, *proc_re, &proc)
140
- if proc_re.size>0 && proc_re[0].is_a?(MathData)
141
- data = proc_re.shift
142
- else
143
- data = MathData.new
144
- end
145
-
146
- proc_re = proc_re.size==0 ? @encode_proc_re : collect_regexp(proc_re)
147
- proc = @encode_proc unless proc
148
-
149
- s = StringScanner.new(src)
150
- encoded = ""
151
-
152
- until s.eos?
153
- if s.scan(/(.*?)(((((#{@params[:throughs]})|#{@params[:dmath_envs]})|#{@params[:math_envs]})|#{proc_re})|#{@params[:escapes]})/m)
154
- encoded << s[1]
155
- case
156
- when s[6]
157
- encoded << s[6]
158
- when s[5], s[4]
159
- env_src = s[5] || s[4]
160
- if @params[:dmath_envs]=~env_src
161
- encoded << "#{@params[:delimiter]}d#{data.dsrc_list.size}#{@params[:delimiter]}"
162
- data.dsrc_list << env_src
163
- else
164
- encoded << "#{@params[:delimiter]}m#{data.msrc_list.size}#{@params[:delimiter]}"
165
- data.msrc_list << env_src
166
- end
167
- when s[3]
168
- size = s[3].size
169
- s.pos = left = s.pos-size
170
- if r=proc.call(s)
171
- right = s.pos
172
- encoded << "#{@params[:delimiter]}u#{data.user_list.size}#{@params[:delimiter]}"
173
- data.user_list << r
174
- data.usrc_list << s.string[left...right]
175
- else
176
- encoded << s.peek(size)
177
- s.pos = s.pos+size
178
- end
179
- when s[2]
180
- encoded << "#{@params[:delimiter]}e#{data.escape_list.size}#{@params[:delimiter]}"
181
- @params[:escapes]=~s[2]
182
- data.esrc_list << s[2]
183
- data.escape_list << escapeXML($+, true)
184
- end
185
- else
186
- encoded << s.rest
187
- s.terminate
188
- end
189
- end
190
-
191
- parse(data, @params[:parser]) unless @params[:without_parse]
192
-
193
- return encoded, data
194
- end
195
-
196
- def error_to_html(e)
197
- "<br />\n#{escapeXML(e.message)}<br />\n<code>#{escapeXML(e.done).gsub(/\n/, "<br />\n")}<strong>#{escapeXML(e.rest).gsub(/\n/, "<br />\n")}</strong></code><br />"
198
- end
199
-
200
- def latex_parser
201
- @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser]
202
- @params[:parser]
203
- end
204
-
205
- def parse(data, parser=nil)
206
- parser = latex_parser unless parser
207
- (data.math_list.size...data.msrc_list.size).each do |i|
208
- begin
209
- @params[:math_envs]=~data.msrc_list[i]
210
- data.math_list[i] = parser.parse($+)
211
- rescue MathML::LaTeX::ParseError => e
212
- if @rescue_proc
213
- data.math_list[i] = @rescue_proc.call(e)
214
- else
215
- data.math_list[i] = error_to_html(e)
216
- end
217
- end
218
- end
219
- (data.dmath_list.size...data.dsrc_list.size).each do |i|
220
- begin
221
- @params[:dmath_envs]=~data.dsrc_list[i]
222
- data.dmath_list[i] = parser.parse($+, true)
223
- rescue MathML::LaTeX::ParseError => e
224
- if @rescue_proc
225
- data.dmath_list[i] = @rescue_proc.call(e)
226
- else
227
- data.dmath_list[i] = error_to_html(e)
228
- end
229
- end
230
- end
231
- end
232
-
233
- def decode(encoded, data, without_parsed = false, &proc)
234
- return nil if encoded==nil
235
- proc = @decode_proc unless proc
236
- encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
237
- i = $2.to_i
238
- t, d, s =
239
- case $1
240
- when "d"
241
- [:dmath, without_parsed ? escapeXML(data.dsrc_list[i], true) : data.dmath_list[i], data.dsrc_list[i]]
242
- when "e"
243
- [:escape, data.escape_list[i], data.esrc_list[i]]
244
- when "m"
245
- [:math, without_parsed ? escapeXML(data.msrc_list[i], true) : data.math_list[i], data.msrc_list[i]]
246
- when "u"
247
- [:user, data.user_list[i], data.usrc_list[i]]
248
- end
249
- if proc
250
- proc.call(d, :type=>t, :index=>i, :src=>s) || d
251
- else
252
- d
253
- end
254
- end
255
- end
256
-
257
- def decode_partial(type, encoded, data, &proc)
258
- return nil if encoded==nil
259
- head =
260
- case type
261
- when :math
262
- "m"
263
- when :dmath
264
- "d"
265
- when :escape
266
- "e"
267
- when :user
268
- "u"
269
- else
270
- return
271
- end
272
- encoded.gsub(/#{Regexp.escape(@params[:delimiter])}#{head}(\d+)#{Regexp.escape(@params[:delimiter])}/) do
273
- i = $1.to_i
274
- t, d, s =
275
- case head
276
- when "d"
277
- [:dmath, data.dmath_list[i], data.dsrc_list[i]]
278
- when "e"
279
- [:escape, data.escape_list[i], data.esrc_list[i]]
280
- when "m"
281
- [:math, data.math_list[i], data.msrc_list[i]]
282
- when "u"
283
- [:user, data.user_list[i], data.usrc_list[i]]
284
- end
285
- if proc
286
- proc.call(d, :type=>t, :index=>i, :src=>s) || "#{@params[:delimiter]}#{head}#{i}#{@params[:delimiter]}"
287
- else
288
- d
289
- end
290
- end
291
- end
292
-
293
- def unencode(encoded, data, without_escape=false, &proc)
294
- return nil if encoded==nil
295
- proc = @unencode_proc unless proc
296
- encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
297
- i = $2.to_i
298
- t, s =
299
- case $1
300
- when "d"
301
- [:dmath, data.dsrc_list[i]]
302
- when "e"
303
- [:escape, data.esrc_list[i]]
304
- when "m"
305
- [:math, data.msrc_list[i]]
306
- when "u"
307
- [:user, data.usrc_list[i]]
308
- end
309
- s = escapeXML(s, true) unless without_escape
310
- if proc
311
- proc.call(s, :type=>t, :index=>i) || s
312
- else
313
- s
314
- end
315
- end
316
- end
317
-
318
- def self.encode(src)
319
- @@default_latex = self.new unless @@default_latex
320
- @@default_latex.encode(src)
321
- end
322
-
323
- def self.decode(src, data)
324
- @@default_latex.decode(src, data)
325
- end
326
-
327
- def parse_eqnarray(src, parser=nil)
328
- src = "\\begin{array}{ccc}#{src}\\end{array}"
329
- parser = latex_parser unless parser
330
- begin
331
- parser.parse(src, true)
332
- rescue MathML::LaTeX::ParseError => e
333
- e = MathML::LaTeX::ParseError.new(e.message,
334
- e.rest.sub(/\\end\{array\}\z/, '\end{eqnarray}'),
335
- e.done.sub(/\A\\begin\{array\}\{ccc\}/, '\begin{eqnarray}'))
336
- @rescue_proc ? @rescue_proc.call(e) : error_to_html(e)
337
- end
338
- end
339
-
340
- def parse_single_command(src, parser=nil)
341
- s = src[SINGLE_COMMAND_RE, 1]
342
- parser = latex_parser unless parser
343
- begin
344
- parser.parse(s)
345
- rescue MathML::LaTeX::ParseError => e
346
- src[SINGLE_COMMAND_RE, 2]
347
- end
348
- end
349
- end
12
+ ESCAPES = { '<' => 'lt',
13
+ '>' => 'gt',
14
+ '&' => 'amp',
15
+ '"' => 'quot',
16
+ "'" => 'apos' }
17
+ INVALID_RE = /(?!)/
18
+ EQNARRAY_RE = /\\begin\s*\{eqnarray\}(#{MathML::LaTeX::MBEC}*?)\\end\s*\{eqnarray\}/
19
+ SINGLE_COMMAND_RE = /(\\([a-zA-Z]+))[ \t]?/
20
+
21
+ def self.escapeXML(s, br = false)
22
+ r = s.gsub(/[<>&"']/) { |m| "&#{ESCAPES[m]};" }
23
+ br ? r.gsub(/\n/, "<br />\n") : r
24
+ end
25
+
26
+ def escapeXML(s, br = false)
27
+ MathML::Util.escapeXML(s, br)
28
+ end
29
+
30
+ def self.collect_regexp(a)
31
+ if a
32
+ a = [a].flatten
33
+ if a.size > 0
34
+ Regexp.new(a.inject('') { |r, i| i.is_a?(Regexp) ? "#{r}#{i}|" : r }.chop)
35
+ else
36
+ INVALID_RE
37
+ end
38
+ else
39
+ INVALID_RE
40
+ end
41
+ end
42
+
43
+ def collect_regexp(a)
44
+ MathML::Util.collect_regexp(a)
45
+ end
46
+
47
+ class MathData
48
+ attr_reader :math_list, :msrc_list, :dmath_list, :dsrc_list, :escape_list, :esrc_list, :user_list, :usrc_list
49
+
50
+ def initialize
51
+ @math_list = []
52
+ @msrc_list = []
53
+ @dmath_list = []
54
+ @dsrc_list = []
55
+ @escape_list = []
56
+ @esrc_list = []
57
+ @user_list = []
58
+ @usrc_list = []
59
+ end
60
+
61
+ def update(s)
62
+ @math_list.concat(s.math_list)
63
+ @msrc_list.concat(s.msrc_list)
64
+ @dmath_list.concat(s.dmath_list)
65
+ @dsrc_list.concat(s.dsrc_list)
66
+ @escape_list.concat(s.escape_list)
67
+ @esrc_list.concat(s.esrc_list)
68
+ @user_list.concat(s.user_list)
69
+ @usrc_list.concat(s.usrc_list)
70
+ end
71
+ end
72
+
73
+ class SimpleLaTeX
74
+ include MathML::Util
75
+ @@default_latex = nil
76
+ DEFAULT = {
77
+ delimiter: "\001",
78
+ math_env_list: [
79
+ /\$((?:\\.|[^\\$])#{MathML::LaTeX::MBEC}*?)\$/m,
80
+ /\\\((#{MathML::LaTeX::MBEC}*?)\\\)/m
81
+ ],
82
+ dmath_env_list: [
83
+ /\$\$(#{MathML::LaTeX::MBEC}*?)\$\$/m,
84
+ /\\\[(#{MathML::LaTeX::MBEC}*?)\\\]/m
85
+ ],
86
+ escape_list: [
87
+ /\\(.)/m
88
+ ],
89
+ through_list: [],
90
+ escape_any: false,
91
+ without_parse: false
92
+ }
93
+
94
+ def initialize(options = {})
95
+ @params = DEFAULT.merge(options)
96
+ @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser] || @params[:without_parse]
97
+
98
+ @params[:math_envs] = collect_regexp(@params[:math_env_list])
99
+ @params[:dmath_envs] = collect_regexp(@params[:dmath_env_list])
100
+ @params[:escapes] = collect_regexp(@params[:escape_list])
101
+ @params[:throughs] = collect_regexp(@params[:through_list])
102
+ reset_encode_proc
103
+ reset_rescue_proc
104
+ reset_decode_proc
105
+ reset_unencode_proc
106
+ end
107
+
108
+ def reset_encode_proc
109
+ @encode_proc_re = INVALID_RE
110
+ @encode_proc = nil
111
+ end
112
+
113
+ def set_encode_proc(*re, &proc)
114
+ @encode_proc_re = collect_regexp(re)
115
+ @encode_proc = proc
116
+ end
117
+
118
+ def reset_rescue_proc
119
+ @rescue_proc = nil
120
+ end
121
+
122
+ def set_rescue_proc(&proc)
123
+ @rescue_proc = proc
124
+ end
125
+
126
+ def reset_decode_proc
127
+ @decode_proc = nil
128
+ end
129
+
130
+ def set_decode_proc(&proc)
131
+ @decode_proc = proc
132
+ end
133
+
134
+ def set_unencode_proc(&proc)
135
+ @unencode_proc = proc
136
+ end
137
+
138
+ def reset_unencode_proc
139
+ @unencode_proc = nil
140
+ end
141
+
142
+ def encode(src, *proc_re, &proc)
143
+ data = if proc_re.size > 0 && proc_re[0].is_a?(MathData)
144
+ proc_re.shift
145
+ else
146
+ MathData.new
147
+ end
148
+
149
+ proc_re = proc_re.size == 0 ? @encode_proc_re : collect_regexp(proc_re)
150
+ proc ||= @encode_proc
151
+
152
+ s = StringScanner.new(src)
153
+ encoded = ''
154
+
155
+ until s.eos?
156
+ if s.scan(/
157
+ (.*?)
158
+ (((((#{@params[:throughs]})|
159
+ #{@params[:dmath_envs]})|
160
+ #{@params[:math_envs]})|
161
+ #{proc_re})|
162
+ #{@params[:escapes]})
163
+ /mx)
164
+ encoded << s[1]
165
+ if s[6]
166
+ encoded << s[6]
167
+ elsif s[5] || s[4]
168
+ env_src = s[5] || s[4]
169
+ if @params[:dmath_envs] =~ env_src
170
+ encoded << "#{@params[:delimiter]}d#{data.dsrc_list.size}#{@params[:delimiter]}"
171
+ data.dsrc_list << env_src
172
+ else
173
+ encoded << "#{@params[:delimiter]}m#{data.msrc_list.size}#{@params[:delimiter]}"
174
+ data.msrc_list << env_src
175
+ end
176
+ elsif s[3]
177
+ size = s[3].size
178
+ s.pos = left = s.pos - size
179
+ if r = proc.call(s)
180
+ right = s.pos
181
+ encoded << "#{@params[:delimiter]}u#{data.user_list.size}#{@params[:delimiter]}"
182
+ data.user_list << r
183
+ data.usrc_list << s.string[left...right]
184
+ else
185
+ encoded << s.peek(size)
186
+ s.pos = s.pos + size
187
+ end
188
+ elsif s[2]
189
+ encoded << "#{@params[:delimiter]}e#{data.escape_list.size}#{@params[:delimiter]}"
190
+ @params[:escapes] =~ s[2]
191
+ data.esrc_list << s[2]
192
+ data.escape_list << escapeXML($+, true)
193
+ end
194
+ else
195
+ encoded << s.rest
196
+ s.terminate
197
+ end
198
+ end
199
+
200
+ parse(data, @params[:parser]) unless @params[:without_parse]
201
+
202
+ [encoded, data]
203
+ end
204
+
205
+ def error_to_html(e)
206
+ "<br />\n#{escapeXML(e.message)}<br />\n<code>#{escapeXML(e.done).gsub(/\n/, "<br />\n")}" \
207
+ "<strong>#{escapeXML(e.rest).gsub(/\n/, "<br />\n")}</strong></code><br />"
208
+ end
209
+
210
+ def latex_parser
211
+ @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser]
212
+ @params[:parser]
213
+ end
214
+
215
+ def parse(data, parser = nil)
216
+ parser ||= latex_parser
217
+ (data.math_list.size...data.msrc_list.size).each do |i|
218
+ @params[:math_envs] =~ data.msrc_list[i]
219
+ data.math_list[i] = parser.parse($+)
220
+ rescue MathML::LaTeX::ParseError => e
221
+ data.math_list[i] = if @rescue_proc
222
+ @rescue_proc.call(e)
223
+ else
224
+ error_to_html(e)
225
+ end
226
+ end
227
+ (data.dmath_list.size...data.dsrc_list.size).each do |i|
228
+ @params[:dmath_envs] =~ data.dsrc_list[i]
229
+ data.dmath_list[i] = parser.parse($+, true)
230
+ rescue MathML::LaTeX::ParseError => e
231
+ data.dmath_list[i] = if @rescue_proc
232
+ @rescue_proc.call(e)
233
+ else
234
+ error_to_html(e)
235
+ end
236
+ end
237
+ end
238
+
239
+ def decode(encoded, data, without_parsed = false, &proc)
240
+ return nil if encoded.nil?
241
+
242
+ proc ||= @decode_proc
243
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
244
+ i = $2.to_i
245
+ t, d, s =
246
+ case $1
247
+ when 'd'
248
+ [:dmath, without_parsed ? escapeXML(data.dsrc_list[i], true) : data.dmath_list[i], data.dsrc_list[i]]
249
+ when 'e'
250
+ [:escape, data.escape_list[i], data.esrc_list[i]]
251
+ when 'm'
252
+ [:math, without_parsed ? escapeXML(data.msrc_list[i], true) : data.math_list[i], data.msrc_list[i]]
253
+ when 'u'
254
+ [:user, data.user_list[i], data.usrc_list[i]]
255
+ end
256
+ if proc
257
+ proc.call(d, type: t, index: i, src: s) || d
258
+ else
259
+ d
260
+ end
261
+ end
262
+ end
263
+
264
+ def decode_partial(type, encoded, data, &proc)
265
+ return nil if encoded.nil?
266
+
267
+ head =
268
+ case type
269
+ when :math
270
+ 'm'
271
+ when :dmath
272
+ 'd'
273
+ when :escape
274
+ 'e'
275
+ when :user
276
+ 'u'
277
+ else
278
+ return
279
+ end
280
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}#{head}(\d+)#{Regexp.escape(@params[:delimiter])}/) do
281
+ i = $1.to_i
282
+ t, d, s =
283
+ case head
284
+ when 'd'
285
+ [:dmath, data.dmath_list[i], data.dsrc_list[i]]
286
+ when 'e'
287
+ [:escape, data.escape_list[i], data.esrc_list[i]]
288
+ when 'm'
289
+ [:math, data.math_list[i], data.msrc_list[i]]
290
+ when 'u'
291
+ [:user, data.user_list[i], data.usrc_list[i]]
292
+ end
293
+ if proc
294
+ proc.call(d, type: t, index: i, src: s) || "#{@params[:delimiter]}#{head}#{i}#{@params[:delimiter]}"
295
+ else
296
+ d
297
+ end
298
+ end
299
+ end
300
+
301
+ def unencode(encoded, data, without_escape = false, &proc)
302
+ return nil if encoded.nil?
303
+
304
+ proc ||= @unencode_proc
305
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
306
+ i = $2.to_i
307
+ t, s =
308
+ case $1
309
+ when 'd'
310
+ [:dmath, data.dsrc_list[i]]
311
+ when 'e'
312
+ [:escape, data.esrc_list[i]]
313
+ when 'm'
314
+ [:math, data.msrc_list[i]]
315
+ when 'u'
316
+ [:user, data.usrc_list[i]]
317
+ end
318
+ s = escapeXML(s, true) unless without_escape
319
+ if proc
320
+ proc.call(s, type: t, index: i) || s
321
+ else
322
+ s
323
+ end
324
+ end
325
+ end
326
+
327
+ def self.encode(src)
328
+ @@default_latex ||= new
329
+ @@default_latex.encode(src)
330
+ end
331
+
332
+ def self.decode(src, data)
333
+ @@default_latex.decode(src, data)
334
+ end
335
+
336
+ def parse_eqnarray(src, parser = nil)
337
+ src = "\\begin{array}{ccc}#{src}\\end{array}"
338
+ parser ||= latex_parser
339
+ begin
340
+ parser.parse(src, true)
341
+ rescue MathML::LaTeX::ParseError => e
342
+ e = MathML::LaTeX::ParseError.new(
343
+ e.message,
344
+ e.rest.sub(/\\end\{array\}\z/, '\end{eqnarray}'),
345
+ e.done.sub(/\A\\begin\{array\}\{ccc\}/, '\begin{eqnarray}')
346
+ )
347
+ @rescue_proc ? @rescue_proc.call(e) : error_to_html(e)
348
+ end
349
+ end
350
+
351
+ def parse_single_command(src, parser = nil)
352
+ s = src[SINGLE_COMMAND_RE, 1]
353
+ parser ||= latex_parser
354
+ begin
355
+ parser.parse(s)
356
+ rescue MathML::LaTeX::ParseError => e
357
+ src[SINGLE_COMMAND_RE, 2]
358
+ end
359
+ end
360
+ end
350
361
  end