mathemagical 0.0.1

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,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