mathml 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +58 -0
- data/Rakefile +121 -0
- data/lib/math_ml.rb +1885 -0
- data/lib/math_ml/string.rb +32 -0
- data/lib/math_ml/util.rb +350 -0
- data/symbols/fetch_symbol.rb +446 -0
- data/symbols/fetch_symbol_test.rb +180 -0
- data/symbols/list.txt +518 -0
- data/test/math_ml_test.rb +780 -0
- data/test/math_ml_test_util.rb +48 -0
- data/test/math_ml_test_util_test.rb +22 -0
- data/test/string_test.rb +36 -0
- data/test/util_test.rb +694 -0
- metadata +80 -0
data/README
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= MathML Library
|
2
|
+
:lang:ja
|
3
|
+
本ライブラリは、LaTeXの記法にしたがって記述された数式をMathMLに変換する処理を提供します。
|
4
|
+
|
5
|
+
:lang:en
|
6
|
+
This library provide converter from LaTeX mathematical expressions to MathML.
|
7
|
+
|
8
|
+
:lang:
|
9
|
+
= URLs
|
10
|
+
* Documents(Japanese)[http://mathml.rubyforge.org/ja/]
|
11
|
+
* Documents(Englise)[http://mathml.rubyforge.org/en/]
|
12
|
+
* MercurialRepository[https://hg.hinet.mydns.jp/math_ml/]
|
13
|
+
|
14
|
+
= Example
|
15
|
+
:lang:ja
|
16
|
+
例えば MathML::String による Stringクラスの拡張を使えば、次のようにして変換処理を呼び出すことが出来ます。
|
17
|
+
|
18
|
+
:lang:en
|
19
|
+
For example, using extension of String class by MathML::String, you can use converter like bellow.
|
20
|
+
|
21
|
+
:lang:
|
22
|
+
#!/usr/bin/ruby
|
23
|
+
# require "rubygems"
|
24
|
+
require "math_ml/string"
|
25
|
+
puts 'x=\frac{-b\pm\sqrt{b^2-4ac}}{2a}'.to_mathml
|
26
|
+
|
27
|
+
:lang:ja
|
28
|
+
このスクリプトの出力は次のようになります。
|
29
|
+
|
30
|
+
:lang:en
|
31
|
+
Output of this script is bellow.
|
32
|
+
|
33
|
+
:lang:
|
34
|
+
<math xmlns='http://www.w3.org/1998/Math/MathML' display='inline'>
|
35
|
+
<mi>x</mi>
|
36
|
+
<mo>=</mo>
|
37
|
+
<mfrac>
|
38
|
+
<mrow>
|
39
|
+
<mo>-</mo>
|
40
|
+
<mi>b</mi>
|
41
|
+
<mo>±</mo>
|
42
|
+
<msqrt><mrow>
|
43
|
+
<msup>
|
44
|
+
<mi>b</mi>
|
45
|
+
<mn>2</mn>
|
46
|
+
</msup>
|
47
|
+
<mo>-</mo>
|
48
|
+
<mn>4</mn>
|
49
|
+
<mi>a</mi>
|
50
|
+
<mi>c</mi>
|
51
|
+
</mrow></msqrt>
|
52
|
+
</mrow>
|
53
|
+
<mrow>
|
54
|
+
<mn>2</mn>
|
55
|
+
<mi>a</mi>
|
56
|
+
</mrow>
|
57
|
+
</mfrac>
|
58
|
+
</math>
|
data/Rakefile
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require "rake/clean"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "rake/rdoctask"
|
4
|
+
require "rake/gempackagetask"
|
5
|
+
|
6
|
+
FILES = FileList["**/*"].exclude("pkg", "html", "eim_xml")
|
7
|
+
CLOBBER.include("eim_xml")
|
8
|
+
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
|
12
|
+
### Document ###
|
13
|
+
RDOC_DIR = "./html/"
|
14
|
+
RDOC_OPTS = ["-S", "-w", "3", "-c", "UTF-8", "-m", "README"]
|
15
|
+
RDOC_OPTS << "-d" if ENV["DOT"]
|
16
|
+
RDOC_FILES = FileList["lib/**/*.rb"]
|
17
|
+
RDOC_EXTRAS = FileList["README"]
|
18
|
+
["", "ja", "en"].each do |l|
|
19
|
+
dir = RDOC_DIR.dup
|
20
|
+
dir << "#{l}/" unless l.empty?
|
21
|
+
Rake::RDocTask.new("rdoc#{":"+l unless l.empty?}") do |rdoc|
|
22
|
+
rdoc.title = "MathML Library"
|
23
|
+
rdoc.options = RDOC_OPTS.dup
|
24
|
+
rdoc.options << "-l" << l unless l.empty?
|
25
|
+
rdoc.rdoc_dir = dir
|
26
|
+
rdoc.rdoc_files.include(RDOC_FILES, RDOC_EXTRAS)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
task "rdoc:all" => ["rdoc", "rdoc:ja", "rdoc:en"]
|
30
|
+
task "rerdoc:all" => ["rerdoc", "rerdoc:ja", "rerdoc:en"]
|
31
|
+
|
32
|
+
|
33
|
+
### Publish document ###
|
34
|
+
task :publish => [:clobber_rdoc, "rdoc:ja", "rdoc:en"] do
|
35
|
+
require "rake/contrib/rubyforgepublisher"
|
36
|
+
cp "index.html", "html/index.html"
|
37
|
+
Rake::RubyForgePublisher.new("mathml", "hiraku").upload
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
### Clone external library ###
|
42
|
+
EIMXML_DIR = "./eim_xml"
|
43
|
+
file EIMXML_DIR do |t|
|
44
|
+
sh "hg clone https://hg.hinet.mydns.jp/eim_xml #{t.name}"
|
45
|
+
end
|
46
|
+
task :eim_xml => EIMXML_DIR
|
47
|
+
|
48
|
+
|
49
|
+
### Test ###
|
50
|
+
task :test => "test:apart"
|
51
|
+
namespace :test do
|
52
|
+
def add_libs_for_test(t)
|
53
|
+
t.libs << "test" << "eim_xml/lib" << "../eim_xml/lib"
|
54
|
+
end
|
55
|
+
FileList["test/*_test.rb"].sort{|a,b| File.mtime(a)<=>File.mtime(b)}.reverse.each do |i|
|
56
|
+
Rake::TestTask.new(:apart) do |t|
|
57
|
+
t.test_files = i
|
58
|
+
add_libs_for_test(t)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
task(:apart).comment = "Run tests separately"
|
62
|
+
|
63
|
+
Rake::TestTask.new(:lump) do |t|
|
64
|
+
t.test_files = FileList["test/*_test.rb"]
|
65
|
+
add_libs_for_test(t)
|
66
|
+
end
|
67
|
+
task(:lump).comment = "Run all tests in a lump"
|
68
|
+
|
69
|
+
Rake::TestTask.new(:symbol) do |t|
|
70
|
+
t.test_files = FileList["symbols/*_test.rb"]
|
71
|
+
add_libs_for_test(t)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
### Build GEM ###
|
77
|
+
GEM_DIR = "./pkg"
|
78
|
+
directory GEM_DIR
|
79
|
+
def build_gem(unstable=false)
|
80
|
+
spec = Gem::Specification.new do |spec|
|
81
|
+
spec.name = "mathml"
|
82
|
+
spec.rubyforge_project = "mathml"
|
83
|
+
spec.version = "0.8.0"
|
84
|
+
spec.summary = "MathML Library"
|
85
|
+
spec.author = "KURODA Hiraku"
|
86
|
+
spec.email = "hiraku@hinet.mydns.jp"
|
87
|
+
spec.homepage = "http://mathml.rubyforge.org/"
|
88
|
+
spec.add_dependency("eimxml")
|
89
|
+
spec.files = FILES
|
90
|
+
spec.test_files = Dir.glob("test/*.rb")
|
91
|
+
spec.has_rdoc = true
|
92
|
+
spec.rdoc_options = RDOC_OPTS.dup
|
93
|
+
spec.extra_rdoc_files = RDOC_EXTRAS
|
94
|
+
end
|
95
|
+
|
96
|
+
spec.version = spec.version.to_s << Time.now.strftime(".%Y.%m%d.%H%M") if unstable
|
97
|
+
b = Gem::Builder.new(spec)
|
98
|
+
gname = b.build
|
99
|
+
mv gname, "#{GEM_DIR}/"
|
100
|
+
end
|
101
|
+
|
102
|
+
desc "Build gem package"
|
103
|
+
task :gem => GEM_DIR do
|
104
|
+
build_gem
|
105
|
+
end
|
106
|
+
|
107
|
+
desc "Build unstable version gem package"
|
108
|
+
task "gem:unstable" => GEM_DIR do
|
109
|
+
build_gem(true)
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
### Build package ###
|
114
|
+
package_task = Rake::PackageTask.new("math_ml",ENV["VER"] || :noversion) do |t|
|
115
|
+
t.package_files.include(FILES)
|
116
|
+
t.need_tar_gz = true
|
117
|
+
end
|
118
|
+
|
119
|
+
file "#{package_task.package_dir_path}" => EIMXML_DIR do |t|
|
120
|
+
cp "#{EIMXML_DIR}/lib/eim_xml.rb", "#{t.name}/lib/"
|
121
|
+
end
|
data/lib/math_ml.rb
ADDED
@@ -0,0 +1,1885 @@
|
|
1
|
+
# MathML Library
|
2
|
+
#
|
3
|
+
# Copyright (C) 2005, KURODA Hiraku <hiraku@hinet.mydns.jp>
|
4
|
+
# You can redistribute it and/or modify it under GPL2.
|
5
|
+
|
6
|
+
unless __FILE__ == File.expand_path(__FILE__)
|
7
|
+
require File.expand_path(File.dirname(__FILE__))+"/"+File.basename(__FILE__, ".rb")
|
8
|
+
else
|
9
|
+
|
10
|
+
require "strscan"
|
11
|
+
module MathML
|
12
|
+
require "eim_xml"
|
13
|
+
|
14
|
+
class XMLElement < EimXML::Element
|
15
|
+
def pop
|
16
|
+
@contents.pop
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.pcstring(s, encoded=false)
|
21
|
+
EimXML::PCString.new(s, encoded)
|
22
|
+
end
|
23
|
+
|
24
|
+
PCString = EimXML::PCString
|
25
|
+
|
26
|
+
class Error < StandardError; end
|
27
|
+
|
28
|
+
class Element < XMLElement
|
29
|
+
attr_reader :display_style
|
30
|
+
|
31
|
+
def as_display_style
|
32
|
+
@display_style = true
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def <<(s)
|
37
|
+
s = MathML.pcstring(s) if String===s
|
38
|
+
super(s)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Variant
|
43
|
+
NORMAL = "normal"
|
44
|
+
BOLD = "bold"
|
45
|
+
BOLD_ITALIC = "bold-italic"
|
46
|
+
def variant=(v)
|
47
|
+
self["mathvariant"] = v
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
module Align
|
52
|
+
CENTER = "center"
|
53
|
+
LEFT = "left"
|
54
|
+
RIGHT = "right"
|
55
|
+
end
|
56
|
+
|
57
|
+
module Line
|
58
|
+
SOLID = "solid"
|
59
|
+
NONE = "none"
|
60
|
+
end
|
61
|
+
|
62
|
+
class Math < XMLElement
|
63
|
+
def initialize(display_style)
|
64
|
+
super("math", "xmlns"=>"http://www.w3.org/1998/Math/MathML")
|
65
|
+
self[:display] = display_style ? "block" : "inline"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Row < Element
|
70
|
+
def initialize
|
71
|
+
super("mrow")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class None < Element
|
76
|
+
def initialize
|
77
|
+
super("none")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Space < Element
|
82
|
+
def initialize(width)
|
83
|
+
super("mspace", "width"=>width)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Fenced < Element
|
88
|
+
attr_reader :open, :close
|
89
|
+
|
90
|
+
def initialize
|
91
|
+
super("mfenced")
|
92
|
+
end
|
93
|
+
|
94
|
+
def open=(o)
|
95
|
+
o = "" if o.to_s=="." || !o
|
96
|
+
o = "{" if o.to_s=="\\{"
|
97
|
+
self[:open] = MathML.pcstring(o, true)
|
98
|
+
end
|
99
|
+
|
100
|
+
def close=(c)
|
101
|
+
c = "" if c.to_s=="." || !c
|
102
|
+
c = "}" if c.to_s=="\\}"
|
103
|
+
self[:close] = MathML.pcstring(c, true)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class Frac < Element
|
108
|
+
def initialize(numerator, denominator)
|
109
|
+
super("mfrac")
|
110
|
+
self << numerator
|
111
|
+
self << denominator
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class SubSup < Element
|
116
|
+
attr_reader :sub, :sup, :body
|
117
|
+
|
118
|
+
def initialize(display_style, body)
|
119
|
+
super("mrow")
|
120
|
+
as_display_style if display_style
|
121
|
+
@body = body
|
122
|
+
|
123
|
+
update_name
|
124
|
+
end
|
125
|
+
|
126
|
+
def update_name
|
127
|
+
if @sub || @sup
|
128
|
+
name = "m"
|
129
|
+
name << (@sub ? (@display_style ? "under" : "sub") : "")
|
130
|
+
name << (@sup ? (@display_style ? "over" : "sup") : "")
|
131
|
+
else
|
132
|
+
name = "mrow"
|
133
|
+
end
|
134
|
+
self.name = name
|
135
|
+
end
|
136
|
+
protected :update_name
|
137
|
+
|
138
|
+
def update_contents
|
139
|
+
contents.clear
|
140
|
+
contents << @body
|
141
|
+
contents << @sub if @sub
|
142
|
+
contents << @sup if @sup
|
143
|
+
end
|
144
|
+
protected :update_contents
|
145
|
+
|
146
|
+
def sub=(sub)
|
147
|
+
@sub = sub
|
148
|
+
update_name
|
149
|
+
end
|
150
|
+
|
151
|
+
def sup=(sup)
|
152
|
+
@sup = sup
|
153
|
+
update_name
|
154
|
+
end
|
155
|
+
|
156
|
+
if Module.constants.include?("REXML") && self < REXML::Element
|
157
|
+
def write(writer=$stdout, indent=-1, transitive=false, ie_hack=true)
|
158
|
+
update_contents
|
159
|
+
super
|
160
|
+
end
|
161
|
+
elsif Module.constants.include?("EimXML") && self < EimXML::Element
|
162
|
+
def write(dst=String.new, level=0, is_head=true)
|
163
|
+
update_contents
|
164
|
+
super
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
class Over < Element
|
170
|
+
def initialize(base, over)
|
171
|
+
super("mover")
|
172
|
+
self << base << over
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
class Under < Element
|
177
|
+
def initialize(base, under)
|
178
|
+
super("munder")
|
179
|
+
self << base << under
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class Number < Element
|
184
|
+
def initialize
|
185
|
+
super("mn")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
class Identifier < Element
|
190
|
+
def initialize
|
191
|
+
super("mi")
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class Operator < Element
|
196
|
+
def initialize
|
197
|
+
super("mo")
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Text < Element
|
202
|
+
def initialize
|
203
|
+
super("mtext")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class Sqrt < Element
|
208
|
+
def initialize
|
209
|
+
super("msqrt")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class Root < Element
|
214
|
+
def initialize(index, base)
|
215
|
+
super("mroot")
|
216
|
+
self << base
|
217
|
+
self << index
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
class Table < Element
|
222
|
+
def initialize
|
223
|
+
super("mtable")
|
224
|
+
end
|
225
|
+
|
226
|
+
def set_align_attribute(name, a, default)
|
227
|
+
if a.is_a?(Array) && a.size>0
|
228
|
+
value = ""
|
229
|
+
a.each do |i|
|
230
|
+
value << " "+i
|
231
|
+
end
|
232
|
+
if value =~ /^( #{default})*$/
|
233
|
+
@attributes.delete(name)
|
234
|
+
else
|
235
|
+
@attributes[name] = value.strip
|
236
|
+
end
|
237
|
+
else
|
238
|
+
@attributes.delete(name)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def aligns=(a)
|
243
|
+
set_align_attribute("columnalign", a, Align::CENTER)
|
244
|
+
end
|
245
|
+
|
246
|
+
def vlines=(a)
|
247
|
+
set_align_attribute("columnlines", a, Line::NONE)
|
248
|
+
end
|
249
|
+
|
250
|
+
def hlines=(a)
|
251
|
+
set_align_attribute("rowlines", a, Line::NONE)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class Tr < Element
|
256
|
+
def initialize
|
257
|
+
super("mtr")
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
class Td < Element
|
262
|
+
def initialize
|
263
|
+
super("mtd")
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
module LaTeX
|
268
|
+
MBEC = /\\.|[^\\]/m
|
269
|
+
|
270
|
+
module RE
|
271
|
+
SPACE = /(?:\s|%.*$)/
|
272
|
+
NUMERICS = /(?:\.\d+)|(?:\d+(\.\d+)?)/
|
273
|
+
OPERATORS = /[,\.\+\-\*=\/\(\)\[\]<>"'|;:!]/
|
274
|
+
ALPHABETS = /[a-zA-Z]/
|
275
|
+
BLOCK = /\A\{(.*?)\}\z/m
|
276
|
+
OPTION = /\A\[(.*)\]\z/m
|
277
|
+
COMMANDS = /\\([a-zA-Z]+|[^a-zA-Z])/
|
278
|
+
WBSLASH = /\\\\/
|
279
|
+
BRACES = /\A([.|\[\]\(\)<>])\z/
|
280
|
+
end
|
281
|
+
|
282
|
+
module Font
|
283
|
+
NORMAL = 0
|
284
|
+
BOLD = 1
|
285
|
+
BLACKBOLD = 2
|
286
|
+
SCRIPT = 3
|
287
|
+
FRAKTUR = 4
|
288
|
+
ROMAN = 5
|
289
|
+
BOLD_ITALIC = 6
|
290
|
+
end
|
291
|
+
|
292
|
+
class BlockNotClosed < StandardError; end
|
293
|
+
class NotEnvironment < StandardError; end
|
294
|
+
class EnvironmentNotEnd < StandardError; end
|
295
|
+
class NeedParameter < StandardError; end
|
296
|
+
class EndMismatchToBegin < StandardError; end
|
297
|
+
class OptionNotClosed < StandardError; end
|
298
|
+
|
299
|
+
class Scanner < StringScanner
|
300
|
+
def done
|
301
|
+
self.string[0, pos]
|
302
|
+
end
|
303
|
+
|
304
|
+
def scan_space
|
305
|
+
_scan(/#{RE::SPACE}+/)
|
306
|
+
end
|
307
|
+
|
308
|
+
def skip_space_and(check_mode)
|
309
|
+
opos = pos
|
310
|
+
scan_space
|
311
|
+
r = yield
|
312
|
+
self.pos = opos if check_mode || !r
|
313
|
+
r
|
314
|
+
end
|
315
|
+
|
316
|
+
alias :_check :check
|
317
|
+
def check(re)
|
318
|
+
skip_space_and(true){_check(re)}
|
319
|
+
end
|
320
|
+
|
321
|
+
alias :_scan :scan
|
322
|
+
def scan(re)
|
323
|
+
skip_space_and(false){_scan(re)}
|
324
|
+
end
|
325
|
+
|
326
|
+
alias :_eos? :eos?
|
327
|
+
def eos?
|
328
|
+
_eos? || _check(/#{RE::SPACE}+\z/)
|
329
|
+
end
|
330
|
+
|
331
|
+
def check_command
|
332
|
+
check(RE::COMMANDS)
|
333
|
+
end
|
334
|
+
|
335
|
+
def scan_command
|
336
|
+
scan(RE::COMMANDS)
|
337
|
+
end
|
338
|
+
|
339
|
+
def peek_command
|
340
|
+
check_command ? self[1] : nil
|
341
|
+
end
|
342
|
+
|
343
|
+
def check_block
|
344
|
+
skip_space_and(true){scan_block}
|
345
|
+
end
|
346
|
+
|
347
|
+
def scan_block
|
348
|
+
return nil unless scan(/\{/)
|
349
|
+
block = "{"
|
350
|
+
bpos = pos-1
|
351
|
+
nest = 1
|
352
|
+
while _scan(/(#{MBEC}*?)([\{\}])/)
|
353
|
+
block << matched
|
354
|
+
case self[2]
|
355
|
+
when "{"
|
356
|
+
nest+=1
|
357
|
+
when "}"
|
358
|
+
nest-=1
|
359
|
+
break if nest==0
|
360
|
+
end
|
361
|
+
end
|
362
|
+
if nest>0
|
363
|
+
self.pos = bpos
|
364
|
+
raise BlockNotClosed
|
365
|
+
end
|
366
|
+
self.pos = bpos
|
367
|
+
_scan(/\A\{(#{Regexp.escape(block[RE::BLOCK, 1].to_s)})\}/)
|
368
|
+
end
|
369
|
+
|
370
|
+
def check_any(remain_space=false)
|
371
|
+
skip_space_and(true){scan_any(remain_space)}
|
372
|
+
end
|
373
|
+
|
374
|
+
def scan_any(remain_space=false)
|
375
|
+
p = pos
|
376
|
+
scan_space
|
377
|
+
r = remain_space ? matched.to_s : ""
|
378
|
+
case
|
379
|
+
when s = scan_block
|
380
|
+
when s = scan_command
|
381
|
+
else
|
382
|
+
unless _scan(/./) || remain_space
|
383
|
+
self.pos = p
|
384
|
+
return nil
|
385
|
+
end
|
386
|
+
s = matched.to_s
|
387
|
+
end
|
388
|
+
r << s
|
389
|
+
end
|
390
|
+
|
391
|
+
def scan_option
|
392
|
+
return nil unless scan(/\[/)
|
393
|
+
opt = "["
|
394
|
+
p = pos-1
|
395
|
+
until (s=scan_any(true)) =~ /\A#{RE::SPACE}*\]\z/
|
396
|
+
opt << s
|
397
|
+
if eos?
|
398
|
+
self.pos = p
|
399
|
+
raise OptionNotClosed
|
400
|
+
end
|
401
|
+
end
|
402
|
+
opt << s
|
403
|
+
self.pos = p
|
404
|
+
_scan(/\A\[(#{Regexp.escape(opt[RE::OPTION, 1].to_s)})\]/)
|
405
|
+
end
|
406
|
+
|
407
|
+
def check_option
|
408
|
+
skip_space_and(true){scan_option}
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
class ParseError < StandardError
|
413
|
+
attr_accessor :rest, :done
|
414
|
+
def initialize(message, rest = "", done = "")
|
415
|
+
@done = done
|
416
|
+
@rest = rest
|
417
|
+
super(message)
|
418
|
+
end
|
419
|
+
|
420
|
+
def inspect
|
421
|
+
"#{message} : '#{@done}' / '#{@rest}'\n"+backtrace[0..5].join("\n")
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
class Macro
|
426
|
+
class Command
|
427
|
+
attr_reader :num, :body, :option
|
428
|
+
def initialize(n, b, o)
|
429
|
+
@num = n
|
430
|
+
@body = b
|
431
|
+
@option = o
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
class Environment
|
436
|
+
attr_reader :num, :beginning, :ending, :option
|
437
|
+
def initialize(n, b, e, o)
|
438
|
+
@num = n
|
439
|
+
@beginning = b
|
440
|
+
@ending = e
|
441
|
+
@option = o
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def initialize
|
446
|
+
@commands = Hash.new
|
447
|
+
@environments = Hash.new
|
448
|
+
end
|
449
|
+
|
450
|
+
def parse_error(message, rest="", whole=nil)
|
451
|
+
rest = whole[/\A.*?(#{Regexp.escape(rest)}.*\z)/, 1] if whole
|
452
|
+
rest << @scanner.rest
|
453
|
+
done = @scanner.string[0, @scanner.string.size-rest.size]
|
454
|
+
ParseError.new(message, rest, done)
|
455
|
+
end
|
456
|
+
|
457
|
+
def parse(src)
|
458
|
+
@scanner = Scanner.new(src)
|
459
|
+
until @scanner.eos?
|
460
|
+
unless @scanner.scan_command
|
461
|
+
@scanner.scan_space
|
462
|
+
raise parse_error("Syntax error.")
|
463
|
+
end
|
464
|
+
case @scanner[1]
|
465
|
+
when "newcommand"
|
466
|
+
parse_newcommand
|
467
|
+
when "newenvironment"
|
468
|
+
parse_newenvironment
|
469
|
+
else
|
470
|
+
raise parse_error("Syntax error.", @scanner.matched)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
rescue BlockNotClosed => e
|
474
|
+
raise parse_error("Block not closed.")
|
475
|
+
rescue OptionNotClosed => e
|
476
|
+
raise parse_error("Option not closed.")
|
477
|
+
end
|
478
|
+
|
479
|
+
def scan_num_of_parameter
|
480
|
+
if @scanner.scan_option
|
481
|
+
raise parse_error("Need positive number.", @scanner[1]+"]") unless @scanner[1]=~/\A#{RE::SPACE}*\d+#{RE::SPACE}*\z/
|
482
|
+
@scanner[1].to_i
|
483
|
+
else
|
484
|
+
0
|
485
|
+
end
|
486
|
+
end
|
487
|
+
|
488
|
+
def check_parameter_numbers(src, opt, whole)
|
489
|
+
s = Scanner.new(src)
|
490
|
+
until s.eos?
|
491
|
+
case
|
492
|
+
when s.scan(/#{MBEC}*?\#(\d+|.)/)
|
493
|
+
raise parse_error("Need positive number.") unless s[1]=~/\d+/
|
494
|
+
raise parse_error("Parameter \# too large.", s[1]+s.rest, whole) if s[1].to_i>opt
|
495
|
+
else
|
496
|
+
return nil
|
497
|
+
end
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
def parse_newcommand
|
502
|
+
case
|
503
|
+
when @scanner.scan_block
|
504
|
+
s = Scanner.new(@scanner[1])
|
505
|
+
raise parse_error("Need newcommand.", s.rest+"}") unless s.scan_command
|
506
|
+
com = s[1]
|
507
|
+
raise parse_error("Syntax error." ,s.rest+"}") unless s.eos?
|
508
|
+
when @scanner.scan_command
|
509
|
+
s = Scanner.new(@scanner[1])
|
510
|
+
com = s.scan_command
|
511
|
+
else
|
512
|
+
raise parse_error("Need newcommand.")
|
513
|
+
end
|
514
|
+
|
515
|
+
optnum = scan_num_of_parameter
|
516
|
+
opt = @scanner.scan_option ? @scanner[1] : nil
|
517
|
+
|
518
|
+
case
|
519
|
+
when @scanner.scan_block
|
520
|
+
body = @scanner[1]
|
521
|
+
when @scanner.scan_command
|
522
|
+
body = @scanner.matched
|
523
|
+
else
|
524
|
+
body = @scanner.scan(/./)
|
525
|
+
end
|
526
|
+
|
527
|
+
raise parse_error("Need parameter.") unless body
|
528
|
+
|
529
|
+
check_parameter_numbers(body, optnum, @scanner.matched)
|
530
|
+
|
531
|
+
optnum-=1 if opt
|
532
|
+
@commands[com] = Command.new(optnum, body, opt)
|
533
|
+
end
|
534
|
+
|
535
|
+
def parse_newenvironment
|
536
|
+
case
|
537
|
+
when @scanner.scan_block
|
538
|
+
env = @scanner[1]
|
539
|
+
when @scanner.scan_command
|
540
|
+
raise ParseError.new
|
541
|
+
when @scanner.scan(/./)
|
542
|
+
env = @scanner.matched
|
543
|
+
end
|
544
|
+
raise parse_error("Syntax error.", env[/\A.*?(\\.*\z)/, 1], @scanner.matched) if env=~/\\/
|
545
|
+
|
546
|
+
optnum = scan_num_of_parameter
|
547
|
+
opt = @scanner.scan_option ? @scanner[1] : nil
|
548
|
+
|
549
|
+
b = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
|
550
|
+
raise parse_error("Need begin block.") unless b
|
551
|
+
check_parameter_numbers(b, optnum, @scanner.matched)
|
552
|
+
e = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
|
553
|
+
raise parse_error("Need end block.") unless e
|
554
|
+
check_parameter_numbers(e, optnum, @scanner.matched)
|
555
|
+
|
556
|
+
optnum -= 1 if opt
|
557
|
+
@environments[env] = Environment.new(optnum, b, e, opt)
|
558
|
+
end
|
559
|
+
|
560
|
+
def commands(com)
|
561
|
+
@commands[com]
|
562
|
+
end
|
563
|
+
|
564
|
+
def expand_command(com, params, opt=nil)
|
565
|
+
return nil unless @commands.has_key?(com)
|
566
|
+
c = @commands[com]
|
567
|
+
opt = c.option if c.option && !opt
|
568
|
+
params.unshift(opt) if c.option
|
569
|
+
raise ParseError.new("Need more parameter.") if params.size < c.num
|
570
|
+
|
571
|
+
c.body.gsub(/(#{MBEC}*?)\#(\d+)/) do
|
572
|
+
$1.to_s << params[$2.to_i-1]
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
def environments(env)
|
577
|
+
@environments[env]
|
578
|
+
end
|
579
|
+
|
580
|
+
def expand_environment(env, body, params, opt=nil)
|
581
|
+
return nil unless @environments.has_key?(env)
|
582
|
+
e = @environments[env]
|
583
|
+
opt = e.option if e.option && !opt
|
584
|
+
params.unshift(opt) if e.option
|
585
|
+
raise ParseError.new("Need more parameter.") if params.size < e.num
|
586
|
+
|
587
|
+
bg = e.beginning.gsub(/(#{MBEC}*?)\#(\d+)/) do
|
588
|
+
$1.to_s << params[$2.to_i-1]
|
589
|
+
end
|
590
|
+
|
591
|
+
en = e.ending.gsub(/(#{MBEC}*?)\#(\d+)/) do
|
592
|
+
$1.to_s << params[$2.to_i-1]
|
593
|
+
end
|
594
|
+
|
595
|
+
" #{bg} #{body} #{en} "
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
module BuiltinCommands; end
|
600
|
+
module BuiltinGroups; end
|
601
|
+
module BuiltinEnvironments; end
|
602
|
+
|
603
|
+
class Parser
|
604
|
+
class CircularReferenceCommand < StandardError; end
|
605
|
+
|
606
|
+
include LaTeX
|
607
|
+
|
608
|
+
include BuiltinEnvironments
|
609
|
+
include BuiltinGroups
|
610
|
+
include BuiltinCommands
|
611
|
+
|
612
|
+
BUILTIN_MACRO = <<'EOS'
|
613
|
+
\newenvironment{smallmatrix}{\begin{matrix}}{\end{matrix}}
|
614
|
+
\newenvironment{pmatrix}{\left(\begin{matrix}}{\end{matrix}\right)}
|
615
|
+
\newenvironment{bmatrix}{\left[\begin{matrix}}{\end{matrix}\right]}
|
616
|
+
\newenvironment{Bmatrix}{\left\{\begin{matrix}}{\end{matrix}\right\}}
|
617
|
+
\newenvironment{vmatrix}{\left|\begin{matrix}}{\end{matrix}\right|}
|
618
|
+
\newenvironment{Vmatrix}{\left\|\begin{matrix}}{\end{matrix}\right\|}
|
619
|
+
EOS
|
620
|
+
|
621
|
+
attr_accessor :unsecure_entity
|
622
|
+
attr_reader :macro
|
623
|
+
def initialize
|
624
|
+
@unsecure_entity = false
|
625
|
+
@entities = Hash.new
|
626
|
+
@commands = Hash.new
|
627
|
+
@symbols = Hash.new
|
628
|
+
@delimiters = Array.new
|
629
|
+
@group_begins = Hash.new
|
630
|
+
@group_ends = Hash.new
|
631
|
+
@macro = Macro.new
|
632
|
+
@macro.parse(BUILTIN_MACRO)
|
633
|
+
@expanded_command = Array.new
|
634
|
+
@expanded_environment = Array.new
|
635
|
+
|
636
|
+
super
|
637
|
+
end
|
638
|
+
|
639
|
+
def add_entity(list)
|
640
|
+
list.each do |i|
|
641
|
+
@entities[i] = true
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
def parse(src, displaystyle=false)
|
646
|
+
@ds = displaystyle
|
647
|
+
begin
|
648
|
+
parse_into(src, Math.new(@ds), Font::NORMAL)
|
649
|
+
rescue ParseError => e
|
650
|
+
e.done = src[0...(src.size - e.rest.size)]
|
651
|
+
raise
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
def push_container(container, scanner=@scanner, font=@font)
|
656
|
+
data = [@container, @scanner, @font]
|
657
|
+
@container, @scanner, @font = [container, scanner, font]
|
658
|
+
begin
|
659
|
+
yield container
|
660
|
+
container
|
661
|
+
ensure
|
662
|
+
@container, @scanner, @font = data
|
663
|
+
end
|
664
|
+
end
|
665
|
+
|
666
|
+
def add_plugin(plugin)
|
667
|
+
self.extend(plugin)
|
668
|
+
end
|
669
|
+
|
670
|
+
def add_commands(*a)
|
671
|
+
if a.size==1 && Hash===a[0]
|
672
|
+
@commands.merge!(a[0])
|
673
|
+
else
|
674
|
+
a.each{|i| @commands[i] = false}
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
def add_multi_command(m, *a)
|
679
|
+
a.each{|i| @commands[i] = m}
|
680
|
+
end
|
681
|
+
|
682
|
+
def add_sym_cmd(hash)
|
683
|
+
@symbols.merge!(hash)
|
684
|
+
end
|
685
|
+
|
686
|
+
def add_delimiter(list)
|
687
|
+
@delimiters.concat(list)
|
688
|
+
end
|
689
|
+
|
690
|
+
def add_group(begin_name, end_name, method=nil)
|
691
|
+
@group_begins[begin_name] = method
|
692
|
+
@group_ends[end_name] = begin_name
|
693
|
+
end
|
694
|
+
|
695
|
+
private
|
696
|
+
def parse_into(src, parent, font=nil)
|
697
|
+
orig = [@scanner, @container, @font, @ds]
|
698
|
+
@scanner = Scanner.new(src)
|
699
|
+
@container = parent
|
700
|
+
@font = font if font
|
701
|
+
begin
|
702
|
+
until @scanner.eos?
|
703
|
+
@container << parse_to_element(true)
|
704
|
+
end
|
705
|
+
@container
|
706
|
+
rescue BlockNotClosed => e
|
707
|
+
raise ParseError.new("Block not closed.", @scanner.rest)
|
708
|
+
rescue NotEnvironment => e
|
709
|
+
raise ParseError.new("Not environment.", @scanner.rest)
|
710
|
+
rescue EnvironmentNotEnd => e
|
711
|
+
raise ParseError.new("Environment not end.", @scanner.rest)
|
712
|
+
rescue OptionNotClosed => e
|
713
|
+
raise ParseError.new("Option not closed.", @scanner.rest)
|
714
|
+
rescue ParseError => e
|
715
|
+
e.rest << @scanner.rest.to_s
|
716
|
+
raise
|
717
|
+
ensure
|
718
|
+
@scanner, @container, @font, @ds = orig
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
def parse_any(message = "Syntax error.")
|
723
|
+
raise ParseError.new(message) unless @scanner.scan_any
|
724
|
+
s = @scanner
|
725
|
+
@scanner = Scanner.new(@scanner.matched)
|
726
|
+
begin
|
727
|
+
parse_to_element
|
728
|
+
ensure
|
729
|
+
@scanner = s
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
def parse_to_element(whole_group = false)
|
734
|
+
if whole_group && @group_begins.has_key?(@scanner.peek_command)
|
735
|
+
@scanner.scan_command
|
736
|
+
parse_group
|
737
|
+
else
|
738
|
+
case
|
739
|
+
when @scanner.scan(RE::NUMERICS)
|
740
|
+
parse_num
|
741
|
+
when @scanner.scan(RE::ALPHABETS)
|
742
|
+
parse_char
|
743
|
+
when @scanner.scan(RE::OPERATORS)
|
744
|
+
parse_operator
|
745
|
+
when @scanner.scan_block
|
746
|
+
parse_block
|
747
|
+
when @scanner.scan(/_/)
|
748
|
+
parse_sub
|
749
|
+
when @scanner.scan(/\^/)
|
750
|
+
parse_sup
|
751
|
+
when @scanner.scan_command
|
752
|
+
parse_command
|
753
|
+
else
|
754
|
+
raise ParseError.new('Syntax error.')
|
755
|
+
end
|
756
|
+
end
|
757
|
+
end
|
758
|
+
|
759
|
+
def parse_num
|
760
|
+
n = Number.new
|
761
|
+
n.extend(Variant).variant = Variant::BOLD if @font==Font::BOLD
|
762
|
+
n << @scanner.matched
|
763
|
+
end
|
764
|
+
|
765
|
+
def parse_char
|
766
|
+
c = @scanner.matched
|
767
|
+
i = Identifier.new
|
768
|
+
case @font
|
769
|
+
when Font::ROMAN
|
770
|
+
i.extend(Variant).variant = Variant::NORMAL
|
771
|
+
when Font::BOLD
|
772
|
+
i.extend(Variant).variant = Variant::BOLD
|
773
|
+
when Font::BOLD_ITALIC
|
774
|
+
i.extend(Variant).variant = Variant::BOLD_ITALIC
|
775
|
+
when Font::BLACKBOLD
|
776
|
+
c = MathML.pcstring(%Q[&#{c}opf;], true)
|
777
|
+
when Font::SCRIPT
|
778
|
+
c = MathML.pcstring(%Q[&#{c}scr;], true)
|
779
|
+
when Font::FRAKTUR
|
780
|
+
c = MathML.pcstring(%Q[&#{c}fr;], true)
|
781
|
+
end
|
782
|
+
i << c
|
783
|
+
end
|
784
|
+
|
785
|
+
def parse_operator
|
786
|
+
o = @scanner.matched
|
787
|
+
Operator.new << o
|
788
|
+
end
|
789
|
+
|
790
|
+
def parse_block
|
791
|
+
os = @scanner
|
792
|
+
@scanner = Scanner.new(@scanner[1])
|
793
|
+
begin
|
794
|
+
push_container(Row.new) do |r|
|
795
|
+
r << parse_to_element(true) until @scanner.eos?
|
796
|
+
end
|
797
|
+
rescue ParseError => e
|
798
|
+
e.rest << '}'
|
799
|
+
raise
|
800
|
+
ensure
|
801
|
+
@scanner = os
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
805
|
+
def parse_sub
|
806
|
+
e = @container.pop
|
807
|
+
e = None.new unless e
|
808
|
+
e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
|
809
|
+
raise ParseError.new("Double subscript.", "_") if e.sub
|
810
|
+
e.sub = parse_any("Subscript not exist.")
|
811
|
+
e
|
812
|
+
end
|
813
|
+
|
814
|
+
def parse_sup
|
815
|
+
e = @container.pop
|
816
|
+
e = None.new unless e
|
817
|
+
e = SubSup.new(@ds && e.display_style, e) unless e.is_a?(SubSup)
|
818
|
+
raise ParseError.new("Double superscript.", "^") if e.sup
|
819
|
+
e.sup = parse_any("Superscript not exist.")
|
820
|
+
e
|
821
|
+
end
|
822
|
+
|
823
|
+
def entitize(str)
|
824
|
+
MathML.pcstring(str.sub(/^(.*)$/){"&#{$1};"}, true)
|
825
|
+
end
|
826
|
+
|
827
|
+
def parse_symbol_command(com, plain=false)
|
828
|
+
unless @symbols.include?(com)
|
829
|
+
@scanner.pos = @scanner.pos-(com.size+1)
|
830
|
+
raise ParseError.new("Undefined command.")
|
831
|
+
end
|
832
|
+
data = @symbols[com]
|
833
|
+
return nil unless data
|
834
|
+
|
835
|
+
su = data[0]
|
836
|
+
el = data[1]
|
837
|
+
el = :o unless el
|
838
|
+
s = data[2]
|
839
|
+
s = com.dup.untaint.to_sym unless s
|
840
|
+
s = com if s.is_a?(String) && s.length==0
|
841
|
+
|
842
|
+
case el
|
843
|
+
when :I
|
844
|
+
el = Identifier.new
|
845
|
+
when :i
|
846
|
+
el = Identifier.new
|
847
|
+
el.extend(Variant).variant = Variant::NORMAL unless s.is_a?(String)&&s.length>1
|
848
|
+
when :o
|
849
|
+
el = Operator.new
|
850
|
+
when :n
|
851
|
+
el = Number.new
|
852
|
+
else
|
853
|
+
raise ParseError.new("Inner data broken.")
|
854
|
+
end
|
855
|
+
|
856
|
+
case s
|
857
|
+
when String
|
858
|
+
when Fixnum
|
859
|
+
s = "&\#x#{s.to_s(16)};"
|
860
|
+
when Symbol
|
861
|
+
s = "&#{s.to_s};"
|
862
|
+
end
|
863
|
+
|
864
|
+
return s if plain
|
865
|
+
el << MathML.pcstring(s, true)
|
866
|
+
el.as_display_style if su==:u
|
867
|
+
el
|
868
|
+
end
|
869
|
+
|
870
|
+
def parse_command
|
871
|
+
com = @scanner[1]
|
872
|
+
matched = @scanner.matched
|
873
|
+
pos = @scanner.pos-matched.size
|
874
|
+
macro = @macro.commands(com)
|
875
|
+
if macro
|
876
|
+
begin
|
877
|
+
flg = @expanded_command.include?(com)
|
878
|
+
@expanded_command.push(com)
|
879
|
+
raise CircularReferenceCommand if flg
|
880
|
+
option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
|
881
|
+
params = Array.new
|
882
|
+
(1..macro.num).each do
|
883
|
+
params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
|
884
|
+
raise ParseError.new("Need more parameter.") unless params.last
|
885
|
+
end
|
886
|
+
r = parse_into(@macro.expand_command(com, params, option), Array.new)
|
887
|
+
return r
|
888
|
+
rescue CircularReferenceCommand
|
889
|
+
if @expanded_command.size>1
|
890
|
+
raise
|
891
|
+
else
|
892
|
+
@scanner.pos = pos
|
893
|
+
raise ParseError.new("Circular reference.")
|
894
|
+
end
|
895
|
+
rescue ParseError => e
|
896
|
+
if @expanded_command.size>1
|
897
|
+
raise
|
898
|
+
else
|
899
|
+
@scanner.pos = pos
|
900
|
+
raise ParseError.new(%[Error in macro(#{e.message} "#{e.rest.strip}").])
|
901
|
+
end
|
902
|
+
ensure
|
903
|
+
@expanded_command.pop
|
904
|
+
end
|
905
|
+
elsif @commands.key?(com)
|
906
|
+
m = @commands[com]
|
907
|
+
m = com unless m
|
908
|
+
return __send__("cmd_#{m.to_s}")
|
909
|
+
end
|
910
|
+
parse_symbol_command(com)
|
911
|
+
end
|
912
|
+
|
913
|
+
def parse_mathfont(font)
|
914
|
+
f = @font
|
915
|
+
@font = font
|
916
|
+
begin
|
917
|
+
push_container(Row.new){|r| r << parse_any}
|
918
|
+
ensure
|
919
|
+
@font = f
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
923
|
+
def parse_group
|
924
|
+
font = @font
|
925
|
+
begin
|
926
|
+
g = @group_begins[@scanner[1]]
|
927
|
+
g = @scanner[1] unless g
|
928
|
+
__send__("grp_#{g.to_s}")
|
929
|
+
ensure
|
930
|
+
@font = font
|
931
|
+
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
module BuiltinCommands
|
936
|
+
OVERS = {'hat'=>'circ', 'breve'=>'smile', 'grave'=>'grave',
|
937
|
+
'acute'=>'acute', 'dot'=>'sdot', 'ddot'=>'nldr', 'tilde'=>'tilde',
|
938
|
+
'bar'=>'macr', 'vec'=>'rightarrow', 'check'=>'vee', 'widehat'=>'circ',
|
939
|
+
'overline'=>'macr', 'widetilde'=>'tilde', 'overbrace'=>'OverBrace'}
|
940
|
+
UNDERS = {'underbrace'=>'UnderBrace', 'underline'=>'macr'}
|
941
|
+
|
942
|
+
def initialize
|
943
|
+
add_commands("\\"=>:backslash)
|
944
|
+
add_commands("entity", "stackrel", "frac", "sqrt", "mbox")
|
945
|
+
add_multi_command(:hat_etc, 'hat', 'breve', 'grave', 'acute', 'dot', 'ddot', 'tilde', 'bar', 'vec', 'check', 'widehat', 'overline', 'widetilde', 'overbrace')
|
946
|
+
add_multi_command(:underbrace_etc, 'underbrace', 'underline')
|
947
|
+
add_multi_command(:quad_etc, "quad", "qquad", ",", ":", ";")
|
948
|
+
add_multi_command(:it_etc, "it", "rm", "bf")
|
949
|
+
add_multi_command(:mathit_etc, "mathit", "mathrm", "mathbf", "bm", "mathbb", "mathscr", "mathfrak")
|
950
|
+
add_sym_cmd(SymbolCommands)
|
951
|
+
add_delimiter(Delimiters)
|
952
|
+
|
953
|
+
super
|
954
|
+
end
|
955
|
+
|
956
|
+
def cmd_backslash
|
957
|
+
@ds ? nil : XMLElement.new("br", "xmlns"=>"http://www.w3.org/1999/xhtml")
|
958
|
+
end
|
959
|
+
|
960
|
+
def cmd_hat_etc
|
961
|
+
com = @scanner[1]
|
962
|
+
Over.new(parse_any, Operator.new << entitize(OVERS[com]))
|
963
|
+
end
|
964
|
+
|
965
|
+
def cmd_underbrace_etc
|
966
|
+
com = @scanner[1]
|
967
|
+
Under.new(parse_any, Operator.new << entitize(UNDERS[com]))
|
968
|
+
end
|
969
|
+
|
970
|
+
def cmd_entity
|
971
|
+
param = @scanner.scan_block ? @scanner[1] : @scanner.scan(/./)
|
972
|
+
raise ParseError.new("Need parameter.") unless param
|
973
|
+
unless @unsecure_entity || @entities[param]
|
974
|
+
param =@scanner.matched[/\A\{#{RE::SPACE}*(.*\})\z/, 1] if @scanner.matched=~RE::BLOCK
|
975
|
+
@scanner.pos = @scanner.pos-(param.size)
|
976
|
+
raise ParseError.new("Unregistered entity.")
|
977
|
+
end
|
978
|
+
Operator.new << entitize(param)
|
979
|
+
end
|
980
|
+
|
981
|
+
def cmd_stackrel
|
982
|
+
o = parse_any; b = parse_any
|
983
|
+
Over.new(b, o)
|
984
|
+
end
|
985
|
+
|
986
|
+
def cmd_quad_etc
|
987
|
+
case @scanner[1]
|
988
|
+
when 'quad'
|
989
|
+
Space.new("1em")
|
990
|
+
when 'qquad'
|
991
|
+
Space.new("2em")
|
992
|
+
when ','
|
993
|
+
Space.new("0.167em")
|
994
|
+
when ':'
|
995
|
+
Space.new("0.222em")
|
996
|
+
when ';'
|
997
|
+
Space.new("0.278em")
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
|
1001
|
+
def cmd_it_etc
|
1002
|
+
case @scanner[1]
|
1003
|
+
when 'it'
|
1004
|
+
@font = Font::NORMAL
|
1005
|
+
when 'rm'
|
1006
|
+
@font = Font::ROMAN
|
1007
|
+
when 'bf'
|
1008
|
+
@font = Font::BOLD
|
1009
|
+
end
|
1010
|
+
nil
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def cmd_mathit_etc
|
1014
|
+
case @scanner[1]
|
1015
|
+
when 'mathit'
|
1016
|
+
parse_mathfont(Font::NORMAL)
|
1017
|
+
when 'mathrm'
|
1018
|
+
parse_mathfont(Font::ROMAN)
|
1019
|
+
when 'mathbf'
|
1020
|
+
parse_mathfont(Font::BOLD)
|
1021
|
+
when 'bm'
|
1022
|
+
parse_mathfont(Font::BOLD_ITALIC)
|
1023
|
+
when 'mathbb'
|
1024
|
+
parse_mathfont(Font::BLACKBOLD)
|
1025
|
+
when 'mathscr'
|
1026
|
+
parse_mathfont(Font::SCRIPT)
|
1027
|
+
when 'mathfrak'
|
1028
|
+
parse_mathfont(Font::FRAKTUR)
|
1029
|
+
end
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
def cmd_frac
|
1033
|
+
n = parse_any; d = parse_any
|
1034
|
+
Frac.new(n, d)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
def cmd_sqrt
|
1038
|
+
if @scanner.scan_option
|
1039
|
+
i = parse_into(@scanner[1], Array.new)
|
1040
|
+
i = i.size==1 ? i[0] : (Row.new << i)
|
1041
|
+
b = parse_any
|
1042
|
+
Root.new(i, b)
|
1043
|
+
else
|
1044
|
+
Sqrt.new << parse_any
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
def cmd_mbox
|
1049
|
+
@scanner.scan_any
|
1050
|
+
Text.new << (@scanner.matched =~ RE::BLOCK ? @scanner[1] : @scanner.matched)
|
1051
|
+
end
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
module BuiltinGroups
|
1055
|
+
class CircularReferenceEnvironment < StandardError; end
|
1056
|
+
|
1057
|
+
def initialize
|
1058
|
+
add_group("begin", "end")
|
1059
|
+
add_group("left", "right", :left_etc)
|
1060
|
+
add_group("bigg", "bigg", :left_etc)
|
1061
|
+
@environments = Hash.new
|
1062
|
+
|
1063
|
+
super
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
def add_environment(*a)
|
1067
|
+
@environments = Hash.new unless @environments
|
1068
|
+
if a.size==1 && Hash===a[0]
|
1069
|
+
@environments.merge!(hash)
|
1070
|
+
else
|
1071
|
+
a.each{|i| @environments[i] = false}
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
def grp_begin
|
1076
|
+
matched = @scanner.matched
|
1077
|
+
begin_pos = @scanner.pos-matched.size
|
1078
|
+
en = @scanner.scan_block ? @scanner[1] : @scanner.scan_any
|
1079
|
+
raise ParseError.new('Environment name not exist.') unless en
|
1080
|
+
|
1081
|
+
macro = @macro.environments(en)
|
1082
|
+
if macro
|
1083
|
+
begin
|
1084
|
+
flg = @expanded_environment.include?(en)
|
1085
|
+
@expanded_environment.push(en)
|
1086
|
+
raise CircularReferenceEnvironment if flg
|
1087
|
+
|
1088
|
+
pos = @scanner.pos
|
1089
|
+
option = (macro.option && @scanner.scan_option) ? @scanner[1] : nil
|
1090
|
+
params = Array.new
|
1091
|
+
(1..macro.num).each do
|
1092
|
+
params << (@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
|
1093
|
+
raise ParseError.new("Need more parameter.") unless params.last
|
1094
|
+
end
|
1095
|
+
body = ""
|
1096
|
+
grpnest = 0
|
1097
|
+
until @scanner.peek_command=="end" && grpnest==0
|
1098
|
+
if @scanner.eos?
|
1099
|
+
@scanner.pos = pos
|
1100
|
+
raise ParseError.new('Matching \end not exist.')
|
1101
|
+
end
|
1102
|
+
com = @scanner.peek_command
|
1103
|
+
grpnest += 1 if @group_begins.has_key?(com)
|
1104
|
+
grpnest -=1 if @group_ends.has_key?(com) && @group_begins[com]
|
1105
|
+
raise ParseError.new("Syntax error.") if grpnest<0
|
1106
|
+
|
1107
|
+
body << @scanner.scan_any(true)
|
1108
|
+
end
|
1109
|
+
@scanner.scan_command
|
1110
|
+
raise ParseError.new("Environment mismatched.", @scanner.matched) unless en==(@scanner.scan_block ? @scanner[1] : @scanner.scan_any)
|
1111
|
+
begin
|
1112
|
+
return parse_into(@macro.expand_environment(en, body, params, option), Array.new)
|
1113
|
+
rescue CircularReferenceEnvironment
|
1114
|
+
if @expanded_environment.size>1
|
1115
|
+
raise
|
1116
|
+
else
|
1117
|
+
@scanner.pos = begin_pos
|
1118
|
+
raise ParseError.new("Circular reference.")
|
1119
|
+
end
|
1120
|
+
rescue ParseError => e
|
1121
|
+
if @expanded_environment.size>1
|
1122
|
+
raise
|
1123
|
+
else
|
1124
|
+
@scanner.pos = begin_pos
|
1125
|
+
raise ParseError.new(%[Error in macro(#{e.message} "#{e.rest.strip}").])
|
1126
|
+
end
|
1127
|
+
end
|
1128
|
+
ensure
|
1129
|
+
@expanded_environment.pop
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
raise ParseError.new("Undefined environment.") unless @environments.has_key?(en)
|
1134
|
+
e = @environments[en]
|
1135
|
+
e = en unless e # default method name
|
1136
|
+
__send__("env_#{e.to_s}")
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
def grp_left_etc
|
1140
|
+
right =
|
1141
|
+
case @scanner[1]
|
1142
|
+
when "left"
|
1143
|
+
"right"
|
1144
|
+
when "bigg"
|
1145
|
+
"bigg"
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
f = Fenced.new
|
1149
|
+
p = @scanner.pos
|
1150
|
+
o = @scanner.scan_any
|
1151
|
+
raise ParseError.new('Need brace here.') unless o && (o=~RE::BRACES || @delimiters.include?(o[RE::COMMANDS, 1]))
|
1152
|
+
|
1153
|
+
f.open = (o=~RE::BRACES ? o : parse_symbol_command(o[RE::COMMANDS, 1], true))
|
1154
|
+
f << push_container(Row.new) do |r|
|
1155
|
+
until @scanner.peek_command==right
|
1156
|
+
if @scanner.eos?
|
1157
|
+
@scanner.pos = p
|
1158
|
+
raise ParseError.new('Brace not closed.')
|
1159
|
+
end
|
1160
|
+
r << parse_to_element(true)
|
1161
|
+
end
|
1162
|
+
end
|
1163
|
+
@scanner.scan_command # skip right
|
1164
|
+
c = @scanner.scan_any
|
1165
|
+
raise ParseError.new('Need brace here.') unless c=~RE::BRACES || @delimiters.include?(c[RE::COMMANDS, 1])
|
1166
|
+
f.close = (c=~RE::BRACES ? c : parse_symbol_command(c[RE::COMMANDS, 1], true))
|
1167
|
+
f
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
module BuiltinEnvironments
|
1172
|
+
def initialize
|
1173
|
+
add_environment("array", "matrix")
|
1174
|
+
|
1175
|
+
super
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
def env_array
|
1179
|
+
layout = @scanner.scan_block ? @scanner.matched : @scanner.scan(/./)
|
1180
|
+
l = Scanner.new(layout=~RE::BLOCK ? layout[RE::BLOCK, 1] : layout)
|
1181
|
+
t = Table.new
|
1182
|
+
aligns = Array.new
|
1183
|
+
vlines = Array.new
|
1184
|
+
vlined = l.check(/\|/)
|
1185
|
+
columned = false
|
1186
|
+
until l.eos?
|
1187
|
+
c = l.scan_any
|
1188
|
+
raise ParseError.new("Syntax error.", layout[/\A.*(#{Regexp.escape(c+l.rest)}.*\z)/m, 1]) unless c=~/[clr\|@]/
|
1189
|
+
|
1190
|
+
if c=='|'
|
1191
|
+
aligns << Align::CENTER if vlined
|
1192
|
+
vlines << Line::SOLID
|
1193
|
+
vlined = true
|
1194
|
+
columned = false
|
1195
|
+
else
|
1196
|
+
vlines << Line::NONE if columned
|
1197
|
+
vlined = false
|
1198
|
+
columned = true
|
1199
|
+
case c
|
1200
|
+
when 'l'
|
1201
|
+
aligns << Align::LEFT
|
1202
|
+
when 'c'
|
1203
|
+
aligns << Align::CENTER
|
1204
|
+
when 'r'
|
1205
|
+
aligns << Align::RIGHT
|
1206
|
+
when '@'
|
1207
|
+
aligns << Align::CENTER
|
1208
|
+
l.scan_any
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
end
|
1212
|
+
t.aligns = aligns
|
1213
|
+
t.vlines = vlines
|
1214
|
+
|
1215
|
+
layout = layout[RE::BLOCK, 1] if layout=~RE::BLOCK
|
1216
|
+
raise ParseError.new('Need parameter here.') if layout==""
|
1217
|
+
|
1218
|
+
hlines = Array.new
|
1219
|
+
row_parsed = false
|
1220
|
+
hlined = false
|
1221
|
+
until @scanner.peek_command=="end"
|
1222
|
+
raise ParseError.new('Matching \end not exist.') if @scanner.eos?
|
1223
|
+
if @scanner.peek_command=="hline"
|
1224
|
+
@scanner.scan_command
|
1225
|
+
t << Tr.new unless row_parsed
|
1226
|
+
hlines << Line::SOLID
|
1227
|
+
row_parsed = false
|
1228
|
+
hlined = true
|
1229
|
+
else
|
1230
|
+
hlines << Line::NONE if row_parsed
|
1231
|
+
t << env_array_row(l.string)
|
1232
|
+
@scanner.scan(RE::WBSLASH)
|
1233
|
+
row_parsed = true
|
1234
|
+
hlined = false
|
1235
|
+
end
|
1236
|
+
end
|
1237
|
+
t.hlines = hlines
|
1238
|
+
|
1239
|
+
if hlined
|
1240
|
+
tr = Tr.new
|
1241
|
+
(0..vlines.size).each {|i| tr << Td.new}
|
1242
|
+
t << tr
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
@scanner.scan_command
|
1246
|
+
raise ParseError.new("Environment mismatched.") unless @scanner.check_block && @scanner[1]=="array"
|
1247
|
+
@scanner.scan_block
|
1248
|
+
t
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
def env_array_row(layout)
|
1252
|
+
l = Scanner.new(layout)
|
1253
|
+
r = Tr.new
|
1254
|
+
first_column = true
|
1255
|
+
vlined = l.check(/\|/)
|
1256
|
+
until l.eos?
|
1257
|
+
c = l.scan(/./)
|
1258
|
+
if c=='|'
|
1259
|
+
r << Td.new if vlined
|
1260
|
+
vlined = true
|
1261
|
+
next
|
1262
|
+
else
|
1263
|
+
vlined = false
|
1264
|
+
case c
|
1265
|
+
when 'r', 'l', 'c'
|
1266
|
+
when '@'
|
1267
|
+
r << parse_into(l.scan_any, Td.new)
|
1268
|
+
next
|
1269
|
+
end
|
1270
|
+
if first_column
|
1271
|
+
first_column = false
|
1272
|
+
else
|
1273
|
+
raise ParseError.new("Need more column.", @scanner.matched.to_s) unless @scanner.scan(/&/)
|
1274
|
+
end
|
1275
|
+
r << push_container(Td.new) do |td|
|
1276
|
+
td << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
|
1277
|
+
end
|
1278
|
+
end
|
1279
|
+
end
|
1280
|
+
r << Td.new if vlined
|
1281
|
+
raise ParseError.new("Too many column.") if @scanner.check(/&/)
|
1282
|
+
r
|
1283
|
+
end
|
1284
|
+
|
1285
|
+
def env_matrix
|
1286
|
+
t = Table.new
|
1287
|
+
hlines = Array.new
|
1288
|
+
hlined = false
|
1289
|
+
row_parsed = false
|
1290
|
+
until @scanner.peek_command=="end"
|
1291
|
+
raise ParseError.new('Matching \end not exist.') if @scanner.eos?
|
1292
|
+
if @scanner.peek_command=="hline"
|
1293
|
+
@scanner.scan_command
|
1294
|
+
t << Tr.new unless row_parsed
|
1295
|
+
hlines << Line::SOLID
|
1296
|
+
row_parsed = false
|
1297
|
+
hlined = true
|
1298
|
+
else
|
1299
|
+
hlines << Line::NONE if row_parsed
|
1300
|
+
t << (r = Tr.new)
|
1301
|
+
r << (td=Td.new)
|
1302
|
+
until @scanner.check(RE::WBSLASH) || @scanner.peek_command=="end" || @scanner.eos?
|
1303
|
+
push_container(td) do |e|
|
1304
|
+
e << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
|
1305
|
+
end
|
1306
|
+
r << (td=Td.new) if @scanner.scan(/&/)
|
1307
|
+
end
|
1308
|
+
@scanner.scan(RE::WBSLASH)
|
1309
|
+
row_parsed = true
|
1310
|
+
hlined = false
|
1311
|
+
end
|
1312
|
+
end
|
1313
|
+
t.hlines = hlines
|
1314
|
+
|
1315
|
+
t << Tr.new if hlined
|
1316
|
+
|
1317
|
+
raise ParseError.new("Need \\end{array}.") unless @scanner.peek_command=="end"
|
1318
|
+
@scanner.scan_command
|
1319
|
+
raise ParseError.new("Environment mismatched.") unless @scanner.check_block && @scanner[1]=="matrix"
|
1320
|
+
@scanner.scan_block
|
1321
|
+
t
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
def env_matrix_row
|
1325
|
+
r = Tr.new
|
1326
|
+
until @scanner.check(RE::WBSLASH) || @scanner.peek_command=="end"
|
1327
|
+
r << push_container(Td.new) do |td|
|
1328
|
+
td << parse_to_element(true) until @scanner.peek_command=="end" || @scanner.check(/(&|\\\\)/) || @scanner.eos?
|
1329
|
+
end
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
end
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
# Automatically generated constants
|
1338
|
+
module MathML::LaTeX::BuiltinCommands
|
1339
|
+
SymbolCommands={
|
1340
|
+
"{"=>[:s,:o,""],
|
1341
|
+
"}"=>[:s,:o,""],
|
1342
|
+
"#"=>[:s,:o,""],
|
1343
|
+
"$"=>[:s,:o,""],
|
1344
|
+
"&"=>[:s,:o,:amp],
|
1345
|
+
"_"=>[:s,:o,""],
|
1346
|
+
"%"=>[:s,:o,""],
|
1347
|
+
","=>nil,
|
1348
|
+
"varepsilon"=>[:s,:I],
|
1349
|
+
"mathdollar"=>[:s,:o,"$"],
|
1350
|
+
"lbrace"=>[:s],
|
1351
|
+
"rbrace"=>[:s],
|
1352
|
+
"P"=>[:s,:o,:para],
|
1353
|
+
"mathparagraph"=>[:s,:o,:para],
|
1354
|
+
"S"=>[:s,:o,:sect],
|
1355
|
+
"mathsection"=>[:s,:o,:sect],
|
1356
|
+
"dag"=>[:s,:o,:dagger],
|
1357
|
+
"dagger"=>[:s],
|
1358
|
+
"ddag"=>[:s,:o,:ddagger],
|
1359
|
+
"ddagger"=>[:s],
|
1360
|
+
"copyright"=>[:s,:o,:copy],
|
1361
|
+
"pounds"=>[:s,:o,:pound],
|
1362
|
+
"mathsterling"=>[:s,:o,:pound],
|
1363
|
+
"dots"=>[:s,:o,:mldr],
|
1364
|
+
"mathellipsis"=>[:s,:o,:mldr],
|
1365
|
+
"ldots"=>[:s,:o,:mldr],
|
1366
|
+
"ensuremath"=>nil,
|
1367
|
+
"|"=>[:s,:o,:DoubleVerticalBar],
|
1368
|
+
"mho"=>[:s],
|
1369
|
+
"Join"=>[:s,:o,:bowtie],
|
1370
|
+
"Box"=>[:s,:o,:square],
|
1371
|
+
"Diamond"=>[:s],
|
1372
|
+
"leadsto"=>[:s,:o,:zigrarr],
|
1373
|
+
"sqsubset"=>[:s],
|
1374
|
+
"sqsupset"=>[:s],
|
1375
|
+
"lhd"=>[:s,:o,:vltri],
|
1376
|
+
"unlhd"=>[:s,:o,:ltrie],
|
1377
|
+
"rhd"=>[:s,:o,:vrtri],
|
1378
|
+
"unrhd"=>[:s,:o,:rtrie],
|
1379
|
+
"log"=>[:s,:i,""],
|
1380
|
+
"lg"=>[:s,:i,""],
|
1381
|
+
"ln"=>[:s,:i,""],
|
1382
|
+
"lim"=>[:u,:i,""],
|
1383
|
+
"limsup"=>[:u,:i,"lim sup"],
|
1384
|
+
"liminf"=>[:u,:i,"lim inf"],
|
1385
|
+
"sin"=>[:s,:i,""],
|
1386
|
+
"arcsin"=>[:s,:i,""],
|
1387
|
+
"sinh"=>[:s,:i,""],
|
1388
|
+
"cos"=>[:s,:i,""],
|
1389
|
+
"arccos"=>[:s,:i,""],
|
1390
|
+
"cosh"=>[:s,:i,""],
|
1391
|
+
"tan"=>[:s,:i,""],
|
1392
|
+
"arctan"=>[:s,:i,""],
|
1393
|
+
"tanh"=>[:s,:i,""],
|
1394
|
+
"cot"=>[:s,:i,""],
|
1395
|
+
"coth"=>[:s,:i,""],
|
1396
|
+
"sec"=>[:s,:i,""],
|
1397
|
+
"csc"=>[:s,:i,""],
|
1398
|
+
"max"=>[:u,:i,""],
|
1399
|
+
"min"=>[:u,:i,""],
|
1400
|
+
"sup"=>[:u,:i,""],
|
1401
|
+
"inf"=>[:u,:i,""],
|
1402
|
+
"arg"=>[:s,:i,""],
|
1403
|
+
"ker"=>[:s,:i,""],
|
1404
|
+
"dim"=>[:s,:i,""],
|
1405
|
+
"hom"=>[:s,:i,""],
|
1406
|
+
"det"=>[:u,:i,""],
|
1407
|
+
"exp"=>[:s,:i,""],
|
1408
|
+
"Pr"=>[:u,:i,""],
|
1409
|
+
"gcd"=>[:u,:i,""],
|
1410
|
+
"deg"=>[:s,:i,""],
|
1411
|
+
"prime"=>[:s],
|
1412
|
+
"alpha"=>[:s,:I],
|
1413
|
+
"beta"=>[:s,:I],
|
1414
|
+
"gamma"=>[:s,:I],
|
1415
|
+
"delta"=>[:s,:I],
|
1416
|
+
"epsilon"=>[:s,:I],
|
1417
|
+
"zeta"=>[:s,:I],
|
1418
|
+
"eta"=>[:s,:I],
|
1419
|
+
"theta"=>[:s,:I],
|
1420
|
+
"iota"=>[:s,:I],
|
1421
|
+
"kappa"=>[:s,:I],
|
1422
|
+
"lambda"=>[:s,:I],
|
1423
|
+
"mu"=>[:s,:I],
|
1424
|
+
"nu"=>[:s,:I],
|
1425
|
+
"xi"=>[:s,:I],
|
1426
|
+
"pi"=>[:s,:I],
|
1427
|
+
"rho"=>[:s,:I],
|
1428
|
+
"sigma"=>[:s,:I],
|
1429
|
+
"tau"=>[:s,:I],
|
1430
|
+
"upsilon"=>[:s,:I],
|
1431
|
+
"phi"=>[:s,:I],
|
1432
|
+
"chi"=>[:s,:I],
|
1433
|
+
"psi"=>[:s,:I],
|
1434
|
+
"omega"=>[:s,:I],
|
1435
|
+
"vartheta"=>[:s,:I],
|
1436
|
+
"varpi"=>[:s,:I],
|
1437
|
+
"varrho"=>[:s,:I],
|
1438
|
+
"varsigma"=>[:s,:I],
|
1439
|
+
"varphi"=>[:s,:I],
|
1440
|
+
"Gamma"=>[:s,:i],
|
1441
|
+
"Delta"=>[:s,:i],
|
1442
|
+
"Theta"=>[:s,:i],
|
1443
|
+
"Lambda"=>[:s,:i],
|
1444
|
+
"Xi"=>[:s,:i],
|
1445
|
+
"Pi"=>[:s,:i],
|
1446
|
+
"Sigma"=>[:s,:i],
|
1447
|
+
"Upsilon"=>[:s,:i,:Upsi],
|
1448
|
+
"Phi"=>[:s,:i],
|
1449
|
+
"Psi"=>[:s,:i],
|
1450
|
+
"Omega"=>[:s,:i],
|
1451
|
+
"aleph"=>[:s,:i],
|
1452
|
+
"hbar"=>[:s,:i,:hslash],
|
1453
|
+
"imath"=>[:s,:i],
|
1454
|
+
"jmath"=>[:s,:i],
|
1455
|
+
"ell"=>[:s],
|
1456
|
+
"wp"=>[:s],
|
1457
|
+
"Re"=>[:s,:i],
|
1458
|
+
"Im"=>[:s,:i],
|
1459
|
+
"partial"=>[:s,:o,:part],
|
1460
|
+
"infty"=>[:s,:n,:infin],
|
1461
|
+
"emptyset"=>[:s,:i,:empty],
|
1462
|
+
"nabla"=>[:s,:i],
|
1463
|
+
"surd"=>[:s,:o,:Sqrt],
|
1464
|
+
"top"=>[:s],
|
1465
|
+
"bot"=>[:s],
|
1466
|
+
"angle"=>[:s],
|
1467
|
+
"not"=>[:s],
|
1468
|
+
"triangle"=>[:s],
|
1469
|
+
"forall"=>[:s],
|
1470
|
+
"exists"=>[:s,:o,:exist],
|
1471
|
+
"neg"=>[:s,:o,:not],
|
1472
|
+
"lnot"=>[:s,:o,:not],
|
1473
|
+
"flat"=>[:s],
|
1474
|
+
"natural"=>[:s],
|
1475
|
+
"sharp"=>[:s],
|
1476
|
+
"clubsuit"=>[:s],
|
1477
|
+
"diamondsuit"=>[:s],
|
1478
|
+
"heartsuit"=>[:s],
|
1479
|
+
"spadesuit"=>[:s],
|
1480
|
+
"coprod"=>[:u],
|
1481
|
+
"bigvee"=>[:u],
|
1482
|
+
"bigwedge"=>[:u],
|
1483
|
+
"biguplus"=>[:u],
|
1484
|
+
"bigcap"=>[:u],
|
1485
|
+
"bigcup"=>[:u],
|
1486
|
+
"intop"=>[:u,:o,:int],
|
1487
|
+
"int"=>[:s,:o],
|
1488
|
+
"prod"=>[:u],
|
1489
|
+
"sum"=>[:u],
|
1490
|
+
"bigotimes"=>[:u],
|
1491
|
+
"bigoplus"=>[:u],
|
1492
|
+
"bigodot"=>[:u],
|
1493
|
+
"ointop"=>[:u,:o,:oint],
|
1494
|
+
"oint"=>[:s],
|
1495
|
+
"bigsqcup"=>[:u],
|
1496
|
+
"smallint"=>[:u,:o,:int],
|
1497
|
+
"triangleleft"=>[:s],
|
1498
|
+
"triangleright"=>[:s],
|
1499
|
+
"bigtriangleup"=>[:s],
|
1500
|
+
"bigtriangledown"=>[:s],
|
1501
|
+
"wedge"=>[:s],
|
1502
|
+
"land"=>[:s,:o,:wedge],
|
1503
|
+
"vee"=>[:s],
|
1504
|
+
"lor"=>[:s,:o,:vee],
|
1505
|
+
"cap"=>[:s],
|
1506
|
+
"cup"=>[:s],
|
1507
|
+
"sqcap"=>[:s],
|
1508
|
+
"sqcup"=>[:s],
|
1509
|
+
"uplus"=>[:s],
|
1510
|
+
"amalg"=>[:s],
|
1511
|
+
"diamond"=>[:s],
|
1512
|
+
"bullet"=>[:s],
|
1513
|
+
"wr"=>[:s],
|
1514
|
+
"div"=>[:s],
|
1515
|
+
"odot"=>[:s],
|
1516
|
+
"oslash"=>[:s],
|
1517
|
+
"otimes"=>[:s],
|
1518
|
+
"ominus"=>[:s],
|
1519
|
+
"oplus"=>[:s],
|
1520
|
+
"mp"=>[:s],
|
1521
|
+
"pm"=>[:s],
|
1522
|
+
"circ"=>[:s,:o,:cir],
|
1523
|
+
"bigcirc"=>[:s],
|
1524
|
+
"setminus"=>[:s],
|
1525
|
+
"cdot"=>[:s,:o,:sdot],
|
1526
|
+
"ast"=>[:s],
|
1527
|
+
"times"=>[:s],
|
1528
|
+
"star"=>[:s],
|
1529
|
+
"propto"=>[:s],
|
1530
|
+
"sqsubseteq"=>[:s],
|
1531
|
+
"sqsupseteq"=>[:s],
|
1532
|
+
"parallel"=>[:s],
|
1533
|
+
"mid"=>[:s],
|
1534
|
+
"dashv"=>[:s],
|
1535
|
+
"vdash"=>[:s],
|
1536
|
+
"nearrow"=>[:s],
|
1537
|
+
"searrow"=>[:s],
|
1538
|
+
"nwarrow"=>[:s],
|
1539
|
+
"swarrow"=>[:s],
|
1540
|
+
"Leftrightarrow"=>[:s],
|
1541
|
+
"Leftarrow"=>[:s],
|
1542
|
+
"Rightarrow"=>[:s],
|
1543
|
+
"neq"=>[:s,:o,:ne],
|
1544
|
+
"ne"=>[:s],
|
1545
|
+
"leq"=>[:s],
|
1546
|
+
"le"=>[:s],
|
1547
|
+
"geq"=>[:s],
|
1548
|
+
"ge"=>[:s],
|
1549
|
+
"succ"=>[:s],
|
1550
|
+
"prec"=>[:s],
|
1551
|
+
"approx"=>[:s],
|
1552
|
+
"succeq"=>[:s,:o,:sccue],
|
1553
|
+
"preceq"=>[:s,:o,:prcue],
|
1554
|
+
"supset"=>[:s],
|
1555
|
+
"subset"=>[:s],
|
1556
|
+
"supseteq"=>[:s],
|
1557
|
+
"subseteq"=>[:s],
|
1558
|
+
"in"=>[:s],
|
1559
|
+
"ni"=>[:s],
|
1560
|
+
"owns"=>[:s,:o,:ni],
|
1561
|
+
"gg"=>[:s],
|
1562
|
+
"ll"=>[:s],
|
1563
|
+
"leftrightarrow"=>[:s],
|
1564
|
+
"leftarrow"=>[:s],
|
1565
|
+
"gets"=>[:s,:o,:leftarrow],
|
1566
|
+
"rightarrow"=>[:s],
|
1567
|
+
"to"=>[:s,:o,:rightarrow],
|
1568
|
+
"mapstochar"=>[:s,:o,:vdash],
|
1569
|
+
"mapsto"=>[:s],
|
1570
|
+
"sim"=>[:s],
|
1571
|
+
"simeq"=>[:s],
|
1572
|
+
"perp"=>[:s],
|
1573
|
+
"equiv"=>[:s],
|
1574
|
+
"asymp"=>[:s],
|
1575
|
+
"smile"=>[:s],
|
1576
|
+
"frown"=>[:s],
|
1577
|
+
"leftharpoonup"=>[:s],
|
1578
|
+
"leftharpoondown"=>[:s],
|
1579
|
+
"rightharpoonup"=>[:s],
|
1580
|
+
"rightharpoondown"=>[:s],
|
1581
|
+
"cong"=>[:s],
|
1582
|
+
"notin"=>[:s],
|
1583
|
+
"rightleftharpoons"=>[:s],
|
1584
|
+
"doteq"=>[:s],
|
1585
|
+
"joinrel"=>nil,
|
1586
|
+
"relbar"=>[:s,:o,"-"],
|
1587
|
+
"Relbar"=>[:s,:o,"="],
|
1588
|
+
"lhook"=>[:s,:o,:sub],
|
1589
|
+
"hookrightarrow"=>[:s],
|
1590
|
+
"rhook"=>[:s,:o,:sup],
|
1591
|
+
"hookleftarrow"=>[:s],
|
1592
|
+
"bowtie"=>[:s],
|
1593
|
+
"models"=>[:s],
|
1594
|
+
"Longrightarrow"=>[:s],
|
1595
|
+
"longrightarrow"=>[:s],
|
1596
|
+
"longleftarrow"=>[:s],
|
1597
|
+
"Longleftarrow"=>[:s],
|
1598
|
+
"longmapsto"=>[:s,:o,:mapsto],
|
1599
|
+
"longleftrightarrow"=>[:s],
|
1600
|
+
"Longleftrightarrow"=>[:s],
|
1601
|
+
"iff"=>[:s],
|
1602
|
+
"ldotp"=>[:s,:o,"."],
|
1603
|
+
"cdotp"=>[:s,:o,:cdot],
|
1604
|
+
"colon"=>[:s],
|
1605
|
+
"cdots"=>[:s,:o,:ctdot],
|
1606
|
+
"vdots"=>[:s,:o,:vellip],
|
1607
|
+
"ddots"=>[:s,:o,:dtdot],
|
1608
|
+
"braceld"=>[:s,:o,0x25dc],
|
1609
|
+
"bracerd"=>[:s,:o,0x25dd],
|
1610
|
+
"bracelu"=>[:s,:o,0x25df],
|
1611
|
+
"braceru"=>[:s,:o,0x25de],
|
1612
|
+
"lmoustache"=>[:s],
|
1613
|
+
"rmoustache"=>[:s],
|
1614
|
+
"arrowvert"=>[:s,:o,:vert],
|
1615
|
+
"Arrowvert"=>[:s,:o,:DoubleVerticalBar],
|
1616
|
+
"Vert"=>[:s,:o,:DoubleVerticalBar],
|
1617
|
+
"vert"=>[:s],
|
1618
|
+
"uparrow"=>[:s],
|
1619
|
+
"downarrow"=>[:s],
|
1620
|
+
"updownarrow"=>[:s],
|
1621
|
+
"Uparrow"=>[:s],
|
1622
|
+
"Downarrow"=>[:s],
|
1623
|
+
"Updownarrow"=>[:s],
|
1624
|
+
"backslash"=>[:s,:o,"\\"],
|
1625
|
+
"rangle"=>[:s],
|
1626
|
+
"langle"=>[:s],
|
1627
|
+
"rceil"=>[:s],
|
1628
|
+
"lceil"=>[:s],
|
1629
|
+
"rfloor"=>[:s],
|
1630
|
+
"lfloor"=>[:s],
|
1631
|
+
"lgroup"=>[:s,:o,0x2570],
|
1632
|
+
"rgroup"=>[:s,:o,0x256f],
|
1633
|
+
"bracevert"=>[:s,:o,:vert],
|
1634
|
+
"mathunderscore"=>[:s,:o,"_"],
|
1635
|
+
"square"=>[:s],
|
1636
|
+
"rightsquigarrow"=>[:s],
|
1637
|
+
"lozenge"=>[:s],
|
1638
|
+
"vartriangleright"=>[:s],
|
1639
|
+
"vartriangleleft"=>[:s],
|
1640
|
+
"trianglerighteq"=>[:s],
|
1641
|
+
"trianglelefteq"=>[:s],
|
1642
|
+
"boxdot"=>[:s,:o,:dotsquare],
|
1643
|
+
"boxplus"=>[:s],
|
1644
|
+
"boxtimes"=>[:s],
|
1645
|
+
"blacksquare"=>[:s],
|
1646
|
+
"centerdot"=>[:s],
|
1647
|
+
"blacklozenge"=>[:s],
|
1648
|
+
"circlearrowright"=>[:s],
|
1649
|
+
"circlearrowleft"=>[:s],
|
1650
|
+
"leftrightharpoons"=>[:s],
|
1651
|
+
"boxminus"=>[:s],
|
1652
|
+
"Vdash"=>[:s],
|
1653
|
+
"Vvdash"=>[:s],
|
1654
|
+
"vDash"=>[:s],
|
1655
|
+
"twoheadrightarrow"=>[:s],
|
1656
|
+
"twoheadleftarrow"=>[:s],
|
1657
|
+
"leftleftarrows"=>[:s],
|
1658
|
+
"rightrightarrows"=>[:s],
|
1659
|
+
"upuparrows"=>[:s],
|
1660
|
+
"downdownarrows"=>[:s],
|
1661
|
+
"upharpoonright"=>[:s],
|
1662
|
+
"restriction"=>[:s,:o,:upharpoonright],
|
1663
|
+
"downharpoonright"=>[:s],
|
1664
|
+
"upharpoonleft"=>[:s],
|
1665
|
+
"downharpoonleft"=>[:s],
|
1666
|
+
"rightarrowtail"=>[:s],
|
1667
|
+
"leftarrowtail"=>[:s],
|
1668
|
+
"leftrightarrows"=>[:s],
|
1669
|
+
"rightleftarrows"=>[:s],
|
1670
|
+
"Lsh"=>[:s],
|
1671
|
+
"Rsh"=>[:s],
|
1672
|
+
"leftrightsquigarrow"=>[:s],
|
1673
|
+
"looparrowleft"=>[:s],
|
1674
|
+
"looparrowright"=>[:s],
|
1675
|
+
"circeq"=>[:s],
|
1676
|
+
"succsim"=>[:s],
|
1677
|
+
"gtrsim"=>[:s],
|
1678
|
+
"gtrapprox"=>[:s],
|
1679
|
+
"multimap"=>[:s],
|
1680
|
+
"therefore"=>[:s],
|
1681
|
+
"because"=>[:s],
|
1682
|
+
"doteqdot"=>[:s],
|
1683
|
+
"Doteq"=>[:s,:o,:doteqdot],
|
1684
|
+
"triangleq"=>[:s],
|
1685
|
+
"precsim"=>[:s],
|
1686
|
+
"lesssim"=>[:s],
|
1687
|
+
"lessapprox"=>[:s],
|
1688
|
+
"eqslantless"=>[:s],
|
1689
|
+
"eqslantgtr"=>[:s],
|
1690
|
+
"curlyeqprec"=>[:s],
|
1691
|
+
"curlyeqsucc"=>[:s],
|
1692
|
+
"preccurlyeq"=>[:s],
|
1693
|
+
"leqq"=>[:s],
|
1694
|
+
"leqslant"=>[:s,:o,:leq],
|
1695
|
+
"lessgtr"=>[:s],
|
1696
|
+
"backprime"=>[:s],
|
1697
|
+
"risingdotseq"=>[:s],
|
1698
|
+
"fallingdotseq"=>[:s],
|
1699
|
+
"succcurlyeq"=>[:s],
|
1700
|
+
"geqq"=>[:s],
|
1701
|
+
"geqslant"=>[:s,:o,:geq],
|
1702
|
+
"gtrless"=>[:s],
|
1703
|
+
"bigstar"=>[:s],
|
1704
|
+
"between"=>[:s],
|
1705
|
+
"blacktriangledown"=>[:s],
|
1706
|
+
"blacktriangleright"=>[:s],
|
1707
|
+
"blacktriangleleft"=>[:s],
|
1708
|
+
"vartriangle"=>[:s,:o,:triangle],
|
1709
|
+
"blacktriangle"=>[:s],
|
1710
|
+
"triangledown"=>[:s],
|
1711
|
+
"eqcirc"=>[:s],
|
1712
|
+
"lesseqgtr"=>[:s],
|
1713
|
+
"gtreqless"=>[:s],
|
1714
|
+
"lesseqqgtr"=>[:s],
|
1715
|
+
"gtreqqless"=>[:s],
|
1716
|
+
"Rrightarrow"=>[:s],
|
1717
|
+
"Lleftarrow"=>[:s],
|
1718
|
+
"veebar"=>[:s],
|
1719
|
+
"barwedge"=>[:s],
|
1720
|
+
"doublebarwedge"=>[:s],
|
1721
|
+
"measuredangle"=>[:s],
|
1722
|
+
"sphericalangle"=>[:s,:o,:angsph],
|
1723
|
+
"varpropto"=>[:s],
|
1724
|
+
"smallsmile"=>[:s,:o,:smile],
|
1725
|
+
"smallfrown"=>[:s,:o,:frown],
|
1726
|
+
"Subset"=>[:s],
|
1727
|
+
"Supset"=>[:s],
|
1728
|
+
"Cup"=>[:s],
|
1729
|
+
"doublecup"=>[:s,:o,:Cup],
|
1730
|
+
"Cap"=>[:s],
|
1731
|
+
"doublecap"=>[:s,:o,:Cap],
|
1732
|
+
"curlywedge"=>[:s],
|
1733
|
+
"curlyvee"=>[:s],
|
1734
|
+
"leftthreetimes"=>[:s],
|
1735
|
+
"rightthreetimes"=>[:s],
|
1736
|
+
"subseteqq"=>[:s],
|
1737
|
+
"supseteqq"=>[:s],
|
1738
|
+
"bumpeq"=>[:s],
|
1739
|
+
"Bumpeq"=>[:s],
|
1740
|
+
"lll"=>[:s,:o,:Ll],
|
1741
|
+
"llless"=>[:s,:o,:Ll],
|
1742
|
+
"ggg"=>[:s],
|
1743
|
+
"gggtr"=>[:s,:o,:ggg],
|
1744
|
+
"circledS"=>[:s],
|
1745
|
+
"pitchfork"=>[:s],
|
1746
|
+
"dotplus"=>[:s],
|
1747
|
+
"backsim"=>[:s],
|
1748
|
+
"backsimeq"=>[:s],
|
1749
|
+
"complement"=>[:s],
|
1750
|
+
"intercal"=>[:s],
|
1751
|
+
"circledcirc"=>[:s],
|
1752
|
+
"circledast"=>[:s],
|
1753
|
+
"circleddash"=>[:s],
|
1754
|
+
"lvertneqq"=>[:s,:o,:lneqq],
|
1755
|
+
"gvertneqq"=>[:s,:o,:gneqq],
|
1756
|
+
"nleq"=>[:s,:o,0x2270],
|
1757
|
+
"ngeq"=>[:s,:o,0x2271],
|
1758
|
+
"nless"=>[:s],
|
1759
|
+
"ngtr"=>[:s],
|
1760
|
+
"nprec"=>[:s],
|
1761
|
+
"nsucc"=>[:s],
|
1762
|
+
"lneqq"=>[:s],
|
1763
|
+
"gneqq"=>[:s],
|
1764
|
+
"nleqslant"=>[:s],
|
1765
|
+
"ngeqslant"=>[:s],
|
1766
|
+
"lneq"=>[:s],
|
1767
|
+
"gneq"=>[:s],
|
1768
|
+
"npreceq"=>[:s,:o,:nprcue],
|
1769
|
+
"nsucceq"=>[:s,:o,:nsccue],
|
1770
|
+
"precnsim"=>[:s],
|
1771
|
+
"succnsim"=>[:s],
|
1772
|
+
"lnsim"=>[:s],
|
1773
|
+
"gnsim"=>[:s],
|
1774
|
+
"nleqq"=>[:s],
|
1775
|
+
"ngeqq"=>[:s],
|
1776
|
+
"precneqq"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>≺</mo><mo>≠</mo></mfrac>"],
|
1777
|
+
"succneqq"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>≻</mo><mo>≠</mo></mfrac>"],
|
1778
|
+
"precnapprox"=>[:s],
|
1779
|
+
"succnapprox"=>[:s],
|
1780
|
+
"lnapprox"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo><</mo><mo>≉</mo></mfrac>"],
|
1781
|
+
"gnapprox"=>[:s,:o,"<mfrac linethickness='0' mathsize='1%'><mo>></mo><mo>≉</mo></mfrac>"],
|
1782
|
+
"nsim"=>[:s],
|
1783
|
+
"ncong"=>[:s],
|
1784
|
+
"diagup"=>[:s,:o,0x2571],
|
1785
|
+
"diagdown"=>[:s,:o,0x2572],
|
1786
|
+
"varsubsetneq"=>[:s,:o,:subsetneq],
|
1787
|
+
"varsupsetneq"=>[:s,:o,:supsetneq],
|
1788
|
+
"nsubseteqq"=>[:s],
|
1789
|
+
"nsupseteqq"=>[:s],
|
1790
|
+
"subsetneqq"=>[:s],
|
1791
|
+
"supsetneqq"=>[:s],
|
1792
|
+
"varsubsetneqq"=>[:s,:o,:subsetneqq],
|
1793
|
+
"varsupsetneqq"=>[:s,:o,:supsetneqq],
|
1794
|
+
"subsetneq"=>[:s],
|
1795
|
+
"supsetneq"=>[:s],
|
1796
|
+
"nsubseteq"=>[:s],
|
1797
|
+
"nsupseteq"=>[:s],
|
1798
|
+
"nparallel"=>[:s],
|
1799
|
+
"nmid"=>[:s],
|
1800
|
+
"nshortmid"=>[:s,:o,:nmid],
|
1801
|
+
"nshortparallel"=>[:s,:o,:nparallel],
|
1802
|
+
"nvdash"=>[:s],
|
1803
|
+
"nVdash"=>[:s],
|
1804
|
+
"nvDash"=>[:s],
|
1805
|
+
"nVDash"=>[:s],
|
1806
|
+
"ntrianglerighteq"=>[:s],
|
1807
|
+
"ntrianglelefteq"=>[:s],
|
1808
|
+
"ntriangleleft"=>[:s],
|
1809
|
+
"ntriangleright"=>[:s],
|
1810
|
+
"nleftarrow"=>[:s],
|
1811
|
+
"nrightarrow"=>[:s],
|
1812
|
+
"nLeftarrow"=>[:s],
|
1813
|
+
"nRightarrow"=>[:s],
|
1814
|
+
"nLeftrightarrow"=>[:s],
|
1815
|
+
"nleftrightarrow"=>[:s],
|
1816
|
+
"divideontimes"=>[:s],
|
1817
|
+
"varnothing"=>[:s],
|
1818
|
+
"nexists"=>[:s],
|
1819
|
+
"Finv"=>[:s,:o,0x2132],
|
1820
|
+
"Game"=>[:s,:o,"G"],
|
1821
|
+
"eth"=>[:s],
|
1822
|
+
"eqsim"=>[:s],
|
1823
|
+
"beth"=>[:s],
|
1824
|
+
"gimel"=>[:s],
|
1825
|
+
"daleth"=>[:s],
|
1826
|
+
"lessdot"=>[:s],
|
1827
|
+
"gtrdot"=>[:s],
|
1828
|
+
"ltimes"=>[:s],
|
1829
|
+
"rtimes"=>[:s],
|
1830
|
+
"shortmid"=>[:s,:o,:mid],
|
1831
|
+
"shortparallel"=>[:s],
|
1832
|
+
"smallsetminus"=>[:s,:o,:setminus],
|
1833
|
+
"thicksim"=>[:s,:o,:sim],
|
1834
|
+
"thickapprox"=>[:s,:o,:approx],
|
1835
|
+
"approxeq"=>[:s],
|
1836
|
+
"succapprox"=>[:s],
|
1837
|
+
"precapprox"=>[:s],
|
1838
|
+
"curvearrowleft"=>[:s],
|
1839
|
+
"curvearrowright"=>[:s],
|
1840
|
+
"digamma"=>[:s],
|
1841
|
+
"varkappa"=>[:s],
|
1842
|
+
"Bbbk"=>[:s,:i,:kopf],
|
1843
|
+
"hslash"=>[:s],
|
1844
|
+
"backepsilon"=>[:s],
|
1845
|
+
"ulcorner"=>[:s,:o,:boxdr],
|
1846
|
+
"urcorner"=>[:s,:o,:boxdl],
|
1847
|
+
"llcorner"=>[:s,:o,:boxur],
|
1848
|
+
"lrcorner"=>[:s,:o,:boxul],
|
1849
|
+
}
|
1850
|
+
Delimiters=[
|
1851
|
+
"lmoustache",
|
1852
|
+
"rmoustache",
|
1853
|
+
"arrowvert",
|
1854
|
+
"Arrowvert",
|
1855
|
+
"Vert",
|
1856
|
+
"vert",
|
1857
|
+
"uparrow",
|
1858
|
+
"downarrow",
|
1859
|
+
"updownarrow",
|
1860
|
+
"Uparrow",
|
1861
|
+
"Downarrow",
|
1862
|
+
"Updownarrow",
|
1863
|
+
"backslash",
|
1864
|
+
"rangle",
|
1865
|
+
"langle",
|
1866
|
+
"rbrace",
|
1867
|
+
"lbrace",
|
1868
|
+
"rceil",
|
1869
|
+
"lceil",
|
1870
|
+
"rfloor",
|
1871
|
+
"lfloor",
|
1872
|
+
"lgroup",
|
1873
|
+
"rgroup",
|
1874
|
+
"bracevert",
|
1875
|
+
"ulcorner",
|
1876
|
+
"urcorner",
|
1877
|
+
"llcorner",
|
1878
|
+
"lrcorner",
|
1879
|
+
"{",
|
1880
|
+
"|",
|
1881
|
+
"}",
|
1882
|
+
]
|
1883
|
+
end
|
1884
|
+
|
1885
|
+
end
|