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