asciimath 1.0.9 → 2.0.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.
- checksums.yaml +5 -5
- data/AST.adoc +457 -0
- data/CHANGELOG.adoc +12 -0
- data/Gemfile.lock +39 -0
- data/README.adoc +27 -3
- data/asciimath.gemspec +9 -5
- data/lib/asciimath.rb +5 -4
- data/lib/asciimath/ast.rb +456 -0
- data/lib/asciimath/cli.rb +4 -1
- data/lib/asciimath/color_table.rb +21 -0
- data/lib/asciimath/html.rb +126 -111
- data/lib/asciimath/latex.rb +386 -0
- data/lib/asciimath/markup.rb +478 -0
- data/lib/asciimath/mathml.rb +189 -87
- data/lib/asciimath/parser.rb +498 -343
- data/lib/asciimath/symbol_table.rb +25 -0
- data/lib/asciimath/version.rb +1 -1
- data/spec/ast.rb +144 -0
- data/spec/parser_spec.rb +592 -165
- data/spec/schema/mathml2/common/common-attribs.xsd +41 -0
- data/spec/schema/mathml2/common/math.xsd +126 -0
- data/spec/schema/mathml2/common/xlink-href.xsd +20 -0
- data/spec/schema/mathml2/content/arith.xsd +90 -0
- data/spec/schema/mathml2/content/calculus.xsd +146 -0
- data/spec/schema/mathml2/content/common-attrib.xsd +30 -0
- data/spec/schema/mathml2/content/constants.xsd +83 -0
- data/spec/schema/mathml2/content/constructs.xsd +260 -0
- data/spec/schema/mathml2/content/elementary-functions.xsd +117 -0
- data/spec/schema/mathml2/content/functions.xsd +73 -0
- data/spec/schema/mathml2/content/linear-algebra.xsd +173 -0
- data/spec/schema/mathml2/content/logic.xsd +53 -0
- data/spec/schema/mathml2/content/relations.xsd +55 -0
- data/spec/schema/mathml2/content/semantics.xsd +85 -0
- data/spec/schema/mathml2/content/sets.xsd +236 -0
- data/spec/schema/mathml2/content/statistics.xsd +136 -0
- data/spec/schema/mathml2/content/tokens.xsd +120 -0
- data/spec/schema/mathml2/content/vector-calculus.xsd +88 -0
- data/spec/schema/mathml2/mathml2.xsd +59 -0
- data/spec/schema/mathml2/presentation/action.xsd +44 -0
- data/spec/schema/mathml2/presentation/characters.xsd +37 -0
- data/spec/schema/mathml2/presentation/common-attribs.xsd +113 -0
- data/spec/schema/mathml2/presentation/common-types.xsd +103 -0
- data/spec/schema/mathml2/presentation/error.xsd +40 -0
- data/spec/schema/mathml2/presentation/layout.xsd +195 -0
- data/spec/schema/mathml2/presentation/scripts.xsd +186 -0
- data/spec/schema/mathml2/presentation/space.xsd +52 -0
- data/spec/schema/mathml2/presentation/style.xsd +69 -0
- data/spec/schema/mathml2/presentation/table.xsd +216 -0
- data/spec/schema/mathml2/presentation/tokens.xsd +124 -0
- data/spec/schema/mathml3/mathml3-common.xsd +99 -0
- data/spec/schema/mathml3/mathml3-content.xsd +684 -0
- data/spec/schema/mathml3/mathml3-presentation.xsd +2151 -0
- data/spec/schema/mathml3/mathml3-strict-content.xsd +186 -0
- data/spec/schema/mathml3/mathml3.xsd +9 -0
- metadata +102 -10
- data/.gitignore +0 -16
- data/.travis.yml +0 -18
data/lib/asciimath/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require_relative 'parser'
|
2
2
|
require_relative 'mathml'
|
3
3
|
require_relative 'html'
|
4
|
+
require_relative 'latex'
|
4
5
|
|
5
6
|
module AsciiMath
|
6
7
|
module CLI
|
@@ -11,8 +12,10 @@ module AsciiMath
|
|
11
12
|
output = AsciiMath.parse(asciimath).to_mathml
|
12
13
|
elsif args.first == "html"
|
13
14
|
output = AsciiMath.parse(asciimath).to_html
|
15
|
+
elsif args.first == "latex"
|
16
|
+
output = AsciiMath.parse(asciimath).to_latex
|
14
17
|
end
|
15
18
|
puts output
|
16
19
|
end
|
17
20
|
end
|
18
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AsciiMath
|
2
|
+
class ColorTableBuilder
|
3
|
+
def initialize()
|
4
|
+
@table = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(*names, r, g, b)
|
8
|
+
entry = {
|
9
|
+
:r => r,
|
10
|
+
:g => g,
|
11
|
+
:b => b
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
names.each { |name| @table[name.freeze] = entry }
|
15
|
+
end
|
16
|
+
|
17
|
+
def build
|
18
|
+
@table.dup.freeze
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/asciimath/html.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
require_relative 'markup'
|
2
|
+
|
1
3
|
module AsciiMath
|
2
|
-
class HTMLBuilder
|
3
|
-
|
4
|
-
|
4
|
+
class HTMLBuilder < ::AsciiMath::MarkupBuilder
|
5
|
+
|
6
|
+
def initialize(opts = {})
|
7
|
+
super(opts[:symbol_table] || DEFAULT_DISPLAY_SYMBOL_TABLE)
|
8
|
+
@prefix = opts[:prefifx] || ''
|
9
|
+
@inline = opts[:inline]
|
5
10
|
@html = ''
|
6
11
|
end
|
7
12
|
|
@@ -9,14 +14,14 @@ module AsciiMath
|
|
9
14
|
@html
|
10
15
|
end
|
11
16
|
|
12
|
-
def append_expression(expression,
|
13
|
-
if inline
|
17
|
+
def append_expression(expression, attrs = {})
|
18
|
+
if @inline
|
14
19
|
inline('', attrs) do
|
15
|
-
append(expression, :
|
20
|
+
append(expression, :row => :omit)
|
16
21
|
end
|
17
22
|
else
|
18
23
|
block('', attrs) do
|
19
|
-
append(expression, :
|
24
|
+
append(expression, :row => :omit)
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
@@ -25,131 +30,141 @@ module AsciiMath
|
|
25
30
|
|
26
31
|
ZWJ = "\u200D"
|
27
32
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
33
|
+
def append_row(expressions)
|
34
|
+
row do
|
35
|
+
expressions.each { |e| append(e) }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def append_operator(operator)
|
40
|
+
operator(operator)
|
41
|
+
end
|
42
|
+
|
43
|
+
def append_identifier(identifier)
|
44
|
+
identifier(identifier)
|
45
|
+
end
|
46
|
+
|
47
|
+
def append_text(text)
|
48
|
+
text(text)
|
49
|
+
end
|
50
|
+
|
51
|
+
def append_number(number)
|
52
|
+
number(number)
|
53
|
+
end
|
54
|
+
|
55
|
+
def append_sqrt(expression)
|
56
|
+
tag("sqrt") do
|
57
|
+
append(child, :row => :omit)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def append_cancel(expression)
|
62
|
+
#TODO - currently ignored
|
63
|
+
append(expression)
|
64
|
+
end
|
65
|
+
|
66
|
+
def append_root(base, index)
|
67
|
+
tag("sqrt") do
|
68
|
+
append(base)
|
69
|
+
append(index)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def append_font(style, e)
|
74
|
+
#TODO - currently ignored
|
75
|
+
append(e)
|
76
|
+
end
|
77
|
+
|
78
|
+
def append_color(color, expression)
|
79
|
+
#TODO - currently ignored
|
80
|
+
append(expression)
|
81
|
+
end
|
82
|
+
|
83
|
+
def append_matrix(lparen, rows, rparen)
|
84
|
+
row do
|
85
|
+
# Figures out a font size for the braces, based on the height of the matrix.
|
86
|
+
# NOTE: This does not currently consider the size of each element within the matrix.
|
87
|
+
brace_height = "font-size: " + rows.length.to_s + "00%;"
|
88
|
+
|
89
|
+
if lparen
|
90
|
+
brace(lparen, {:style => brace_height})
|
91
|
+
else
|
92
|
+
blank(ZWJ)
|
93
|
+
end
|
94
|
+
matrix_width = "grid-template-columns:repeat(" + rows[0].length.to_s + ",1fr);"
|
95
|
+
matrix_height = "grid-template-rows:repeat(" + rows.length.to_s + ",1fr);"
|
96
|
+
|
97
|
+
matrix({:style => (matrix_width + matrix_height)}) do
|
98
|
+
rows.each do |row|
|
99
|
+
row.each do |col|
|
95
100
|
row do
|
96
|
-
|
97
|
-
# NOTE: This does not currently consider the size of each element within the matrix.
|
98
|
-
brace_height = "font-size: " + expression[:rows].length.to_s + "00%;"
|
99
|
-
|
100
|
-
if expression[:lparen]
|
101
|
-
brace(expression[:lparen], {:style => brace_height})
|
102
|
-
else
|
103
|
-
blank(ZWJ)
|
104
|
-
end
|
105
|
-
matrix_width = "grid-template-columns:repeat(" + expression[:rows][0].length.to_s + ",1fr);"
|
106
|
-
matrix_height = "grid-template-rows:repeat(" + expression[:rows].length.to_s + ",1fr);"
|
107
|
-
|
108
|
-
matrix({:style => (matrix_width + matrix_height)}) do
|
109
|
-
expression[:rows].each do |row|
|
110
|
-
row.each do |col|
|
111
|
-
row do
|
112
|
-
append(col)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
if expression[:rparen]
|
118
|
-
brace(expression[:rparen], {:style => brace_height})
|
119
|
-
else
|
120
|
-
blank(ZWJ)
|
121
|
-
end
|
101
|
+
append(col)
|
122
102
|
end
|
103
|
+
end
|
123
104
|
end
|
105
|
+
end
|
106
|
+
if rparen
|
107
|
+
brace(rparen, {:style => brace_height})
|
108
|
+
else
|
109
|
+
blank(ZWJ)
|
110
|
+
end
|
124
111
|
end
|
125
112
|
end
|
126
|
-
|
113
|
+
|
114
|
+
def append_operator_unary(operator, expression)
|
115
|
+
tag(operator) do
|
116
|
+
append(expression, :row => :omit)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def append_identifier_unary(identifier, expression)
|
121
|
+
row do
|
122
|
+
identifier(identifier)
|
123
|
+
append(expression, :row => :omit)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def append_paren(lparen, e, rparen, opts = {})
|
128
|
+
if opts[:row] == :omit
|
129
|
+
brace(lparen) if lparen
|
130
|
+
append(e, :row => :omit)
|
131
|
+
brace(rparen) if rparen
|
132
|
+
else
|
133
|
+
row do
|
134
|
+
brace(lparen) if lparen
|
135
|
+
append(e, :row => :omit)
|
136
|
+
brace(rparen) if rparen
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
127
141
|
def append_subsup(base, sub, sup)
|
128
142
|
append(base)
|
129
143
|
subsup do
|
130
144
|
if sup
|
131
145
|
smaller do
|
132
|
-
append(sup
|
146
|
+
append(sup)
|
133
147
|
end
|
134
148
|
else
|
135
149
|
smaller(ZWJ)
|
136
150
|
end
|
137
151
|
if sub
|
138
152
|
smaller do
|
139
|
-
append(sub
|
153
|
+
append(sub)
|
140
154
|
end
|
141
155
|
else
|
142
156
|
smaller(ZWJ)
|
143
157
|
end
|
144
158
|
end
|
145
159
|
end
|
146
|
-
|
160
|
+
|
147
161
|
def append_underover(base, under, over)
|
162
|
+
# TODO: Handle over/under braces in some way? SVG maybe?
|
148
163
|
blank(ZWJ)
|
149
164
|
underover do
|
150
165
|
smaller do
|
151
166
|
if over
|
152
|
-
append(over
|
167
|
+
append(over)
|
153
168
|
else
|
154
169
|
blank(ZWJ)
|
155
170
|
end
|
@@ -157,14 +172,14 @@ module AsciiMath
|
|
157
172
|
append(base)
|
158
173
|
smaller do
|
159
174
|
if under
|
160
|
-
append(under
|
175
|
+
append(under)
|
161
176
|
else
|
162
177
|
blank(ZWJ)
|
163
178
|
end
|
164
179
|
end
|
165
180
|
end
|
166
181
|
end
|
167
|
-
|
182
|
+
|
168
183
|
def append_fraction(numerator, denominator)
|
169
184
|
blank(ZWJ)
|
170
185
|
fraction do
|
@@ -172,7 +187,7 @@ module AsciiMath
|
|
172
187
|
fraction_cell do
|
173
188
|
smaller do
|
174
189
|
row do
|
175
|
-
append(numerator
|
190
|
+
append(numerator)
|
176
191
|
end
|
177
192
|
end
|
178
193
|
end
|
@@ -181,7 +196,7 @@ module AsciiMath
|
|
181
196
|
fraction_cell do
|
182
197
|
smaller do
|
183
198
|
row do
|
184
|
-
append(denominator
|
199
|
+
append(denominator)
|
185
200
|
end
|
186
201
|
end
|
187
202
|
end
|
@@ -192,7 +207,7 @@ module AsciiMath
|
|
192
207
|
def method_missing(meth, *args, &block)
|
193
208
|
tag(meth, *args, &block)
|
194
209
|
end
|
195
|
-
|
210
|
+
|
196
211
|
def tag(tag, *args)
|
197
212
|
attrs = args.last.is_a?(Hash) ? args.pop : {}
|
198
213
|
text = args.last.is_a?(String) ? args.pop : ''
|
@@ -228,7 +243,7 @@ module AsciiMath
|
|
228
243
|
|
229
244
|
class Expression
|
230
245
|
def to_html(prefix = "", inline = true, attrs = {})
|
231
|
-
HTMLBuilder.new(prefix).append_expression(
|
246
|
+
HTMLBuilder.new(:prefix => prefix, :inline => inline).append_expression(ast, attrs).to_s
|
232
247
|
end
|
233
248
|
end
|
234
249
|
end
|
@@ -0,0 +1,386 @@
|
|
1
|
+
require_relative 'ast'
|
2
|
+
|
3
|
+
module AsciiMath
|
4
|
+
class LatexBuilder
|
5
|
+
def initialize
|
6
|
+
@latex = ''
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@latex
|
11
|
+
end
|
12
|
+
|
13
|
+
def append_expression(expression)
|
14
|
+
append(expression)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
SPECIAL_CHARACTERS = [?&, ?%, ?$, ?#, ?_, ?{, ?}, ?~, ?^, ?[, ?]].map(&:ord)
|
21
|
+
|
22
|
+
SYMBOLS = {
|
23
|
+
:plus => ?+,
|
24
|
+
:minus => ?-,
|
25
|
+
:ast => ?*,
|
26
|
+
:slash => ?/,
|
27
|
+
:eq => ?=,
|
28
|
+
:ne => "\\neq",
|
29
|
+
:assign => ":=",
|
30
|
+
:lt => ?<,
|
31
|
+
:gt => ?>,
|
32
|
+
:sub => "\\text{–}",
|
33
|
+
:sup => "\\text{^}",
|
34
|
+
:implies => "\\Rightarrow",
|
35
|
+
:iff => "\\Leftrightarrow",
|
36
|
+
:if => "\\operatorname{if}",
|
37
|
+
:and => "\\operatorname{and}",
|
38
|
+
:or => "\\operatorname{or}",
|
39
|
+
:lparen => ?(,
|
40
|
+
:rparen => ?),
|
41
|
+
:lbracket => ?[,
|
42
|
+
:rbracket => ?],
|
43
|
+
:lbrace => "\\{",
|
44
|
+
:rbrace => "\\}",
|
45
|
+
:lvert => "\\lVert",
|
46
|
+
:rvert => "\\rVert",
|
47
|
+
:vbar => ?|,
|
48
|
+
nil => ?.,
|
49
|
+
:integral => "\\int",
|
50
|
+
:dx => "dx",
|
51
|
+
:dy => "dy",
|
52
|
+
:dz => "dz",
|
53
|
+
:dt => "dt",
|
54
|
+
:contourintegral => "\\oint",
|
55
|
+
:Lim => "\\operatorname{Lim}",
|
56
|
+
:Sin => "\\operatorname{Sin}",
|
57
|
+
:Cos => "\\operatorname{Cos}",
|
58
|
+
:Tan => "\\operatorname{Tan}",
|
59
|
+
:Sinh => "\\operatorname{Sinh}",
|
60
|
+
:Cosh => "\\operatorname{Cosh}",
|
61
|
+
:Tanh => "\\operatorname{Tanh}",
|
62
|
+
:Cot => "\\operatorname{Cot}",
|
63
|
+
:Sec => "\\operatorname{Sec}",
|
64
|
+
:Csc => "\\operatorname{Csc}",
|
65
|
+
:sech => "\\operatorname{sech}",
|
66
|
+
:csch => "\\operatorname{csch}",
|
67
|
+
:Abs => "\\operatorname{Abs}",
|
68
|
+
:Log => "\\operatorname{Log}",
|
69
|
+
:Ln => "\\operatorname{Ln}",
|
70
|
+
:lcm => "\\operatorname{lcm}",
|
71
|
+
:lub => "\\operatorname{lub}",
|
72
|
+
:glb => "\\operatorname{glb}",
|
73
|
+
:partial => "\\del",
|
74
|
+
:prime => ?',
|
75
|
+
:tilde => "\\~",
|
76
|
+
:nbsp => "\\;",
|
77
|
+
:lceiling => "\\lceil",
|
78
|
+
:rceiling => "\\rceil",
|
79
|
+
:dstruck_captial_c => "\\mathbb{C}",
|
80
|
+
:dstruck_captial_n => "\\mathbb{N}",
|
81
|
+
:dstruck_captial_q => "\\mathbb{Q}",
|
82
|
+
:dstruck_captial_r => "\\mathbb{R}",
|
83
|
+
:dstruck_captial_z => "\\mathbb{Z}",
|
84
|
+
:f => "f",
|
85
|
+
:g => "g",
|
86
|
+
:to => "\\rightarrow",
|
87
|
+
:bold => "\\mathbf",
|
88
|
+
:double_struck => "\\mathbb",
|
89
|
+
:italic => "\\mathit",
|
90
|
+
:bold_italic => "\\mathbf",
|
91
|
+
:script => "\\mathscr",
|
92
|
+
:bold_script => "\\mathscr",
|
93
|
+
:monospace => "\\mathtt",
|
94
|
+
:fraktur => "\\mathfrak",
|
95
|
+
:bold_fraktur => "\\mathfrak",
|
96
|
+
:sans_serif => "\\mathsf",
|
97
|
+
:bold_sans_serif => "\\mathsf",
|
98
|
+
:sans_serif_italic => "\\mathsf",
|
99
|
+
:sans_serif_bold_italic => "\\mathsf",
|
100
|
+
}
|
101
|
+
|
102
|
+
COLOURS = {
|
103
|
+
[0xFF, 0xFF, 0xFF] => "white",
|
104
|
+
[0xFF, 0x00, 0x00] => "red",
|
105
|
+
[0x00, 0xFF, 0x00] => "green",
|
106
|
+
[0x00, 0x00, 0xFF] => "blue",
|
107
|
+
[0xBF, 0x80, 0x40] => "brown",
|
108
|
+
[0x00, 0xAD, 0xEF] => "cyan",
|
109
|
+
[0x40, 0x40, 0x40] => "darkgray",
|
110
|
+
[0x80, 0x80, 0x80] => "gray",
|
111
|
+
[0xBF, 0xBF, 0xBF] => "lightgray",
|
112
|
+
[0xA4, 0xDB, 0x00] => "lime",
|
113
|
+
[0xE9, 0x00, 0x8A] => "magenta",
|
114
|
+
[0x8E, 0x86, 0x00] => "olive",
|
115
|
+
[0xFF, 0x80, 0x00] => "orange",
|
116
|
+
[0xFF, 0xBF, 0xBF] => "pink",
|
117
|
+
[0xBF, 0x00, 0x40] => "purple",
|
118
|
+
[0x00, 0x80, 0x80] => "teal",
|
119
|
+
[0x80, 0x00, 0x80] => "violet",
|
120
|
+
[0xFF, 0xF2, 0x00] => "yellow",
|
121
|
+
}
|
122
|
+
|
123
|
+
def append(expression, separator = " ")
|
124
|
+
case expression
|
125
|
+
when Array
|
126
|
+
expression.each { |e| append(e, separator) }
|
127
|
+
when String
|
128
|
+
@latex << expression
|
129
|
+
when Symbol
|
130
|
+
@latex << expression.to_s
|
131
|
+
when AsciiMath::AST::Sequence, AsciiMath::AST::MatrixRow
|
132
|
+
c = expression.length
|
133
|
+
|
134
|
+
expression.each do |e|
|
135
|
+
c -= 1
|
136
|
+
append(e)
|
137
|
+
@latex << separator if c > 0
|
138
|
+
end
|
139
|
+
|
140
|
+
when AsciiMath::AST::Symbol
|
141
|
+
@latex << symbol(expression.value)
|
142
|
+
|
143
|
+
when AsciiMath::AST::Identifier
|
144
|
+
append_escaped(expression.value)
|
145
|
+
|
146
|
+
when AsciiMath::AST::Text
|
147
|
+
text do
|
148
|
+
append_escaped(expression.value)
|
149
|
+
end
|
150
|
+
|
151
|
+
when AsciiMath::AST::Number
|
152
|
+
@latex << expression.value
|
153
|
+
|
154
|
+
when AsciiMath::AST::Paren
|
155
|
+
parens(expression.lparen, expression.rparen, expression.expression)
|
156
|
+
|
157
|
+
when AsciiMath::AST::Group
|
158
|
+
append(expression.expression)
|
159
|
+
|
160
|
+
when AsciiMath::AST::SubSup
|
161
|
+
sub = expression.sub_expression
|
162
|
+
sup = expression.sup_expression
|
163
|
+
e = expression.base_expression
|
164
|
+
|
165
|
+
curly(e)
|
166
|
+
|
167
|
+
if sub
|
168
|
+
@latex << "_"
|
169
|
+
curly(sub)
|
170
|
+
end
|
171
|
+
|
172
|
+
if sup
|
173
|
+
@latex << "^"
|
174
|
+
curly(sup)
|
175
|
+
end
|
176
|
+
|
177
|
+
when AsciiMath::AST::UnaryOp
|
178
|
+
op = expression.operator.value
|
179
|
+
|
180
|
+
case op
|
181
|
+
when :norm
|
182
|
+
parens(:lvert, :rvert, expression.operand)
|
183
|
+
when :floor
|
184
|
+
parens(:lfloor, :rfloor, expression.operand)
|
185
|
+
when :ceil
|
186
|
+
parens(:lceiling, :rceiling, expression.operand)
|
187
|
+
when :overarc
|
188
|
+
overset do
|
189
|
+
@latex << "\\frown"
|
190
|
+
end
|
191
|
+
|
192
|
+
curly do
|
193
|
+
append(expression.operand)
|
194
|
+
end
|
195
|
+
else
|
196
|
+
macro(op) do
|
197
|
+
append(expression.operand)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
when AsciiMath::AST::BinaryOp, AsciiMath::AST::InfixOp
|
202
|
+
op = expression.operator.value
|
203
|
+
|
204
|
+
case op
|
205
|
+
when :root
|
206
|
+
macro("sqrt", expression.operand1) do
|
207
|
+
append(expression.operand2)
|
208
|
+
end
|
209
|
+
|
210
|
+
when :color
|
211
|
+
curly do
|
212
|
+
color_value = expression.operand1
|
213
|
+
red = color_value.red
|
214
|
+
green = color_value.green
|
215
|
+
blue = color_value.blue
|
216
|
+
|
217
|
+
if COLOURS.has_key? [red, green, blue]
|
218
|
+
color do
|
219
|
+
@latex << COLOURS[[red, green, blue]]
|
220
|
+
end
|
221
|
+
else
|
222
|
+
color('RGB') do
|
223
|
+
@latex << red.to_s << ',' << green.to_s << ',' << blue.to_s
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
@latex << " "
|
228
|
+
append(expression.operand2)
|
229
|
+
end
|
230
|
+
|
231
|
+
else
|
232
|
+
@latex << symbol(op)
|
233
|
+
|
234
|
+
curly do
|
235
|
+
append(expression.operand1)
|
236
|
+
end
|
237
|
+
|
238
|
+
curly do
|
239
|
+
append(expression.operand2)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
when AsciiMath::AST::Matrix
|
244
|
+
len = expression.length - 1
|
245
|
+
|
246
|
+
parens(expression.lparen, expression.rparen) do
|
247
|
+
c = expression.length
|
248
|
+
@latex << "\\begin{matrix} "
|
249
|
+
|
250
|
+
expression.each do |row|
|
251
|
+
c -= 1
|
252
|
+
append(row, " & ")
|
253
|
+
@latex << " \\\\ " if c > 0
|
254
|
+
end
|
255
|
+
|
256
|
+
@latex << " \\end{matrix}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def macro(macro, *args)
|
262
|
+
@latex << symbol(macro)
|
263
|
+
|
264
|
+
if args.length != 0
|
265
|
+
@latex << "["
|
266
|
+
append(args, "][")
|
267
|
+
@latex << "]"
|
268
|
+
end
|
269
|
+
|
270
|
+
if block_given?
|
271
|
+
curly do
|
272
|
+
yield self
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def method_missing(meth, *args, &block)
|
278
|
+
macro(meth, *args, &block)
|
279
|
+
end
|
280
|
+
|
281
|
+
def parens(lparen, rparen, content = nil, &block)
|
282
|
+
l = lparen.is_a?(AsciiMath::AST::Symbol) ? lparen.value : lparen
|
283
|
+
r = rparen.is_a?(AsciiMath::AST::Symbol) ? rparen.value : rparen
|
284
|
+
|
285
|
+
if block_given?
|
286
|
+
if l || r
|
287
|
+
@latex << "\\left " << symbol(l) << " "
|
288
|
+
yield self
|
289
|
+
@latex << " \\right " << symbol(r)
|
290
|
+
else
|
291
|
+
yield self
|
292
|
+
end
|
293
|
+
else
|
294
|
+
needs_left_right = !is_small(content)
|
295
|
+
|
296
|
+
@latex << "\\left " if needs_left_right
|
297
|
+
@latex << symbol(l) << " " if l or needs_left_right
|
298
|
+
|
299
|
+
append(content)
|
300
|
+
|
301
|
+
@latex << " \\right" if needs_left_right
|
302
|
+
@latex << " " << symbol(r) if r or needs_left_right
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def curly(expression = nil, &block)
|
307
|
+
if block_given?
|
308
|
+
@latex << ?{
|
309
|
+
yield self
|
310
|
+
@latex << ?}
|
311
|
+
else
|
312
|
+
case expression
|
313
|
+
when AsciiMath::AST::Symbol, AsciiMath::AST::Text
|
314
|
+
append(expression)
|
315
|
+
return
|
316
|
+
when AsciiMath::AST::Identifier, AsciiMath::AST::Number
|
317
|
+
if expression.value.length <= 1
|
318
|
+
append(expression)
|
319
|
+
return
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
@latex << ?{
|
324
|
+
append(expression)
|
325
|
+
@latex << ?}
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def append_escaped(text)
|
330
|
+
text.each_codepoint do |cp|
|
331
|
+
@latex << "\\" if SPECIAL_CHARACTERS.include? cp
|
332
|
+
@latex << cp
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def symbol(s)
|
337
|
+
SYMBOLS[s] || "\\#{s.to_s}"
|
338
|
+
end
|
339
|
+
|
340
|
+
def is_small(e)
|
341
|
+
case e
|
342
|
+
when AsciiMath::AST::SubSup
|
343
|
+
is_very_small(e.sub_expression) and is_very_small(e.sup_expression) and is_very_small(e.base_expression)
|
344
|
+
when AsciiMath::AST::Sequence
|
345
|
+
e.all? { |s| is_small(s) }
|
346
|
+
else
|
347
|
+
is_very_small(e)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def is_very_small(e)
|
352
|
+
case e
|
353
|
+
when AsciiMath::AST::Identifier, AsciiMath::AST::Number
|
354
|
+
e.value.length <= 1
|
355
|
+
when AsciiMath::AST::Symbol
|
356
|
+
case e.value
|
357
|
+
when :plus, :minus, :cdot, :dx, :dy, :dz, :dt, :f, :g, :mod
|
358
|
+
true
|
359
|
+
else
|
360
|
+
false
|
361
|
+
end
|
362
|
+
when AsciiMath::AST::UnaryOp
|
363
|
+
case e.operator
|
364
|
+
when :hat, :overline, :underline, :vec, :dot, :ddot, :color
|
365
|
+
is_very_small(e.operand)
|
366
|
+
else
|
367
|
+
false
|
368
|
+
end
|
369
|
+
when AsciiMath::AST::Group
|
370
|
+
is_very_small(e.expression)
|
371
|
+
when AsciiMath::AST::Sequence
|
372
|
+
e.all? { |s| is_very_small(e) }
|
373
|
+
when nil
|
374
|
+
true
|
375
|
+
else
|
376
|
+
false
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
class Expression
|
382
|
+
def to_latex
|
383
|
+
LatexBuilder.new().append_expression(ast).to_s
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|