math_ml 0.9

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.
@@ -0,0 +1,479 @@
1
+ require "eim_xml/parser"
2
+ require "eim_xml/dsl"
3
+ require "math_ml"
4
+ require "spec/util"
5
+
6
+ describe MathML::LaTeX::Parser do
7
+ include MathML::Spec::Util
8
+
9
+ def check_chr(tag, src)
10
+ src.scan(/./) do |c|
11
+ smml(c).should == "<#{tag}>#{c}</#{tag}>"
12
+ end
13
+ end
14
+
15
+ def check_hash(tag, hash)
16
+ hash.each do |k, v|
17
+ smml(k).should == "<#{tag}>#{v}</#{tag}>"
18
+ end
19
+ end
20
+
21
+ def check_entity(tag, hash)
22
+ check_hash(tag, hash.inject({}){|r, i| r[i[0]]="&#{i[1]};"; r})
23
+ end
24
+
25
+ it "Spec#strip_math_ml" do
26
+ src = "<math test='dummy'> <a> b </a> <c> d </c></math>"
27
+ strip_math_ml(src).should == "<a>b</a><c>d</c>"
28
+ end
29
+
30
+ describe "#parse" do
31
+ it "should return math element" do
32
+ ns = "http://www.w3.org/1998/Math/MathML"
33
+
34
+ e = new_parser.parse("")
35
+ e.should match(EimXML::Element.new(:math, :display=>"inline", "xmlns"=>ns))
36
+ e.attributes.keys.size.should == 2
37
+ e.contents.should be_empty
38
+
39
+ e = new_parser.parse("", true)
40
+ e.should match(EimXML::Element.new(:math, :display=>"block", "xmlns"=>ns))
41
+ e.attributes.keys.size.should == 2
42
+ e.contents.should be_empty
43
+
44
+ e = new_parser.parse("", false)
45
+ e.should match(EimXML::Element.new(:math, :display=>"inline", "xmlns"=>ns))
46
+ e.attributes.keys.size.should == 2
47
+ e.contents.should be_empty
48
+ end
49
+
50
+ it "should ignore space" do
51
+ smml("{ a }").should == "<mrow><mi>a</mi></mrow>"
52
+ end
53
+
54
+ it "should process latex block" do
55
+ lambda{smml("test {test} {test")}.should raise_parse_error("Block not closed.", "test {test} ", "{test")
56
+ end
57
+
58
+ it "should raise error when error happened" do
59
+ src = 'a\hoge c'
60
+ lambda{smml(src)}.should raise_parse_error("Undefined command.", "a", '\hoge c')
61
+
62
+ src = '\sqrt\sqrt1'
63
+ lambda{smml(src)}.should raise_parse_error("Syntax error.", '\sqrt\sqrt', "1")
64
+
65
+ src = "a{b"
66
+ lambda{smml(src)}.should raise_parse_error("Block not closed.", "a", "{b")
67
+ end
68
+
69
+ it "should process numerics" do
70
+ smml('1234567890').should == "<mn>1234567890</mn>"
71
+ smml('1.2').should == "<mn>1.2</mn>"
72
+ smml('1.').should == "<mn>1</mn><mo>.</mo>"
73
+ smml('.2').should == "<mn>.2</mn>"
74
+ smml('1.2.3').should == "<mn>1.2</mn><mn>.3</mn>"
75
+ end
76
+
77
+ it "should process alphabets" do
78
+ smml("abc").should == "<mi>a</mi><mi>b</mi><mi>c</mi>"
79
+ check_chr("mi", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
80
+ end
81
+
82
+ it "should process non alphabet command" do
83
+ smml('\|').should == "<mo>&DoubleVerticalBar;</mo>"
84
+ end
85
+
86
+ it "should process space commands" do
87
+ smml('\ ').should == "<mspace width='1em' />"
88
+ smml('\quad').should == "<mspace width='1em' />"
89
+ smml('\qquad').should == "<mspace width='2em' />"
90
+ smml('\,').should == "<mspace width='0.167em' />"
91
+ smml('\:').should == "<mspace width='0.222em' />"
92
+ smml('\;').should == "<mspace width='0.278em' />"
93
+ smml('\!').should == "<mspace width='-0.167em' />"
94
+ smml('~').should == "<mspace width='1em' />"
95
+ end
96
+
97
+ it "should process operators" do
98
+ check_chr("mo", ",.+-*=/()[]|;:!")
99
+ check_entity("mo", {"<"=>"lt", ">"=>"gt", '"'=>"quot"})
100
+ check_hash("mo", {'\backslash'=>'\\', '\%'=>'%', '\{'=>'{', '\}'=>'}', '\$'=>'$', '\#'=>'#'})
101
+ end
102
+
103
+ it "should process prime" do
104
+ smml("a'").should == "<msup><mi>a</mi><mo>&prime;</mo></msup>"
105
+ smml("a''").should == "<msup><mi>a</mi><mo>&prime;&prime;</mo></msup>"
106
+ smml("a'''").should == "<msup><mi>a</mi><mo>&prime;&prime;&prime;</mo></msup>"
107
+ smml("'").should == "<msup><none /><mo>&prime;</mo></msup>"
108
+
109
+ lambda{smml("a^b'")}.should raise_parse_error("Double superscript.", "a^b", "'")
110
+
111
+ smml("a'^b").should == "<msup><mi>a</mi><mrow><mo>&prime;</mo><mi>b</mi></mrow></msup>"
112
+ smml("a'''^b").should == "<msup><mi>a</mi><mrow><mo>&prime;&prime;&prime;</mo><mi>b</mi></mrow></msup>"
113
+ smml("a'b").should == "<msup><mi>a</mi><mo>&prime;</mo></msup><mi>b</mi>"
114
+ end
115
+
116
+ it "should process sqrt" do
117
+ smml('\sqrt a').should == "<msqrt><mi>a</mi></msqrt>"
118
+ smml('\sqrt[2]3').should == "<mroot><mn>3</mn><mn>2</mn></mroot>"
119
+ smml('\sqrt[2a]3').should == "<mroot><mn>3</mn><mrow><mn>2</mn><mi>a</mi></mrow></mroot>"
120
+ lambda{smml('\sqrt[12')}.should raise_parse_error("Option not closed.", '\sqrt', "[12")
121
+ end
122
+
123
+ it "should process subsup" do
124
+ smml("a_b^c").should == "<msubsup><mi>a</mi><mi>b</mi><mi>c</mi></msubsup>"
125
+ smml("a_b").should == "<msub><mi>a</mi><mi>b</mi></msub>"
126
+ smml("a^b").should == "<msup><mi>a</mi><mi>b</mi></msup>"
127
+ smml("_a^b").should == "<msubsup><none /><mi>a</mi><mi>b</mi></msubsup>"
128
+
129
+ lambda{smml("a_b_c")}.should raise_parse_error("Double subscript.", "a_b", "_c")
130
+ lambda{smml("a^b^c")}.should raise_parse_error("Double superscript.", "a^b", "^c")
131
+ lambda{smml("a_")}.should raise_parse_error("Subscript not exist.", "a_", "")
132
+ lambda{smml("a^")}.should raise_parse_error("Superscript not exist.", "a^", "")
133
+ end
134
+
135
+ it "should process underover" do
136
+ smml('\sum_a^b', true).should == "<munderover><mo>&sum;</mo><mi>a</mi><mi>b</mi></munderover>"
137
+ smml('\sum_a^b').should == "<msubsup><mo>&sum;</mo><mi>a</mi><mi>b</mi></msubsup>"
138
+ smml('\sum_a', true).should == "<munder><mo>&sum;</mo><mi>a</mi></munder>"
139
+ smml('\sum^a', true).should == "<mover><mo>&sum;</mo><mi>a</mi></mover>"
140
+ smml('\sum_a').should == "<msub><mo>&sum;</mo><mi>a</mi></msub>"
141
+ smml('\sum^a').should == "<msup><mo>&sum;</mo><mi>a</mi></msup>"
142
+
143
+ lambda{smml('\sum_b_c')}.should raise_parse_error("Double subscript.", '\sum_b', "_c")
144
+ lambda{smml('\sum^b^c')}.should raise_parse_error("Double superscript.", '\sum^b', "^c")
145
+ lambda{smml('\sum_')}.should raise_parse_error("Subscript not exist.", '\sum_', "")
146
+ lambda{smml('\sum^')}.should raise_parse_error("Superscript not exist.", '\sum^', "")
147
+ end
148
+
149
+ it "should process font commands" do
150
+ smml('a{\bf b c}d').should == "<mi>a</mi><mrow><mi mathvariant='bold'>b</mi><mi mathvariant='bold'>c</mi></mrow><mi>d</mi>"
151
+ smml('\bf a{\it b c}d').should == "<mi mathvariant='bold'>a</mi><mrow><mi>b</mi><mi>c</mi></mrow><mi mathvariant='bold'>d</mi>"
152
+ smml('a{\rm b c}d').should == "<mi>a</mi><mrow><mi mathvariant='normal'>b</mi><mi mathvariant='normal'>c</mi></mrow><mi>d</mi>"
153
+
154
+ smml('a \mathbf{bc}d').should == "<mi>a</mi><mrow><mrow><mi mathvariant='bold'>b</mi><mi mathvariant='bold'>c</mi></mrow></mrow><mi>d</mi>"
155
+ smml('\mathbf12').should == "<mrow><mn mathvariant='bold'>1</mn></mrow><mn>2</mn>"
156
+ smml('\bf a \mathit{bc} d').should == "<mi mathvariant='bold'>a</mi><mrow><mrow><mi>b</mi><mi>c</mi></mrow></mrow><mi mathvariant='bold'>d</mi>"
157
+ smml('a\mathrm{bc}d').should == "<mi>a</mi><mrow><mrow><mi mathvariant='normal'>b</mi><mi mathvariant='normal'>c</mi></mrow></mrow><mi>d</mi>"
158
+
159
+ smml('a \mathbb{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bopf;</mi><mi>&copf;</mi></mrow></mrow><mi>d</mi>"
160
+ smml('a \mathscr{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bscr;</mi><mi>&cscr;</mi></mrow></mrow><mi>d</mi>"
161
+ smml('a \mathfrak{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bfr;</mi><mi>&cfr;</mi></mrow></mrow><mi>d</mi>"
162
+ smml('a \bm{bc}d').should == "<mi>a</mi><mrow><mrow><mi mathvariant='bold-italic'>b</mi><mi mathvariant='bold-italic'>c</mi></mrow></mrow><mi>d</mi>"
163
+ smml('\bm ab').should == "<mrow><mi mathvariant='bold-italic'>a</mi></mrow><mi>b</mi>"
164
+
165
+ lambda{smml('\mathit')}.should raise_parse_error("Syntax error.", '\mathit', "")
166
+ lambda{smml('\mathrm')}.should raise_parse_error("Syntax error.", '\mathrm', "")
167
+ lambda{smml('\mathbf')}.should raise_parse_error("Syntax error.", '\mathbf', "")
168
+ lambda{smml('\mathbb')}.should raise_parse_error("Syntax error.", '\mathbb', "")
169
+ lambda{smml('\mathscr')}.should raise_parse_error("Syntax error.", '\mathscr', "")
170
+ lambda{smml('\mathfrak')}.should raise_parse_error("Syntax error.", '\mathfrak', "")
171
+ end
172
+
173
+ it "should process mbox" do
174
+ smml('a\mbox{b c}d').should == "<mi>a</mi><mtext>b c</mtext><mi>d</mi>"
175
+ smml('\mbox{<>\'"&}').should == '<mtext>&lt;&gt;&apos;&quot;&amp;</mtext>'
176
+ end
177
+
178
+ it "should process frac" do
179
+ smml('\frac ab').should == "<mfrac><mi>a</mi><mi>b</mi></mfrac>"
180
+ smml('\frac12').should == "<mfrac><mn>1</mn><mn>2</mn></mfrac>"
181
+
182
+ lambda{smml('\frac a')}.should raise_parse_error("Syntax error.", '\frac a', "")
183
+ end
184
+
185
+ it "should process environment" do
186
+ lambda{smml('{\begin}rest')}.should raise_parse_error("Environment name not exist.", '{\begin', '}rest')
187
+
188
+ lambda{smml('{\begin{array}{c}dummy}rest')}.should raise_parse_error('Matching \end not exist.', '{\begin{array}{c}dummy', '}rest')
189
+
190
+ lambda{smml('\begin{array}c dummy\end{test}')}.should raise_parse_error("Environment mismatched.", '\begin{array}c dummy\end', "{test}")
191
+
192
+ lambda{smml('\left(\begin{array}\right)')}.should raise_parse_error("Syntax error.", '\left(\begin{array}', '\right)')
193
+ end
194
+
195
+ it "should process array" do
196
+ smml('\begin{array}{lrc} a & b & c \\\\ d & e & f \\\\ \end{array}').should == "<mtable columnalign='left right center'><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd><mtd><mi>c</mi></mtd></mtr><mtr><mtd><mi>d</mi></mtd><mtd><mi>e</mi></mtd><mtd><mi>f</mi></mtd></mtr></mtable>"
197
+
198
+ smml('\begin{array}{lrc}a&b&c\\\\d&e&f \end{array}').should == "<mtable columnalign='left right center'><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd><mtd><mi>c</mi></mtd></mtr><mtr><mtd><mi>d</mi></mtd><mtd><mi>e</mi></mtd><mtd><mi>f</mi></mtd></mtr></mtable>"
199
+
200
+ smml('\begin{array}{c}\end{array}').should == "<mtable />"
201
+
202
+ lambda{smml('\begin{array}\end{array}')}.should raise_parse_error('Syntax error.', '\begin{array}', '\end{array}')
203
+
204
+ lambda{smml('\begin{array}{a}\end{array}')}.should raise_parse_error("Syntax error.", '\begin{array}{', 'a}\end{array}')
205
+
206
+ lambda{smml('\begin{array}{cc}a\\\\b&c\end{array}')}.should raise_parse_error("Need more column.", '\begin{array}{cc}a', '\\\\b&c\end{array}')
207
+
208
+ lambda{smml('\begin{array}{cc}a\end{array}')}.should raise_parse_error("Need more column.", '\begin{array}{cc}a', '\end{array}')
209
+
210
+ lambda{smml('\begin{array}{c}a&\end{array}')}.should raise_parse_error("Too many column.", '\begin{array}{c}a', '&\end{array}')
211
+
212
+ smml('\begin{array}{cc}&\end{array}').should == "<mtable><mtr><mtd /><mtd /></mtr></mtable>"
213
+
214
+ math_ml('\left\{\begin{array}ca_b\end{array}\right\}')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
215
+ element :mrow do
216
+ element :mtable do
217
+ element :mtr do
218
+ element :mtd do
219
+ element :msub do
220
+ element(:mi).add("a")
221
+ element(:mi).add("b")
222
+ end
223
+ end
224
+ end
225
+ end
226
+ end
227
+ }
228
+
229
+ smml('\begin{array}{@{a_1}l@bc@cr@d}A&B&C\end{array}').should == "<mtable columnalign='center left center center center right center'><mtr><mtd><mrow><msub><mi>a</mi><mn>1</mn></msub></mrow></mtd><mtd><mi>A</mi></mtd><mtd><mi>b</mi></mtd><mtd><mi>B</mi></mtd><mtd><mi>c</mi></mtd><mtd><mi>C</mi></mtd><mtd><mi>d</mi></mtd></mtr></mtable>"
230
+
231
+ math_ml('\left\{\begin{array}ca_b\end{array}\right\}')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
232
+ element :mrow do
233
+ element :mtable do
234
+ element :mtr do
235
+ element :mtd do
236
+ element :msub do
237
+ element(:mi).add("a")
238
+ element(:mi).add("b")
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ }
245
+
246
+ smml('\begin{array}{c|c}a&b\\\\c&d\end{array}').should == "<mtable columnlines='solid'><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd></mtr><mtr><mtd><mi>c</mi></mtd><mtd><mi>d</mi></mtd></mtr></mtable>"
247
+ smml('\begin{array}{|c|}a\\\\c\end{array}').should == "<mtable columnlines='solid solid'><mtr><mtd /><mtd><mi>a</mi></mtd><mtd /></mtr><mtr><mtd /><mtd><mi>c</mi></mtd><mtd /></mtr></mtable>"
248
+ smml('\begin{array}{c}\hline c\end{array}').should == "<mtable rowlines='solid'><mtr /><mtr><mtd><mi>c</mi></mtd></mtr></mtable>"
249
+ smml('\begin{array}{c@acc}c&c&c\\\\\hline\end{array}').should == "<mtable rowlines='solid'><mtr><mtd><mi>c</mi></mtd><mtd><mi>a</mi></mtd><mtd><mi>c</mi></mtd><mtd><mi>c</mi></mtd></mtr><mtr><mtd /><mtd /><mtd /><mtd /></mtr></mtable>"
250
+ smml('\begin{array}{c}\hline a\\\\b\\\\\hline\end{array}').should == "<mtable rowlines='solid none solid'><mtr /><mtr><mtd><mi>a</mi></mtd></mtr><mtr><mtd><mi>b</mi></mtd></mtr><mtr><mtd /></mtr></mtable>"
251
+ end
252
+
253
+ it "should parse \\left and \\right" do
254
+ math_ml('\left(\frac12\right)')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"(", :close=>")"){
255
+ element :mrow do
256
+ element :mfrac do
257
+ element(:mn).add("1")
258
+ element(:mn).add("2")
259
+ end
260
+ end
261
+ }
262
+
263
+ math_ml('\left \lfloor a\right \rfloor')[0].should =~ EimXML::DSL.element(:mfenced, :open=>EimXML::PCString.new("&lfloor;", true), :close=>EimXML::PCString.new("&rfloor;", true)){
264
+ element :mrow do
265
+ element(:mi).add("a")
266
+ end
267
+ }
268
+
269
+ math_ml('\left \{ a \right \}')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
270
+ element :mrow do
271
+ element(:mi).add("a")
272
+ end
273
+ }
274
+
275
+ math_ml('\left\{\begin{array}c\begin{array}ca\end{array}\end{array}\right\}')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
276
+ element :mrow do
277
+ element :mtable do
278
+ element :mtr do
279
+ element :mtd do
280
+ element :mtable do
281
+ element :mtr do
282
+ element :mtd do
283
+ element(:mi).add("a")
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
291
+ }
292
+
293
+ math_ml('\left(\sum_a\right)')[0].should =~ EimXML::DSL.element(:mfenced, :open=>"(", :close=>")"){
294
+ element :mrow do
295
+ element :msub do
296
+ element(:mo).add(EimXML::PCString.new("&sum;", true))
297
+ element(:mi).add("a")
298
+ end
299
+ end
300
+ }
301
+
302
+ math_ml('\left(\sum_a\right)', true)[0].should =~ EimXML::DSL.element(:mfenced, :open=>"(", :close=>")"){
303
+ element :mrow do
304
+ element :munder do
305
+ element(:mo).add(EimXML::PCString.new("&sum;", true))
306
+ element(:mi).add("a")
307
+ end
308
+ end
309
+ }
310
+
311
+ lambda{smml('\left(test')}.should raise_parse_error("Brace not closed.", '\left', '(test')
312
+
313
+ math_ml('\left\|a\right\|')[0].should =~ EimXML::DSL.element(:mfenced, :open=>EimXML::PCString.new("&DoubleVerticalBar;", true), :close=>EimXML::PCString.new("&DoubleVerticalBar;", true)){
314
+ element :mrow do
315
+ element(:mi).add("a")
316
+ end
317
+ }
318
+
319
+ lambda{smml('\left')}.should raise_parse_error("Need brace here.", '\left', "")
320
+ end
321
+
322
+ it "should parse overs" do
323
+ smml('\hat a').should == "<mover><mi>a</mi><mo>&circ;</mo></mover>"
324
+ smml('\hat12').should == "<mover><mn>1</mn><mo>&circ;</mo></mover><mn>2</mn>"
325
+ lambda{smml('{\hat}a')}.should raise_parse_error("Syntax error.", '{\hat', '}a')
326
+ end
327
+
328
+ it "should parse unders" do
329
+ smml('\underline a').should == "<munder><mi>a</mi><mo>&macr;</mo></munder>"
330
+ smml('\underline12').should == "<munder><mn>1</mn><mo>&macr;</mo></munder><mn>2</mn>"
331
+ lambda{smml('{\underline}a')}.should raise_parse_error("Syntax error.", '{\underline', '}a')
332
+ end
333
+
334
+ it "should parse stackrel" do
335
+ smml('\stackrel\to=').should == "<mover><mo>=</mo><mo>&rightarrow;</mo></mover>"
336
+ smml('\stackrel12').should == "<mover><mn>2</mn><mn>1</mn></mover>"
337
+ end
338
+
339
+ it "should parse comment" do
340
+ smml('a%b').should == "<mi>a</mi>"
341
+ end
342
+
343
+ it "should parse entity" do
344
+ p = new_parser
345
+ lambda{smml('\entity{therefore}', false, p)}.should raise_parse_error("Unregistered entity.", '\entity{', "therefore}")
346
+
347
+ p.unsecure_entity = true
348
+ smml('\entity{therefore}', false, p).should == "<mo>&therefore;</mo>"
349
+
350
+ p.unsecure_entity = false
351
+ lambda{smml('\entity{therefore}', false, p)}.should raise_parse_error("Unregistered entity.", '\entity{', "therefore}")
352
+
353
+ p.add_entity(['therefore'])
354
+ smml('\entity{therefore}', false, p).should == "<mo>&therefore;</mo>"
355
+ end
356
+
357
+ it "should parse backslash" do
358
+ smml('\\\\').should == "<br xmlns='http://www.w3.org/1999/xhtml' />"
359
+ end
360
+
361
+ it "can be used with macro" do
362
+ macro = <<'EOS'
363
+ \newcommand{\root}[2]{\sqrt[#1]{#2}}
364
+ \newcommand{\ROOT}[2]{\sqrt[#1]#2}
365
+ \newenvironment{braced}[2]{\left#1}{\right#2}
366
+ \newenvironment{sq}[2]{\sqrt[#2]{#1}}{\sqrt#2}
367
+ \newcommand{\R}{\mathbb R}
368
+ \newenvironment{BB}{\mathbb A}{\mathbb B}
369
+ EOS
370
+ p = new_parser
371
+ p.macro.parse(macro)
372
+
373
+ smml('\root12', false, p).should == "<mroot><mrow><mn>2</mn></mrow><mn>1</mn></mroot>"
374
+ smml('\root{12}{34}', false, p).should == "<mroot><mrow><mn>34</mn></mrow><mn>12</mn></mroot>"
375
+ smml('\ROOT{12}{34}', false, p).should == "<mroot><mn>3</mn><mn>12</mn></mroot><mn>4</mn>"
376
+ lambda{smml('\root', false, p)}.should raise_parse_error('Error in macro(Need more parameter. "").', '', '\root')
377
+
378
+
379
+ math_ml('\begin{braced}{|}{)}\frac12\end{braced}', false, p)[0].should =~ EimXML::DSL.element(:mfenced, :open=>"|", :close=>")"){
380
+ element(:mrow) do
381
+ element(:mfrac) do
382
+ element(:mn).add("1")
383
+ element(:mn).add("2")
384
+ end
385
+ end
386
+ }
387
+
388
+ smml('\begin{sq}{12}{34}a\end{sq}', false, p).should == "<mroot><mrow><mn>12</mn></mrow><mn>34</mn></mroot><mi>a</mi><msqrt><mn>3</mn></msqrt><mn>4</mn>"
389
+ lambda{smml('\begin{braced}', false, p)}.should raise_parse_error("Need more parameter.", '\begin{braced}', "")
390
+ lambda{smml('\begin{braced}123', false, p)}.should raise_parse_error('Matching \end not exist.', '\begin{braced}', "123")
391
+ lambda{smml('\begin{braced}123\end{brace}', false, p)}.should raise_parse_error("Environment mismatched.", '\begin{braced}123\end', '{brace}')
392
+ smml('\R', false, p).should == "<mrow><mi>&Ropf;</mi></mrow>"
393
+ smml('\begin{BB}\end{BB}', false, p).should == "<mrow><mi>&Aopf;</mi></mrow><mrow><mi>&Bopf;</mi></mrow>"
394
+ end
395
+
396
+ it "should raise error when macro define circular reference" do
397
+ macro = <<'EOT'
398
+ \newcommand{\C}{\C}
399
+ \newenvironment{E}{\begin{E}}{\end{E}}
400
+ \newcommand{\D}{\begin{F}\end{F}}
401
+ \newenvironment{F}{\D}{}
402
+ EOT
403
+ ps = new_parser
404
+ ps.macro.parse(macro)
405
+
406
+ lambda{smml('\C', false, ps)}.should raise_parse_error("Circular reference.", "", '\C')
407
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error("Circular reference.", "", '\begin{E}\end{E}')
408
+ lambda{smml('\D', false, ps)}.should raise_parse_error("Circular reference.", "", '\D')
409
+ lambda{smml('\begin{F}\end{F}', false, ps)}.should raise_parse_error("Circular reference.", "", '\begin{F}\end{F}')
410
+ end
411
+
412
+ it "should raise error when macro uses undefined command" do
413
+ macro = <<'EOT'
414
+ \newcommand{\C}{\dummy}
415
+ \newenvironment{E}{\dummy}{}
416
+ EOT
417
+ ps = new_parser
418
+ ps.macro.parse(macro)
419
+
420
+ lambda{smml('\C', false, ps)}.should raise_parse_error('Error in macro(Undefined command. "\dummy").', "", '\C')
421
+ lambda{smml('\C', false, ps)}.should raise_parse_error('Error in macro(Undefined command. "\dummy").', "", '\C')
422
+
423
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error('Error in macro(Undefined command. "\dummy").', '', '\begin{E}\end{E}')
424
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error('Error in macro(Undefined command. "\dummy").', "", '\begin{E}\end{E}')
425
+ end
426
+
427
+ it "can be used with macro with option" do
428
+ macro = <<'EOS'
429
+ \newcommand{\opt}[1][x]{#1}
430
+ \newcommand{\optparam}[2][]{#1#2}
431
+ \newenvironment{newenv}[1][x]{#1}{#1}
432
+ \newenvironment{optenv}[2][]{#1}{#2}
433
+ EOS
434
+
435
+ p = new_parser
436
+ p.macro.parse(macro)
437
+
438
+ smml('\opt a', false, p).should == "<mi>x</mi><mi>a</mi>"
439
+ smml('\opt[0] a', false, p).should == "<mn>0</mn><mi>a</mi>"
440
+ smml('\optparam a', false, p).should == "<mi>a</mi>"
441
+ smml('\optparam[0] a', false, p).should == "<mn>0</mn><mi>a</mi>"
442
+
443
+ smml('\begin{newenv}a\end{newenv}', false, p).should == "<mi>x</mi><mi>a</mi><mi>x</mi>"
444
+ smml('\begin{newenv}[0]a\end{newenv}', false, p).should == "<mn>0</mn><mi>a</mi><mn>0</mn>"
445
+ smml('\begin{optenv}0a\end{optenv}', false, p).should == "<mi>a</mi><mn>0</mn>"
446
+ smml('\begin{optenv}[0]1a\end{optenv}', false, p).should == "<mn>0</mn><mi>a</mi><mn>1</mn>"
447
+ end
448
+
449
+ it "should parse matrix environment" do
450
+ smml('\begin{matrix}&&\\\\&\end{matrix}').should == "<mtable><mtr><mtd /><mtd /><mtd /></mtr><mtr><mtd /><mtd /></mtr></mtable>"
451
+ lambda{smml('\begin{matrix}&&\\\\&\end{mat}')}.should raise_parse_error("Environment mismatched.", '\begin{matrix}&&\\\\&\end', "{mat}")
452
+ lambda{smml('\begin{matrix}&&\\\\&')}.should raise_parse_error("Matching \\end not exist.", '\begin{matrix}&&\\\\&', '')
453
+ smml('\begin{matrix}\begin{matrix}a&b\\\\c&d\end{matrix}&1\\\\0&1\\\\\end{matrix}').should == "<mtable><mtr><mtd><mtable><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd></mtr><mtr><mtd><mi>c</mi></mtd><mtd><mi>d</mi></mtd></mtr></mtable></mtd><mtd><mn>1</mn></mtd></mtr><mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr></mtable>"
454
+ smml('\begin{matrix}\end{matrix}').should == "<mtable />"
455
+ smml('\begin{matrix}\hline a\\\\b\\\\\hline\end{matrix}').should == "<mtable rowlines='solid none solid'><mtr /><mtr><mtd><mi>a</mi></mtd></mtr><mtr><mtd><mi>b</mi></mtd></mtr><mtr /></mtable>"
456
+
457
+ smml('\begin{smallmatrix}\end{smallmatrix}').should == "<mtable />"
458
+ math_ml('\begin{pmatrix}\end{pmatrix}')[0].should =~ EimXML::Element.new(:mfenced, :open=>"(", :close=>")")
459
+ math_ml('\begin{bmatrix}\end{bmatrix}')[0].should =~ EimXML::Element.new(:mfenced, :open=>"[", :close=>"]")
460
+ math_ml('\begin{Bmatrix}\end{Bmatrix}')[0].should =~ EimXML::Element.new(:mfenced, :open=>"{", :close=>"}")
461
+ math_ml('\begin{vmatrix}\end{vmatrix}')[0].should =~ EimXML::Element.new(:mfenced, :open=>"|", :close=>"|")
462
+ math_ml('\begin{Vmatrix}\end{Vmatrix}')[0].should =~ EimXML::Element.new(:mfenced, :open=>EimXML::PCString.new("&DoubleVerticalBar;", true), :close=>EimXML::PCString.new("&DoubleVerticalBar;", true))
463
+ end
464
+
465
+ it "can be used in safe mode" do
466
+ Thread.start do
467
+ $SAFE=1
468
+ $SAFE.should == 1
469
+ lambda{smml('\alpha'.taint)}.should_not raise_error
470
+ end.join
471
+
472
+ $SAFE.should == 0
473
+ end
474
+
475
+ it "should parse symbols" do
476
+ smml('\precneqq').should == "<mo><mfrac linethickness='0' mathsize='1%'><mo>&prec;</mo><mo>&ne;</mo></mfrac></mo>"
477
+ end
478
+ end
479
+ end