mathemagical 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,122 @@
1
+ require "mathemagical"
2
+ require "spec/util"
3
+
4
+ describe Mathemagical::LaTeX::Macro do
5
+ include Mathemagical::Spec::Util
6
+
7
+ before(:all) do
8
+ @src = <<'EOT'
9
+ \newcommand{\newcom}{test}
10
+ \newcommand{\paramcom}[2]{param2 #2, param1 #1.}
11
+ \newcommand\ALPHA\alpha
12
+ \newcommand\BETA[1]\beta
13
+ \newcommand{\nothing}{}
14
+ \newenvironment{newenv}{begin_newenv}{end_newenv}
15
+ \newenvironment{paramenv}[2]{begin 1:#1, 2:#2}{end 2:#2 1:#1}
16
+ \newenvironment{nothing}{}{}
17
+ \newenvironment{separated environment}{sep}{env}
18
+ \newenvironment ENV
19
+ EOT
20
+ end
21
+
22
+ before do
23
+ @m = Mathemagical::LaTeX::Macro.new
24
+ @m.parse(@src)
25
+ end
26
+
27
+ it "#parse" do
28
+ @m = Mathemagical::LaTeX::Macro.new
29
+ lambda{@m.parse(@src)}.should_not raise_error
30
+
31
+ @m = Mathemagical::LaTeX::Macro.new
32
+ lambda{@m.parse('\newcommand{notcommand}{}')}.should raise_parse_error("Need newcommand.", '\\newcommand{', "notcommand}{}")
33
+ lambda{@m.parse('\newcommand{\separated command}{}')}.should raise_parse_error("Syntax error.", '\newcommand{\separated', " command}{}")
34
+ lambda{@m.parse('\newcommand{\nobody}')}.should raise_parse_error("Need parameter.", '\newcommand{\nobody}', "")
35
+ lambda{@m.parse('\newcommand{\noparam}{#1}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\noparam}{#', "1}")
36
+ lambda{@m.parse('\newcommand{\overopt}[1]{#1#2}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\overopt}[1]{#1#', "2}")
37
+ lambda{@m.parse('\newcommand{\strangeopt}[-1]')}.should raise_parse_error("Need positive number.", '\newcommand{\strangeopt}[', "-1]")
38
+ lambda{@m.parse('\newcommand{\strangeopt}[a]')}.should raise_parse_error("Need positive number.", '\newcommand{\strangeopt}[', "a]")
39
+
40
+ lambda{@m.parse('\newenvironment{\command}{}{}')}.should raise_parse_error("Syntax error.", '\newenvironment{', '\command}{}{}')
41
+ lambda{@m.parse('\newenvironment{nobegin}')}.should raise_parse_error("Need begin block.", '\newenvironment{nobegin}', "")
42
+ lambda{@m.parse('\newenvironment{noend}{}')}.should raise_parse_error("Need end block.", '\newenvironment{noend}{}', "")
43
+ lambda{@m.parse('\newenvironment{noparam}{#1}{}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{noparam}{#', "1}{}")
44
+ lambda{@m.parse('\newenvironment{overparam}[1]{#1#2}{}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{overparam}[1]{#1#', "2}{}")
45
+ lambda{@m.parse('\newenvironment{strangeparam}[-1]{}{}')}.should raise_parse_error("Need positive number.", '\newenvironment{strangeparam}[', "-1]{}{}")
46
+ lambda{@m.parse('\newenvironment{strangeparam}[a]{}{}')}.should raise_parse_error("Need positive number.", '\newenvironment{strangeparam}[', "a]{}{}")
47
+
48
+ lambda{@m.parse('\newcommand{\valid}{OK} \invalid{\test}{NG}')}.should raise_parse_error("Syntax error.", '\newcommand{\valid}{OK} ', '\invalid{\test}{NG}')
49
+ lambda{@m.parse('\newcommand{\valid}{OK} invalid{\test}{NG}')}.should raise_parse_error("Syntax error.", '\newcommand{\valid}{OK} ', 'invalid{\test}{NG}')
50
+
51
+ lambda{@m.parse('\newcommand{\newcom}[test')}.should raise_parse_error("Option not closed.", '\newcommand{\newcom}', '[test')
52
+ lambda{@m.parse('\newcommand{\newcom}[1][test')}.should raise_parse_error("Option not closed.", '\newcommand{\newcom}[1]', '[test')
53
+ lambda{@m.parse('\newcommand{\newcom}[1][]{#1#2}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\newcom}[1][]{#1#', '2}')
54
+ lambda{@m.parse('\newenvironment{newenv}[1][test')}.should raise_parse_error("Option not closed.", '\newenvironment{newenv}[1]', '[test')
55
+ lambda{@m.parse('\newenvironment{newenv}[1][test')}.should raise_parse_error("Option not closed.", '\newenvironment{newenv}[1]', '[test')
56
+
57
+ lambda{@m.parse('\newcommand{\newcom')}.should raise_parse_error("Block not closed.", '\newcommand', '{\newcom')
58
+ lambda{@m.parse('\newcommand{\newcom}{test1{test2}{test3')}.should raise_parse_error("Block not closed.", '\newcommand{\newcom}', '{test1{test2}{test3')
59
+
60
+ lambda{@m.parse('\newenvironment{newenv}[1][]{#1 #2}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{newenv}[1][]{#1 #', '2}')
61
+ end
62
+
63
+ it "#commands" do
64
+ @m.commands("newcom").num.should == 0
65
+ @m.commands("paramcom").num.should == 2
66
+ @m.commands("no").should == nil
67
+ end
68
+
69
+ it "#expand_command" do
70
+ @m.expand_command("not coommand", []).should == nil
71
+
72
+ @m.expand_command("newcom", []).should == "test"
73
+ @m.expand_command("newcom", ["dummy_param"]).should == "test"
74
+ @m.expand_command("paramcom", ["1", "2"]).should == "param2 2, param1 1."
75
+ @m.expand_command("paramcom", ["12", "34"]).should == "param2 34, param1 12."
76
+ lambda{@m.expand_command("paramcom", ["12"])}.should raise_parse_error("Need more parameter.", "", "")
77
+ lambda{@m.expand_command("paramcom", [])}.should raise_parse_error("Need more parameter.", "", "")
78
+ end
79
+
80
+ it "#environments" do
81
+ @m.environments("newenv").num.should == 0
82
+ @m.environments("paramenv").num.should == 2
83
+ @m.environments("not_env").should == nil
84
+ @m.environments("separated environment").num.should == 0
85
+ end
86
+
87
+ it "#expand_environment" do
88
+ @m.expand_environment('notregistered', "dummy", []).should == nil
89
+ @m.expand_environment("newenv", "body", []).should == ' begin_newenv body end_newenv '
90
+ @m.expand_environment("paramenv", "body", ["1", "2"]).should == ' begin 1:1, 2:2 body end 2:2 1:1 '
91
+ @m.expand_environment("paramenv", "body", ["12", "34"]).should == ' begin 1:12, 2:34 body end 2:34 1:12 '
92
+ lambda{@m.expand_environment("paramenv", "body", ["1"])}.should raise_parse_error("Need more parameter.", "", "")
93
+ lambda{@m.expand_environment("paramenv", "body", [])}.should raise_parse_error("Need more parameter.", "", "")
94
+ @m.expand_environment("nothing", "body", []).should == ' body '
95
+ @m.expand_environment("separated environment", "body", []).should == ' sep body env '
96
+ @m.expand_environment("E", "body", []).should == ' N body V '
97
+ end
98
+
99
+ it "#expand_with_options" do
100
+ src = <<'EOT'
101
+ \newcommand{\opt}[1][x]{#1}
102
+ \newcommand{\optparam}[2][]{#1#2}
103
+ \newenvironment{newenv}[1][x]{s:#1}{e:#1}
104
+ \newenvironment{optenv}[2][]{s:#1}{e:#2}
105
+ EOT
106
+
107
+ m = Mathemagical::LaTeX::Macro.new
108
+ m.parse(src)
109
+
110
+ m.expand_command("opt", []).should == 'x'
111
+ m.expand_command("opt", [], "1").should == '1'
112
+
113
+ m.expand_command("optparam", ["1"]).should == '1'
114
+ m.expand_command("optparam", ["1"], "2").should == '21'
115
+
116
+ m.expand_environment("newenv", "test", []).should == " s:x test e:x "
117
+ m.expand_environment("newenv", "test", [], "1").should == " s:1 test e:1 "
118
+
119
+ m.expand_environment("optenv", "test", ["1"]).should == " s: test e:1 "
120
+ m.expand_environment("optenv", "test", ["1"], "2").should == " s:2 test e:1 "
121
+ end
122
+ end
@@ -0,0 +1,574 @@
1
+ # coding: utf-8
2
+ require "eim_xml/parser"
3
+ require "eim_xml/dsl"
4
+ require "mathemagical"
5
+ require "spec/util"
6
+ require "mathemagical/symbol/character_reference"
7
+ require "mathemagical/symbol/utf8"
8
+
9
+ describe Mathemagical::LaTeX::Parser do
10
+ include Mathemagical::Spec::Util
11
+
12
+ def check_chr(tag, src)
13
+ src.scan(/./) do |c|
14
+ tag_re = Regexp.escape(tag)
15
+ smml(c).should =~ /\A<#{tag_re}(\s+[^>]+)?>#{Regexp.escape(c)}<\/#{tag_re}>\z/
16
+ end
17
+ end
18
+
19
+ def check_hash(tag, hash)
20
+ hash.each do |k, v|
21
+ tag_re = Regexp.escape(tag)
22
+ smml(k).should =~ /\A<#{tag_re}(\s+[^>]+)?>#{Regexp.escape(v)}<\/#{tag_re}>\z/
23
+ end
24
+ end
25
+
26
+ def check_entity(tag, hash)
27
+ check_hash(tag, hash.inject({}){|r, i| r[i[0]]="&#{i[1]};"; r})
28
+ end
29
+
30
+ it "Spec#strip_math_ml" do
31
+ src = "<math test='dummy'> <a> b </a> <c> d </c></math>"
32
+ strip_math_ml(src).should == "<a>b</a><c>d</c>"
33
+ end
34
+
35
+ describe "#parse" do
36
+ it "should return math element" do
37
+ ns = "http://www.w3.org/1998/Math/MathML"
38
+
39
+ e = new_parser.parse("")
40
+ e.to_s.should match("<math display='inline' xmlns='#{ns}' />")
41
+ e.attributes.keys.size.should == 2
42
+ e.children.should be_empty
43
+
44
+ e = new_parser.parse("", true)
45
+ e.to_s.should match("<math display='block' xmlns='#{ns}' />")
46
+ e.attributes.keys.size.should == 2
47
+ e.children.should be_empty
48
+
49
+ e = new_parser.parse("", false)
50
+ e.to_s.should match("<math display='inline' xmlns='#{ns}' />")
51
+ e.attributes.keys.size.should == 2
52
+ e.children.should be_empty
53
+ end
54
+
55
+ it "should ignore space" do
56
+ smml("{ a }").should == "<mrow><mi>a</mi></mrow>"
57
+ end
58
+
59
+ it "should process latex block" do
60
+ lambda{smml("test {test} {test")}.should raise_parse_error("Block not closed.", "test {test} ", "{test")
61
+ end
62
+
63
+ it "should raise error when error happened" do
64
+ src = 'a\hoge c'
65
+ lambda{smml(src)}.should raise_parse_error("Undefined command: hoge", "a", '\hoge c')
66
+
67
+ src = '\sqrt\sqrt1'
68
+ lambda{smml(src)}.should raise_parse_error("Syntax error.", '\sqrt\sqrt', "1")
69
+
70
+ src = "a{b"
71
+ lambda{smml(src)}.should raise_parse_error("Block not closed.", "a", "{b")
72
+ end
73
+
74
+ it "should process numerics" do
75
+ smml('1234567890').should == "<mn>1234567890</mn>"
76
+ smml('1.2').should == "<mn>1.2</mn>"
77
+ smml('1.').should == "<mn>1</mn><mo stretchy='false'>.</mo>"
78
+ smml('.2').should == "<mn>.2</mn>"
79
+ smml('1.2.3').should == "<mn>1.2</mn><mn>.3</mn>"
80
+ end
81
+
82
+ it "should process alphabets" do
83
+ smml("abc").should == "<mi>a</mi><mi>b</mi><mi>c</mi>"
84
+ check_chr("mi", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
85
+ end
86
+
87
+ it "should process non alphabet command" do
88
+ smml('\|').should == "<mo stretchy='false'>&DoubleVerticalBar;</mo>"
89
+ end
90
+
91
+ it "should process space commands" do
92
+ smml('\ ').should == "<mspace width='1em' />"
93
+ smml('\quad').should == "<mspace width='1em' />"
94
+ smml('\qquad').should == "<mspace width='2em' />"
95
+ smml('\,').should == "<mspace width='0.167em' />"
96
+ smml('\:').should == "<mspace width='0.222em' />"
97
+ smml('\;').should == "<mspace width='0.278em' />"
98
+ smml('\!').should == "<mspace width='-0.167em' />"
99
+ smml('~').should == "<mspace width='1em' />"
100
+ end
101
+
102
+ it "should process operators" do
103
+ check_chr("mo", ",.+-*=/()[]|;:!")
104
+ check_entity("mo", {"<"=>"lt", ">"=>"gt", '"'=>"quot"})
105
+ check_hash("mo", {'\backslash'=>'\\', '\%'=>'%', '\{'=>'{', '\}'=>'}', '\$'=>'$', '\#'=>'#'})
106
+ end
107
+
108
+ describe "should process prime" do
109
+ it "entity reference" do
110
+ smml("a'").should == "<msup><mi>a</mi><mo>&prime;</mo></msup>"
111
+ smml("a''").should == "<msup><mi>a</mi><mo>&prime;&prime;</mo></msup>"
112
+ smml("a'''").should == "<msup><mi>a</mi><mo>&prime;&prime;&prime;</mo></msup>"
113
+ smml("'").should == "<msup><none /><mo>&prime;</mo></msup>"
114
+
115
+ lambda{smml("a^b'")}.should raise_parse_error("Double superscript.", "a^b", "'")
116
+
117
+ smml("a'^b").should == "<msup><mi>a</mi><mrow><mo>&prime;</mo><mi>b</mi></mrow></msup>"
118
+ smml("a'''^b").should == "<msup><mi>a</mi><mrow><mo>&prime;&prime;&prime;</mo><mi>b</mi></mrow></msup>"
119
+ smml("a'b").should == "<msup><mi>a</mi><mo>&prime;</mo></msup><mi>b</mi>"
120
+ end
121
+
122
+ it "utf8" do
123
+ @parser = Mathemagical::LaTeX::Parser.new(:symbol=>Mathemagical::Symbol::UTF8)
124
+ smml("a'").should == "<msup><mi>a</mi><mo>′</mo></msup>"
125
+ smml("a'''").should == "<msup><mi>a</mi><mo>′′′</mo></msup>"
126
+ end
127
+
128
+ it "character reference" do
129
+ @parser = Mathemagical::LaTeX::Parser.new(:symbol=>Mathemagical::Symbol::CharacterReference)
130
+ smml("a'").should == "<msup><mi>a</mi><mo>&#x2032;</mo></msup>"
131
+ smml("a'''").should == "<msup><mi>a</mi><mo>&#x2032;&#x2032;&#x2032;</mo></msup>"
132
+ end
133
+ end
134
+
135
+ it "should process sqrt" do
136
+ smml('\sqrt a').should == "<msqrt><mi>a</mi></msqrt>"
137
+ smml('\sqrt[2]3').should == "<mroot><mn>3</mn><mn>2</mn></mroot>"
138
+ smml('\sqrt[2a]3').should == "<mroot><mn>3</mn><mrow><mn>2</mn><mi>a</mi></mrow></mroot>"
139
+ lambda{smml('\sqrt[12')}.should raise_parse_error("Option not closed.", '\sqrt', "[12")
140
+ end
141
+
142
+ it "should process subsup" do
143
+ smml("a_b^c").should == "<msubsup><mi>a</mi><mi>b</mi><mi>c</mi></msubsup>"
144
+ smml("a_b").should == "<msub><mi>a</mi><mi>b</mi></msub>"
145
+ smml("a^b").should == "<msup><mi>a</mi><mi>b</mi></msup>"
146
+ smml("_a^b").should == "<msubsup><none /><mi>a</mi><mi>b</mi></msubsup>"
147
+
148
+ lambda{smml("a_b_c")}.should raise_parse_error("Double subscript.", "a_b", "_c")
149
+ lambda{smml("a^b^c")}.should raise_parse_error("Double superscript.", "a^b", "^c")
150
+ lambda{smml("a_")}.should raise_parse_error("Subscript not exist.", "a_", "")
151
+ lambda{smml("a^")}.should raise_parse_error("Superscript not exist.", "a^", "")
152
+ end
153
+
154
+ it "should process underover" do
155
+ smml('\sum_a^b', true).should == "<munderover><mo stretchy='false'>&sum;</mo><mi>a</mi><mi>b</mi></munderover>"
156
+ smml('\sum_a^b').should == "<msubsup><mo stretchy='false'>&sum;</mo><mi>a</mi><mi>b</mi></msubsup>"
157
+ smml('\sum_a', true).should == "<munder><mo stretchy='false'>&sum;</mo><mi>a</mi></munder>"
158
+ smml('\sum^a', true).should == "<mover><mo stretchy='false'>&sum;</mo><mi>a</mi></mover>"
159
+ smml('\sum_a').should == "<msub><mo stretchy='false'>&sum;</mo><mi>a</mi></msub>"
160
+ smml('\sum^a').should == "<msup><mo stretchy='false'>&sum;</mo><mi>a</mi></msup>"
161
+
162
+ lambda{smml('\sum_b_c')}.should raise_parse_error("Double subscript.", '\sum_b', "_c")
163
+ lambda{smml('\sum^b^c')}.should raise_parse_error("Double superscript.", '\sum^b', "^c")
164
+ lambda{smml('\sum_')}.should raise_parse_error("Subscript not exist.", '\sum_', "")
165
+ lambda{smml('\sum^')}.should raise_parse_error("Superscript not exist.", '\sum^', "")
166
+ end
167
+
168
+ it "should process font commands" do
169
+ 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>"
170
+ 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>"
171
+ 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>"
172
+
173
+ 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>"
174
+ smml('\mathbf12').should == "<mrow><mn mathvariant='bold'>1</mn></mrow><mn>2</mn>"
175
+ 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>"
176
+ 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>"
177
+
178
+ smml('a \mathbb{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bopf;</mi><mi>&copf;</mi></mrow></mrow><mi>d</mi>"
179
+ smml('a \mathscr{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bscr;</mi><mi>&cscr;</mi></mrow></mrow><mi>d</mi>"
180
+ smml('a \mathfrak{b c} d').should == "<mi>a</mi><mrow><mrow><mi>&bfr;</mi><mi>&cfr;</mi></mrow></mrow><mi>d</mi>"
181
+ 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>"
182
+ smml('\bm ab').should == "<mrow><mi mathvariant='bold-italic'>a</mi></mrow><mi>b</mi>"
183
+
184
+ lambda{smml('\mathit')}.should raise_parse_error("Syntax error.", '\mathit', "")
185
+ lambda{smml('\mathrm')}.should raise_parse_error("Syntax error.", '\mathrm', "")
186
+ lambda{smml('\mathbf')}.should raise_parse_error("Syntax error.", '\mathbf', "")
187
+ lambda{smml('\mathbb')}.should raise_parse_error("Syntax error.", '\mathbb', "")
188
+ lambda{smml('\mathscr')}.should raise_parse_error("Syntax error.", '\mathscr', "")
189
+ lambda{smml('\mathfrak')}.should raise_parse_error("Syntax error.", '\mathfrak', "")
190
+ end
191
+
192
+ it "should process mbox" do
193
+ smml('a\mbox{b c}d').should == "<mi>a</mi><mtext>b c</mtext><mi>d</mi>"
194
+ smml('\mbox{<>\'"&}').should == '<mtext>&lt;&gt;&apos;&quot;&amp;</mtext>'
195
+ end
196
+
197
+ it "should process frac" do
198
+ smml('\frac ab').should == "<mfrac><mi>a</mi><mi>b</mi></mfrac>"
199
+ smml('\frac12').should == "<mfrac><mn>1</mn><mn>2</mn></mfrac>"
200
+
201
+ lambda{smml('\frac a')}.should raise_parse_error("Syntax error.", '\frac a', "")
202
+ end
203
+
204
+ it "should process environment" do
205
+ lambda{smml('{\begin}rest')}.should raise_parse_error("Environment name not exist.", '{\begin', '}rest')
206
+
207
+ lambda{smml('{\begin{array}{c}dummy}rest')}.should raise_parse_error('Matching \end not exist.', '{\begin{array}{c}dummy', '}rest')
208
+
209
+ lambda{smml('\begin{array}c dummy\end{test}')}.should raise_parse_error("Environment mismatched.", '\begin{array}c dummy\end', "{test}")
210
+
211
+ lambda{smml('\left(\begin{array}\right)')}.should raise_parse_error("Syntax error.", '\left(\begin{array}', '\right)')
212
+ end
213
+
214
+ it "should process array" do
215
+ 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>"
216
+
217
+ 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>"
218
+
219
+ smml('\begin{array}{c}\end{array}').should == "<mtable />"
220
+
221
+ lambda{smml('\begin{array}\end{array}')}.should raise_parse_error('Syntax error.', '\begin{array}', '\end{array}')
222
+
223
+ lambda{smml('\begin{array}{a}\end{array}')}.should raise_parse_error("Syntax error.", '\begin{array}{', 'a}\end{array}')
224
+
225
+ 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}')
226
+
227
+ lambda{smml('\begin{array}{cc}a\end{array}')}.should raise_parse_error("Need more column.", '\begin{array}{cc}a', '\end{array}')
228
+
229
+ lambda{smml('\begin{array}{c}a&\end{array}')}.should raise_parse_error("Too many column.", '\begin{array}{c}a', '&\end{array}')
230
+
231
+ smml('\begin{array}{cc}&\end{array}').should == "<mtable><mtr><mtd /><mtd /></mtr></mtable>"
232
+
233
+ smml('\left\{\begin{array}ca_b\end{array}\right\}').to_s.should == EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
234
+ element :mrow do
235
+ element :mtable do
236
+ element :mtr do
237
+ element :mtd do
238
+ element :msub do
239
+ element(:mi).add("a")
240
+ element(:mi).add("b")
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+ }.to_s
247
+
248
+ 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>"
249
+
250
+ smml('\left\{\begin{array}ca_b\end{array}\right\}').should == EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}"){
251
+ element :mrow do
252
+ element :mtable do
253
+ element :mtr do
254
+ element :mtd do
255
+ element :msub do
256
+ element(:mi).add("a")
257
+ element(:mi).add("b")
258
+ end
259
+ end
260
+ end
261
+ end
262
+ end
263
+ }.to_s
264
+
265
+ 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>"
266
+ 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>"
267
+ smml('\begin{array}{c}\hline c\end{array}').should == "<mtable rowlines='solid'><mtr /><mtr><mtd><mi>c</mi></mtd></mtr></mtable>"
268
+ 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>"
269
+ 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>"
270
+ end
271
+
272
+ it "should parse \\left and \\right" do
273
+ smml('\left(\frac12\right)').should == EimXML::DSL.element(:mfenced, :open=>"(", :close=>")"){
274
+ element :mrow do
275
+ element :mfrac do
276
+ element(:mn).add("1")
277
+ element(:mn).add("2")
278
+ end
279
+ end
280
+ }.to_s
281
+
282
+ smml('\left \{ a \right \}').should == EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}") do
283
+ element :mrow do
284
+ element(:mi).add("a")
285
+ end
286
+ end.to_s
287
+
288
+ smml('\left\{\begin{array}c\begin{array}ca\end{array}\end{array}\right\}').should == EimXML::DSL.element(:mfenced, :open=>"{", :close=>"}") do
289
+ element :mrow do
290
+ element :mtable do
291
+ element :mtr do
292
+ element :mtd do
293
+ element :mtable do
294
+ element :mtr do
295
+ element :mtd do
296
+ element(:mi).add("a")
297
+ end
298
+ end
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end.to_s
305
+
306
+ smml('\left(\sum_a\right)').should == EimXML::DSL.element(:mfenced, :open=>"(", :close=>")") do
307
+ element :mrow do
308
+ element :msub do
309
+ element(:mo, :stretchy => "false").add(EimXML::PCString.new("&sum;", true))
310
+ element(:mi).add("a")
311
+ end
312
+ end
313
+ end.to_s
314
+
315
+ smml('\left(\sum_a\right)', true).should == EimXML::DSL.element(:mfenced, :open=>"(", :close=>")") do
316
+ element :mrow do
317
+ element :munder do
318
+ element(:mo, :stretchy => "false").add(EimXML::PCString.new("&sum;", true))
319
+ element(:mi).add("a")
320
+ end
321
+ end
322
+ end.to_s
323
+
324
+ lambda{smml('\left(test')}.should raise_parse_error("Brace not closed.", '\left', '(test')
325
+
326
+ smml('\left\|a\right\|').should == EimXML::DSL.element(:mfenced, :open=>EimXML::PCString.new("&DoubleVerticalBar;", true), :close=>EimXML::PCString.new("&DoubleVerticalBar;", true)) do
327
+ element :mrow do
328
+ element(:mi).add("a")
329
+ end
330
+ end.to_s
331
+
332
+ lambda{smml('\left')}.should raise_parse_error("Need brace here.", '\left', "")
333
+ end
334
+
335
+ it "should parse overs" do
336
+ smml('\hat a').should == "<mover><mi>a</mi><mo>&circ;</mo></mover>"
337
+ smml('\hat12').should == "<mover><mn>1</mn><mo>&circ;</mo></mover><mn>2</mn>"
338
+ lambda{smml('{\hat}a')}.should raise_parse_error("Syntax error.", '{\hat', '}a')
339
+ end
340
+
341
+ it "should parse unders" do
342
+ smml('\underline a').should == "<munder><mi>a</mi><mo>&macr;</mo></munder>"
343
+ smml('\underline12').should == "<munder><mn>1</mn><mo>&macr;</mo></munder><mn>2</mn>"
344
+ lambda{smml('{\underline}a')}.should raise_parse_error("Syntax error.", '{\underline', '}a')
345
+ end
346
+
347
+ it "should parse stackrel" do
348
+ smml('\stackrel\to=').should == "<mover><mo stretchy='false'>=</mo><mo stretchy='false'>&rightarrow;</mo></mover>"
349
+ smml('\stackrel12').should == "<mover><mn>2</mn><mn>1</mn></mover>"
350
+ end
351
+
352
+ it "should parse comment" do
353
+ smml('a%b').should == "<mi>a</mi>"
354
+ end
355
+
356
+ it "should parse entity" do
357
+ p = new_parser
358
+ lambda{smml('\entity{therefore}', false, p)}.should raise_parse_error("Unregistered entity.", '\entity{', "therefore}")
359
+
360
+ p.unsecure_entity = true
361
+ smml('\entity{therefore}', false, p).should == "<mo>&therefore;</mo>"
362
+
363
+ p.unsecure_entity = false
364
+ lambda{smml('\entity{therefore}', false, p)}.should raise_parse_error("Unregistered entity.", '\entity{', "therefore}")
365
+
366
+ p.add_entity(['therefore'])
367
+ smml('\entity{therefore}', false, p).should == "<mo>&therefore;</mo>"
368
+ end
369
+
370
+ it "should parse backslash" do
371
+ smml('\\\\').should == "<br xmlns='http://www.w3.org/1999/xhtml' />"
372
+ end
373
+
374
+ it "can be used with macro" do
375
+ macro = <<'EOS'
376
+ \newcommand{\root}[2]{\sqrt[#1]{#2}}
377
+ \newcommand{\ROOT}[2]{\sqrt[#1]#2}
378
+ \newenvironment{braced}[2]{\left#1}{\right#2}
379
+ \newenvironment{sq}[2]{\sqrt[#2]{#1}}{\sqrt#2}
380
+ \newcommand{\R}{\mathbb R}
381
+ \newenvironment{BB}{\mathbb A}{\mathbb B}
382
+ EOS
383
+ p = new_parser
384
+ p.macro.parse(macro)
385
+
386
+ smml('\root12', false, p).should == "<mroot><mrow><mn>2</mn></mrow><mn>1</mn></mroot>"
387
+ smml('\root{12}{34}', false, p).should == "<mroot><mrow><mn>34</mn></mrow><mn>12</mn></mroot>"
388
+ smml('\ROOT{12}{34}', false, p).should == "<mroot><mn>3</mn><mn>12</mn></mroot><mn>4</mn>"
389
+ lambda{smml('\root', false, p)}.should raise_parse_error('Error in macro(Need more parameter. "").', '', '\root')
390
+
391
+
392
+ smml('\begin{braced}{|}{)}\frac12\end{braced}', false, p).should == EimXML::DSL.element(:mfenced, :open=>"|", :close=>")") do
393
+ element(:mrow) do
394
+ element(:mfrac) do
395
+ element(:mn).add("1")
396
+ element(:mn).add("2")
397
+ end
398
+ end
399
+ end.to_s
400
+
401
+ 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>"
402
+ lambda{smml('\begin{braced}', false, p)}.should raise_parse_error("Need more parameter.", '\begin{braced}', "")
403
+ lambda{smml('\begin{braced}123', false, p)}.should raise_parse_error('Matching \end not exist.', '\begin{braced}', "123")
404
+ lambda{smml('\begin{braced}123\end{brace}', false, p)}.should raise_parse_error("Environment mismatched.", '\begin{braced}123\end', '{brace}')
405
+ smml('\R', false, p).should == "<mrow><mi>&Ropf;</mi></mrow>"
406
+ smml('\begin{BB}\end{BB}', false, p).should == "<mrow><mi>&Aopf;</mi></mrow><mrow><mi>&Bopf;</mi></mrow>"
407
+ end
408
+
409
+ it "should raise error when macro define circular reference" do
410
+ macro = <<'EOT'
411
+ \newcommand{\C}{\C}
412
+ \newenvironment{E}{\begin{E}}{\end{E}}
413
+ \newcommand{\D}{\begin{F}\end{F}}
414
+ \newenvironment{F}{\D}{}
415
+ EOT
416
+ ps = new_parser
417
+ ps.macro.parse(macro)
418
+
419
+ lambda{smml('\C', false, ps)}.should raise_parse_error("Circular reference.", "", '\C')
420
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error("Circular reference.", "", '\begin{E}\end{E}')
421
+ lambda{smml('\D', false, ps)}.should raise_parse_error("Circular reference.", "", '\D')
422
+ lambda{smml('\begin{F}\end{F}', false, ps)}.should raise_parse_error("Circular reference.", "", '\begin{F}\end{F}')
423
+ end
424
+
425
+ it "should raise error when macro uses undefined command" do
426
+ macro = <<'EOT'
427
+ \newcommand{\C}{\dummy}
428
+ \newenvironment{E}{\dummy}{}
429
+ EOT
430
+ ps = new_parser
431
+ ps.macro.parse(macro)
432
+
433
+ lambda{smml('\C', false, ps)}.should raise_parse_error('Error in macro(Undefined command: dummy "\dummy").', "", '\C')
434
+ lambda{smml('\C', false, ps)}.should raise_parse_error('Error in macro(Undefined command: dummy "\dummy").', "", '\C')
435
+
436
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error('Error in macro(Undefined command: dummy "\dummy").', '', '\begin{E}\end{E}')
437
+ lambda{smml('\begin{E}\end{E}', false, ps)}.should raise_parse_error('Error in macro(Undefined command: dummy "\dummy").', "", '\begin{E}\end{E}')
438
+ end
439
+
440
+ it "can be used with macro with option" do
441
+ macro = <<'EOS'
442
+ \newcommand{\opt}[1][x]{#1}
443
+ \newcommand{\optparam}[2][]{#1#2}
444
+ \newenvironment{newenv}[1][x]{#1}{#1}
445
+ \newenvironment{optenv}[2][]{#1}{#2}
446
+ EOS
447
+
448
+ p = new_parser
449
+ p.macro.parse(macro)
450
+
451
+ smml('\opt a', false, p).should == "<mi>x</mi><mi>a</mi>"
452
+ smml('\opt[0] a', false, p).should == "<mn>0</mn><mi>a</mi>"
453
+ smml('\optparam a', false, p).should == "<mi>a</mi>"
454
+ smml('\optparam[0] a', false, p).should == "<mn>0</mn><mi>a</mi>"
455
+
456
+ smml('\begin{newenv}a\end{newenv}', false, p).should == "<mi>x</mi><mi>a</mi><mi>x</mi>"
457
+ smml('\begin{newenv}[0]a\end{newenv}', false, p).should == "<mn>0</mn><mi>a</mi><mn>0</mn>"
458
+ smml('\begin{optenv}0a\end{optenv}', false, p).should == "<mi>a</mi><mn>0</mn>"
459
+ smml('\begin{optenv}[0]1a\end{optenv}', false, p).should == "<mn>0</mn><mi>a</mi><mn>1</mn>"
460
+ end
461
+
462
+ it "should parse matrix environment" do
463
+ smml('\begin{matrix}&&\\\\&\end{matrix}').should == "<mtable><mtr><mtd /><mtd /><mtd /></mtr><mtr><mtd /><mtd /></mtr></mtable>"
464
+ lambda{smml('\begin{matrix}&&\\\\&\end{mat}')}.should raise_parse_error("Environment mismatched.", '\begin{matrix}&&\\\\&\end', "{mat}")
465
+ lambda{smml('\begin{matrix}&&\\\\&')}.should raise_parse_error("Matching \\end not exist.", '\begin{matrix}&&\\\\&', '')
466
+ 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>"
467
+ smml('\begin{matrix}\end{matrix}').should == "<mtable />"
468
+ 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>"
469
+
470
+ smml('\begin{smallmatrix}\end{smallmatrix}').should == "<mtable />"
471
+ smml('\begin{pmatrix}\end{pmatrix}').should == "<mfenced open='(' close=')'><mrow><mtable /></mrow></mfenced>"
472
+ smml('\begin{bmatrix}\end{bmatrix}').should == "<mfenced open='[' close=']'><mrow><mtable /></mrow></mfenced>"
473
+ smml('\begin{Bmatrix}\end{Bmatrix}').should == "<mfenced open='{' close='}'><mrow><mtable /></mrow></mfenced>"
474
+ smml('\begin{vmatrix}\end{vmatrix}').should == "<mfenced open='|' close='|'><mrow><mtable /></mrow></mfenced>"
475
+ smml('\begin{Vmatrix}\end{Vmatrix}').should == "<mfenced open='&DoubleVerticalBar;' close='&DoubleVerticalBar;'><mrow><mtable /></mrow></mfenced>"
476
+ end
477
+
478
+ it "can be used in safe mode" do
479
+ Thread.start do
480
+ $SAFE=1
481
+ $SAFE.should == 1
482
+ lambda{smml('\alpha'.taint)}.should_not raise_error
483
+ end.join
484
+
485
+ $SAFE.should == 0
486
+ end
487
+
488
+ it "should parse symbols" do
489
+ smml('\precneqq').should == "<mo stretchy='false'>&#x2ab5;</mo>"
490
+ end
491
+ end
492
+
493
+ context ".new should accept symbol table" do
494
+ it "character reference" do
495
+ @parser = Mathemagical::LaTeX::Parser.new(:symbol=>Mathemagical::Symbol::CharacterReference)
496
+ smml('\alpha').should == "<mi>&#x3b1;</mi>"
497
+ smml('\mathbb{abcABC}').should == "<mrow><mrow><mi>&#x1d552;</mi><mi>&#x1d553;</mi><mi>&#x1d554;</mi><mi>&#x1d538;</mi><mi>&#x1d539;</mi><mi>&#x2102;</mi></mrow></mrow>"
498
+ smml('\mathscr{abcABC}').should == "<mrow><mrow><mi>&#x1d4b6;</mi><mi>&#x1d4b7;</mi><mi>&#x1d4b8;</mi><mi>&#x1d49c;</mi><mi>&#x212c;</mi><mi>&#x1d49e;</mi></mrow></mrow>"
499
+ smml('\mathfrak{abcABC}').should == "<mrow><mrow><mi>&#x1d51e;</mi><mi>&#x1d51f;</mi><mi>&#x1d520;</mi><mi>&#x1d504;</mi><mi>&#x1d505;</mi><mi>&#x212d;</mi></mrow></mrow>"
500
+ end
501
+
502
+ it "utf8" do
503
+ @parser = Mathemagical::LaTeX::Parser.new(:symbol=>Mathemagical::Symbol::UTF8)
504
+ smml('\alpha').should == "<mi>α</mi>"
505
+ smml('\mathbb{abcABC}').should == "<mrow><mrow><mi>𝕒</mi><mi>𝕓</mi><mi>𝕔</mi><mi>𝔸</mi><mi>𝔹</mi><mi>ℂ</mi></mrow></mrow>"
506
+ smml('\mathscr{abcABC}').should == "<mrow><mrow><mi>𝒶</mi><mi>𝒷</mi><mi>𝒸</mi><mi>𝒜</mi><mi>ℬ</mi><mi>𝒞</mi></mrow></mrow>"
507
+ smml('\mathfrak{abcABC}').should == "<mrow><mrow><mi>𝔞</mi><mi>𝔟</mi><mi>𝔠</mi><mi>𝔄</mi><mi>𝔅</mi><mi>ℭ</mi></mrow></mrow>"
508
+ end
509
+ end
510
+
511
+ context "#symbol_table" do
512
+ it "should return when .new was given name of symbol-module" do
513
+ ps = Mathemagical::LaTeX::Parser
514
+ symbol = Mathemagical::Symbol
515
+
516
+ ps.new(:symbol=>symbol::UTF8).symbol_table.should == symbol::UTF8
517
+ ps.new(:symbol=>symbol::EntityReference).symbol_table.should == symbol::EntityReference
518
+ ps.new(:symbol=>symbol::CharacterReference).symbol_table.should == symbol::CharacterReference
519
+
520
+ ps.new(:symbol=>:utf8).symbol_table.should == symbol::UTF8
521
+ ps.new(:symbol=>:entity).symbol_table.should == symbol::EntityReference
522
+ ps.new(:symbol=>:character).symbol_table.should == symbol::CharacterReference
523
+
524
+ ps.new.symbol_table.should == symbol::EntityReference
525
+ ps.new(:symbol=>nil).symbol_table.should == symbol::EntityReference
526
+ end
527
+
528
+ context "should return default symbol module" do
529
+ before do
530
+ @loaded_features = $LOADED_FEATURES.dup
531
+ $LOADED_FEATURES.delete_if{|i| i=~/mathemagical/}
532
+ if ::Object.const_defined?(:Mathemagical)
533
+ @Mathemagical = ::Object.const_get(:Mathemagical)
534
+ ::Object.module_eval{remove_const(:Mathemagical)}
535
+ end
536
+ end
537
+
538
+ after do
539
+ $LOADED_FEATURES.clear
540
+ $LOADED_FEATURES.push(@loaded_features.shift) until @loaded_features.empty?
541
+ if @Mathemagical
542
+ ::Object.module_eval{remove_const(:Mathemagical)}
543
+ ::Object.const_set(:Mathemagical, @Mathemagical)
544
+ end
545
+ end
546
+
547
+ it "character entity reference version by default" do
548
+ require("mathemagical").should be_true
549
+ Mathemagical::LaTeX::Parser.new.symbol_table.should == Mathemagical::Symbol::EntityReference
550
+ end
551
+
552
+ describe "character entity reference version when set by requiring" do
553
+ it do
554
+ require("mathemagical/symbol/entity_reference").should be_true
555
+ Mathemagical::LaTeX::Parser.new.symbol_table.should == Mathemagical::Symbol::EntityReference
556
+ end
557
+ end
558
+
559
+ describe "utf8 version when set by requiring" do
560
+ it do
561
+ require("mathemagical/symbol/utf8").should be_true
562
+ Mathemagical::LaTeX::Parser.new.symbol_table.should == Mathemagical::Symbol::UTF8
563
+ end
564
+ end
565
+
566
+ describe "numeric character reference version when set by requiring" do
567
+ it do
568
+ require("mathemagical/symbol/character_reference").should be_true
569
+ Mathemagical::LaTeX::Parser.new.symbol_table.should == Mathemagical::Symbol::CharacterReference
570
+ end
571
+ end
572
+ end
573
+ end
574
+ end