asciimath 1.0.6 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.travis.yml +6 -8
- data/AST.adoc +457 -0
- data/CHANGELOG.adoc +44 -1
- data/README.adoc +66 -3
- data/asciimath.gemspec +7 -5
- 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 +148 -119
- data/lib/asciimath/latex.rb +400 -0
- data/lib/asciimath/markup.rb +479 -0
- data/lib/asciimath/mathml.rb +196 -83
- data/lib/asciimath/parser.rb +531 -318
- 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 +598 -147
- 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 +108 -11
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,168 +14,180 @@ 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
|
|
23
28
|
|
|
24
29
|
private
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
+
ZWJ = "\u200D"
|
|
32
|
+
|
|
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|
|
|
93
100
|
row do
|
|
94
|
-
|
|
95
|
-
# NOTE: This does not currently consider the size of each element within the matrix.
|
|
96
|
-
brace_height = "font-size: " + expression[:rows].length.to_s + "00%;"
|
|
97
|
-
|
|
98
|
-
if expression[:lparen]
|
|
99
|
-
brace(expression[:lparen], {:style => brace_height})
|
|
100
|
-
else
|
|
101
|
-
blank("‍")
|
|
102
|
-
end
|
|
103
|
-
matrix_width = "grid-template-columns:repeat(" + expression[:rows][0].length.to_s + ",1fr);"
|
|
104
|
-
matrix_height = "grid-template-rows:repeat(" + expression[:rows].length.to_s + ",1fr);"
|
|
105
|
-
|
|
106
|
-
matrix({:style => (matrix_width + matrix_height)}) do
|
|
107
|
-
expression[:rows].each do |row|
|
|
108
|
-
row.each do |col|
|
|
109
|
-
row do
|
|
110
|
-
append(col)
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
if expression[:rparen]
|
|
116
|
-
brace(expression[:rparen], {:style => brace_height})
|
|
117
|
-
else
|
|
118
|
-
blank("‍")
|
|
119
|
-
end
|
|
101
|
+
append(col)
|
|
120
102
|
end
|
|
103
|
+
end
|
|
121
104
|
end
|
|
105
|
+
end
|
|
106
|
+
if rparen
|
|
107
|
+
brace(rparen, {:style => brace_height})
|
|
108
|
+
else
|
|
109
|
+
blank(ZWJ)
|
|
110
|
+
end
|
|
122
111
|
end
|
|
123
112
|
end
|
|
124
|
-
|
|
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
|
+
|
|
125
141
|
def append_subsup(base, sub, sup)
|
|
126
142
|
append(base)
|
|
127
143
|
subsup do
|
|
128
144
|
if sup
|
|
129
145
|
smaller do
|
|
130
|
-
append(sup
|
|
146
|
+
append(sup)
|
|
131
147
|
end
|
|
132
148
|
else
|
|
133
|
-
smaller(
|
|
149
|
+
smaller(ZWJ)
|
|
134
150
|
end
|
|
135
151
|
if sub
|
|
136
152
|
smaller do
|
|
137
|
-
append(sub
|
|
153
|
+
append(sub)
|
|
138
154
|
end
|
|
139
155
|
else
|
|
140
|
-
smaller(
|
|
156
|
+
smaller(ZWJ)
|
|
141
157
|
end
|
|
142
158
|
end
|
|
143
159
|
end
|
|
144
|
-
|
|
160
|
+
|
|
145
161
|
def append_underover(base, under, over)
|
|
146
|
-
|
|
162
|
+
# TODO: Handle over/under braces in some way? SVG maybe?
|
|
163
|
+
blank(ZWJ)
|
|
147
164
|
underover do
|
|
148
165
|
smaller do
|
|
149
166
|
if over
|
|
150
|
-
append(over
|
|
167
|
+
append(over)
|
|
151
168
|
else
|
|
152
|
-
blank(
|
|
169
|
+
blank(ZWJ)
|
|
153
170
|
end
|
|
154
171
|
end
|
|
155
172
|
append(base)
|
|
156
173
|
smaller do
|
|
157
174
|
if under
|
|
158
|
-
append(under
|
|
175
|
+
append(under)
|
|
159
176
|
else
|
|
160
|
-
blank(
|
|
177
|
+
blank(ZWJ)
|
|
161
178
|
end
|
|
162
179
|
end
|
|
163
180
|
end
|
|
164
181
|
end
|
|
165
|
-
|
|
182
|
+
|
|
166
183
|
def append_fraction(numerator, denominator)
|
|
167
|
-
blank(
|
|
184
|
+
blank(ZWJ)
|
|
168
185
|
fraction do
|
|
169
186
|
fraction_row do
|
|
170
187
|
fraction_cell do
|
|
171
188
|
smaller do
|
|
172
189
|
row do
|
|
173
|
-
append(numerator
|
|
190
|
+
append(numerator)
|
|
174
191
|
end
|
|
175
192
|
end
|
|
176
193
|
end
|
|
@@ -179,7 +196,7 @@ module AsciiMath
|
|
|
179
196
|
fraction_cell do
|
|
180
197
|
smaller do
|
|
181
198
|
row do
|
|
182
|
-
append(denominator
|
|
199
|
+
append(denominator)
|
|
183
200
|
end
|
|
184
201
|
end
|
|
185
202
|
end
|
|
@@ -190,7 +207,7 @@ module AsciiMath
|
|
|
190
207
|
def method_missing(meth, *args, &block)
|
|
191
208
|
tag(meth, *args, &block)
|
|
192
209
|
end
|
|
193
|
-
|
|
210
|
+
|
|
194
211
|
def tag(tag, *args)
|
|
195
212
|
attrs = args.last.is_a?(Hash) ? args.pop : {}
|
|
196
213
|
text = args.last.is_a?(String) ? args.pop : ''
|
|
@@ -203,7 +220,19 @@ module AsciiMath
|
|
|
203
220
|
|
|
204
221
|
if block_given? || text
|
|
205
222
|
@html << '>'
|
|
206
|
-
|
|
223
|
+
text.each_codepoint do |cp|
|
|
224
|
+
if cp == 38
|
|
225
|
+
@html << "&"
|
|
226
|
+
elsif cp == 60
|
|
227
|
+
@html << "<"
|
|
228
|
+
elsif cp == 62
|
|
229
|
+
@html << ">"
|
|
230
|
+
elsif cp > 127
|
|
231
|
+
@html << "&#x#{cp.to_s(16).upcase};"
|
|
232
|
+
else
|
|
233
|
+
@html << cp
|
|
234
|
+
end
|
|
235
|
+
end
|
|
207
236
|
yield if block_given?
|
|
208
237
|
@html << '</span>'
|
|
209
238
|
else
|
|
@@ -214,7 +243,7 @@ module AsciiMath
|
|
|
214
243
|
|
|
215
244
|
class Expression
|
|
216
245
|
def to_html(prefix = "", inline = true, attrs = {})
|
|
217
|
-
HTMLBuilder.new(prefix).append_expression(
|
|
246
|
+
HTMLBuilder.new(:prefix => prefix, :inline => inline).append_expression(ast, attrs).to_s
|
|
218
247
|
end
|
|
219
248
|
end
|
|
220
|
-
end
|
|
249
|
+
end
|
|
@@ -0,0 +1,400 @@
|
|
|
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
|
+
}.freeze
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
SPECIAL_CHARACTERS = [?&, ?%, ?$, ?#, ?_, ?{, ?}, ?~, ?^, ?[, ?]].map(&:ord)
|
|
104
|
+
|
|
105
|
+
COLOURS = {
|
|
106
|
+
[0xFF, 0xFF, 0xFF] => "white",
|
|
107
|
+
[0xFF, 0x00, 0x00] => "red",
|
|
108
|
+
[0x00, 0xFF, 0x00] => "green",
|
|
109
|
+
[0x00, 0x00, 0xFF] => "blue",
|
|
110
|
+
[0xBF, 0x80, 0x40] => "brown",
|
|
111
|
+
[0x00, 0xAD, 0xEF] => "cyan",
|
|
112
|
+
[0x40, 0x40, 0x40] => "darkgray",
|
|
113
|
+
[0x80, 0x80, 0x80] => "gray",
|
|
114
|
+
[0xBF, 0xBF, 0xBF] => "lightgray",
|
|
115
|
+
[0xA4, 0xDB, 0x00] => "lime",
|
|
116
|
+
[0xE9, 0x00, 0x8A] => "magenta",
|
|
117
|
+
[0x8E, 0x86, 0x00] => "olive",
|
|
118
|
+
[0xFF, 0x80, 0x00] => "orange",
|
|
119
|
+
[0xFF, 0xBF, 0xBF] => "pink",
|
|
120
|
+
[0xBF, 0x00, 0x40] => "purple",
|
|
121
|
+
[0x00, 0x80, 0x80] => "teal",
|
|
122
|
+
[0x80, 0x00, 0x80] => "violet",
|
|
123
|
+
[0xFF, 0xF2, 0x00] => "yellow",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
def append(expression, separator = " ")
|
|
127
|
+
case expression
|
|
128
|
+
when Array
|
|
129
|
+
expression.each { |e| append(e, separator) }
|
|
130
|
+
when String
|
|
131
|
+
@latex << expression
|
|
132
|
+
when Symbol
|
|
133
|
+
@latex << expression.to_s
|
|
134
|
+
when AsciiMath::AST::Sequence, AsciiMath::AST::MatrixRow
|
|
135
|
+
c = expression.length
|
|
136
|
+
|
|
137
|
+
expression.each do |e|
|
|
138
|
+
c -= 1
|
|
139
|
+
append(e)
|
|
140
|
+
@latex << separator if c > 0
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
when AsciiMath::AST::Symbol
|
|
144
|
+
@latex << resolve_symbol(expression.value)
|
|
145
|
+
|
|
146
|
+
when AsciiMath::AST::Identifier
|
|
147
|
+
append_escaped(expression.value)
|
|
148
|
+
|
|
149
|
+
when AsciiMath::AST::Text
|
|
150
|
+
text do
|
|
151
|
+
append_escaped(expression.value)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
when AsciiMath::AST::Number
|
|
155
|
+
@latex << expression.value
|
|
156
|
+
|
|
157
|
+
when AsciiMath::AST::Paren
|
|
158
|
+
parens(expression.lparen, expression.rparen, expression.expression)
|
|
159
|
+
|
|
160
|
+
when AsciiMath::AST::Group
|
|
161
|
+
append(expression.expression)
|
|
162
|
+
|
|
163
|
+
when AsciiMath::AST::SubSup
|
|
164
|
+
sub = expression.sub_expression
|
|
165
|
+
sup = expression.sup_expression
|
|
166
|
+
e = expression.base_expression
|
|
167
|
+
|
|
168
|
+
curly(e)
|
|
169
|
+
|
|
170
|
+
if sub
|
|
171
|
+
@latex << "_"
|
|
172
|
+
curly(sub)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
if sup
|
|
176
|
+
@latex << "^"
|
|
177
|
+
curly(sup)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
when AsciiMath::AST::UnaryOp
|
|
181
|
+
op = expression.operator.value
|
|
182
|
+
|
|
183
|
+
case op
|
|
184
|
+
when :norm
|
|
185
|
+
parens(:lvert, :rvert, expression.operand)
|
|
186
|
+
when :floor
|
|
187
|
+
parens(:lfloor, :rfloor, expression.operand)
|
|
188
|
+
when :ceil
|
|
189
|
+
parens(:lceiling, :rceiling, expression.operand)
|
|
190
|
+
when :overarc
|
|
191
|
+
overset do
|
|
192
|
+
@latex << "\\frown"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
curly do
|
|
196
|
+
append(expression.operand)
|
|
197
|
+
end
|
|
198
|
+
else
|
|
199
|
+
macro(op) do
|
|
200
|
+
append(expression.operand)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
when AsciiMath::AST::BinaryOp, AsciiMath::AST::InfixOp
|
|
205
|
+
op = expression.operator.value
|
|
206
|
+
|
|
207
|
+
case op
|
|
208
|
+
when :root
|
|
209
|
+
macro("sqrt", expression.operand1) do
|
|
210
|
+
append(expression.operand2)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
when :color
|
|
214
|
+
curly do
|
|
215
|
+
color_value = expression.operand1
|
|
216
|
+
red = color_value.red
|
|
217
|
+
green = color_value.green
|
|
218
|
+
blue = color_value.blue
|
|
219
|
+
|
|
220
|
+
if COLOURS.has_key? [red, green, blue]
|
|
221
|
+
color do
|
|
222
|
+
@latex << COLOURS[[red, green, blue]]
|
|
223
|
+
end
|
|
224
|
+
else
|
|
225
|
+
color('RGB') do
|
|
226
|
+
@latex << red.to_s << ',' << green.to_s << ',' << blue.to_s
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
@latex << " "
|
|
231
|
+
append(expression.operand2)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
else
|
|
235
|
+
@latex << resolve_symbol(op)
|
|
236
|
+
|
|
237
|
+
curly do
|
|
238
|
+
append(expression.operand1)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
curly do
|
|
242
|
+
append(expression.operand2)
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
when AsciiMath::AST::Matrix
|
|
247
|
+
len = expression.length - 1
|
|
248
|
+
|
|
249
|
+
parens(expression.lparen, expression.rparen) do
|
|
250
|
+
c = expression.length
|
|
251
|
+
@latex << "\\begin{matrix} "
|
|
252
|
+
|
|
253
|
+
expression.each do |row|
|
|
254
|
+
c -= 1
|
|
255
|
+
append(row, " & ")
|
|
256
|
+
@latex << " \\\\ " if c > 0
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
@latex << " \\end{matrix}"
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def macro(macro, *args)
|
|
265
|
+
@latex << resolve_symbol(macro)
|
|
266
|
+
|
|
267
|
+
if args.length != 0
|
|
268
|
+
@latex << "["
|
|
269
|
+
append(args, "][")
|
|
270
|
+
@latex << "]"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
if block_given?
|
|
274
|
+
curly do
|
|
275
|
+
yield self
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def method_missing(meth, *args, &block)
|
|
281
|
+
macro(meth, *args, &block)
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def parens(lparen, rparen, content = nil, &block)
|
|
285
|
+
l = lparen.is_a?(AsciiMath::AST::Symbol) ? lparen.value : lparen
|
|
286
|
+
r = rparen.is_a?(AsciiMath::AST::Symbol) ? rparen.value : rparen
|
|
287
|
+
|
|
288
|
+
if block_given?
|
|
289
|
+
if l || r
|
|
290
|
+
@latex << "\\left " << resolve_symbol(l) << " "
|
|
291
|
+
yield self
|
|
292
|
+
@latex << " \\right " << resolve_symbol(r)
|
|
293
|
+
else
|
|
294
|
+
yield self
|
|
295
|
+
end
|
|
296
|
+
else
|
|
297
|
+
needs_left_right = !is_small(content)
|
|
298
|
+
|
|
299
|
+
@latex << "\\left " if needs_left_right
|
|
300
|
+
@latex << resolve_symbol(l) << " " if l or needs_left_right
|
|
301
|
+
|
|
302
|
+
append(content)
|
|
303
|
+
|
|
304
|
+
@latex << " \\right" if needs_left_right
|
|
305
|
+
@latex << " " << resolve_symbol(r) if r or needs_left_right
|
|
306
|
+
end
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def curly(expression = nil, &block)
|
|
310
|
+
if block_given?
|
|
311
|
+
@latex << ?{
|
|
312
|
+
yield self
|
|
313
|
+
@latex << ?}
|
|
314
|
+
else
|
|
315
|
+
case expression
|
|
316
|
+
when AsciiMath::AST::Symbol, AsciiMath::AST::Text
|
|
317
|
+
append(expression)
|
|
318
|
+
return
|
|
319
|
+
when AsciiMath::AST::Identifier, AsciiMath::AST::Number
|
|
320
|
+
if expression.value.length <= 1
|
|
321
|
+
append(expression)
|
|
322
|
+
return
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
@latex << ?{
|
|
327
|
+
append(expression)
|
|
328
|
+
@latex << ?}
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def append_escaped(text)
|
|
333
|
+
text.each_codepoint do |cp|
|
|
334
|
+
@latex << "\\" if SPECIAL_CHARACTERS.include? cp
|
|
335
|
+
@latex << cp
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
def resolve_symbol(s)
|
|
340
|
+
symbol = @symbol_table[s]
|
|
341
|
+
|
|
342
|
+
case symbol
|
|
343
|
+
when String
|
|
344
|
+
return symbol
|
|
345
|
+
when Hash
|
|
346
|
+
return symbol[:value]
|
|
347
|
+
when nil
|
|
348
|
+
return "\\#{s.to_s}"
|
|
349
|
+
else
|
|
350
|
+
raise "Invalid entry in symbol table"
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def is_small(e)
|
|
355
|
+
case e
|
|
356
|
+
when AsciiMath::AST::SubSup
|
|
357
|
+
is_very_small(e.sub_expression) and is_very_small(e.sup_expression) and is_very_small(e.base_expression)
|
|
358
|
+
when AsciiMath::AST::Sequence
|
|
359
|
+
e.all? { |s| is_small(s) }
|
|
360
|
+
else
|
|
361
|
+
is_very_small(e)
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def is_very_small(e)
|
|
366
|
+
case e
|
|
367
|
+
when AsciiMath::AST::Identifier, AsciiMath::AST::Number
|
|
368
|
+
e.value.length <= 1
|
|
369
|
+
when AsciiMath::AST::Symbol
|
|
370
|
+
case e.value
|
|
371
|
+
when :plus, :minus, :cdot, :dx, :dy, :dz, :dt, :f, :g, :mod
|
|
372
|
+
true
|
|
373
|
+
else
|
|
374
|
+
false
|
|
375
|
+
end
|
|
376
|
+
when AsciiMath::AST::UnaryOp
|
|
377
|
+
case e.operator
|
|
378
|
+
when :hat, :overline, :underline, :vec, :dot, :ddot, :color
|
|
379
|
+
is_very_small(e.operand)
|
|
380
|
+
else
|
|
381
|
+
false
|
|
382
|
+
end
|
|
383
|
+
when AsciiMath::AST::Group
|
|
384
|
+
is_very_small(e.expression)
|
|
385
|
+
when AsciiMath::AST::Sequence
|
|
386
|
+
e.all? { |s| is_very_small(e) }
|
|
387
|
+
when nil
|
|
388
|
+
true
|
|
389
|
+
else
|
|
390
|
+
false
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
class Expression
|
|
396
|
+
def to_latex(symbol_table = nil)
|
|
397
|
+
LatexBuilder.new(symbol_table).append_expression(ast).to_s
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|