1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Morten Nielsen <mortenb@gmail.com>
5 *
6 */
7
8if(!defined('DOKU_INC')) die();
9if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
10require_once(DOKU_PLUGIN.'syntax.php');
11
12class syntax_plugin_masciimath extends DokuWiki_Syntax_Plugin {
13
14    public $syms;
15    public $parsestr;
16
17    function getType(){ return 'substition'; }
18    function getSort(){ return 300; }
19    function connectTo($mode) { $this->Lexer->addSpecialPattern('`.*?`',$mode,'plugin_masciimath'); }
20
21    function setup_syms() {
22        $this->syms = array(
23
24        # Array of associative arrays, der rummer disse keys:
25        #   'ascii' er token i rå ascii-form
26        #   'ml' er token på string-form, med evt yderste parantes bevaret
27        #   'ml_nobr' er token på string-form med evt yderste parantes skrællet af (medmindre det er pipes)
28        #   'ml_sign' og 'ml_op' er kode for symboler i sekundær rolle, som sign (unært minus) og som svag operator (pipe)
29        #   'row' er token som array af komma-separerede elementer (kun til brug for matrix-logikken)
30        #   'type' bestemmer en operators type:
31        #      'op' er minimal præ- eller infix-operator der står som symbol i en row; parseren opfatter et påfølgende minus som unær operator
32        #      'leftbracket' og 'rightbracket' er parantestegn, dog ikke '|' som får særbehandling
33        #      'infix' er operatorer som ^, _, /, og //
34        #      'unary' er operatorer som sqrt, hat og fr
35        #      'binary' er operatorer som 'root' og 'stackrel'
36        #      'func' er operatorer som sin, cos og lim (der binder stærkt)
37        #      'magnum' er operatorer som int, sum og prod (der binder svagt)
38        #   'underover' er sand for operatorer som prod og lim, der foretrækker at have sub/super-script stående "under/over" sig
39        #   'wrap_left' og 'wrap_right' forklarer hvordan en operator implementeres
40
41        // Signs ('sign')
42        array ('ascii' => '+', 'ml' => '<mo>&plus;</mo>', 'type' => 'sign', 'ml_sign' => '<mo>&plus;</mo>'),
43        array ('ascii' => '+ ', 'ml' => '<mo>&plus;</mo>'),
44        array ('ascii' => '-', 'ml' => '<mo>&minus;</mo>', 'type' => 'sign', 'ml_sign' => '<mo lspace="1px" rspace="1px">&#x2010;</mo>'),   # &#x2010; is HYPHEN
45        array ('ascii' => '- ', 'ml' => '<mo>&minus;</mo>'),
46        array ('ascii' => '+-', 'ml' => '<mo>&plusmn;</mo>', 'type' => 'sign', 'ml_sign' => '<mo>&plusmn;</mo>'),
47        array ('ascii' => '+- ', 'ml' => '<mo>&plusmn;</mo>'),
48
49        // Symbolic operators ('op')
50        array ('ascii' => ',', 'ml' => '<mo>,</mo>', 'type' => 'op'),
51        array ('ascii' => ':', 'ml' => '<mo>:</mo>', 'type' => 'op'),
52        array ('ascii' => ';', 'ml' => '<mo>;</mo>', 'type' => 'op'),
53        array ('ascii' => '=', 'ml' => '<mo>=</mo>', 'type' => 'op'),
54        array ('ascii' => '\\\\', 'ml' => '<mo>\</mo>', 'type' => 'op'), # backslash
55        array ('ascii' => '\ ', 'ml' => '<mo>&nbsp;</mo>', 'type' => 'op'),
56        array ('ascii' => '*', 'ml' => '<mo>&sdot;</mo>', 'type' => 'op'),
57        array ('ascii' => '**', 'ml' => '<mo>&#x22C6;</mo>', 'type' => 'op'),
58        array ('ascii' => 'xx', 'ml' => '<mo>&times;</mo>', 'type' => 'op'),
59        array ('ascii' => 'times', 'ml' => '<mo>&times;</mo>', 'type' => 'op'),
60        array ('ascii' => '-:', 'ml' => '<mo>&divide;</mo>', 'type' => 'op'),
61        array ('ascii' => '@', 'ml' => '<mo>&#x2218;</mo>', 'type' => 'op'),
62        array ('ascii' => 'o+', 'ml' => '<mo>&oplus;</mo>', 'type' => 'op'),
63        array ('ascii' => 'ox', 'ml' => '<mo>&otimes;</mo>', 'type' => 'op'),
64        array ('ascii' => 'o.', 'ml' => '<mo>&#x2299;</mo>', 'type' => 'op'),
65        array ('ascii' => '^^', 'ml' => '<mo>&and;</mo>', 'type' => 'op'),
66        array ('ascii' => 'vv', 'ml' => '<mo>&or;</mo>', 'type' => 'op'),
67        array ('ascii' => 'nn', 'ml' => '<mo>&cap;</mo>', 'type' => 'op'),
68        array ('ascii' => 'uu', 'ml' => '<mo>&cup;</mo>', 'type' => 'op'),
69        array ('ascii' => '!=', 'ml' => '<mo>&ne;</mo>', 'type' => 'op'),
70        array ('ascii' => 'ne', 'ml' => '<mo>&ne;</mo>', 'type' => 'op'),
71        array ('ascii' => '<', 'ml' => '<mo>&lt;</mo>', 'type' => 'op'),
72        array ('ascii' => 'lt', 'ml' => '<mo>&lt;</mo>', 'type' => 'op'),
73        array ('ascii' => '>', 'ml' => '<mo>&gt;</mo>', 'type' => 'op'),
74        array ('ascii' => 'gt', 'ml' => '<mo>&gt;</mo>', 'type' => 'op'),
75        array ('ascii' => '<=', 'ml' => '<mo>&le;</mo>', 'type' => 'op'),
76        array ('ascii' => 'le', 'ml' => '<mo>&le;</mo>', 'type' => 'op'),
77        array ('ascii' => '>=', 'ml' => '<mo>&ge;</mo>', 'type' => 'op'),
78        array ('ascii' => 'ge', 'ml' => '<mo>&ge;</mo>', 'type' => 'op'),
79        array ('ascii' => '<<', 'ml' => '<mo>&#x226a;</mo>', 'type' => 'op'),
80        array ('ascii' => '>>', 'ml' => '<mo>&#x226b;</mo>', 'type' => 'op'),
81        array ('ascii' => '>-', 'ml' => '<mo>&#x227B;</mo>', 'type' => 'op'),
82        array ('ascii' => '-<', 'ml' => '<mo>&#x227A;</mo>', 'type' => 'op'),
83        array ('ascii' => 'in', 'ml' => '<mo>&isin;</mo>', 'type' => 'op'),
84        array ('ascii' => '!in', 'ml' => '<mo>&notin;</mo>', 'type' => 'op'),
85        array ('ascii' => 'sub', 'ml' => '<mo>&sub;</mo>', 'type' => 'op'),
86        array ('ascii' => 'sup', 'ml' => '<mo>&sup;</mo>', 'type' => 'op'),
87        array ('ascii' => '!sub', 'ml' => '<mo>&nsub;</mo>', 'type' => 'op'),
88        array ('ascii' => 'nsub', 'ml' => '<mo>&nsub;</mo>', 'type' => 'op'),
89        array ('ascii' => 'sube', 'ml' => '<mo>&sube;</mo>', 'type' => 'op'),
90        array ('ascii' => 'supe', 'ml' => '<mo>&supe;</mo>', 'type' => 'op'),
91        array ('ascii' => '-=', 'ml' => '<mo>&equiv;</mo>', 'type' => 'op'),
92        array ('ascii' => '~=', 'ml' => '<mo>&cong;</mo>', 'type' => 'op'),
93        array ('ascii' => '~~', 'ml' => '<mo>&asymp;</mo>', 'type' => 'op'),
94        array ('ascii' => 'approx', 'ml' => '<mo>&asymp;</mo>', 'type' => 'op'),
95        array ('ascii' => 'prop', 'ml' => '<mo>&prop;</mo>', 'type' => 'op'),
96        array ('ascii' => 'not', 'ml' => '<mo>&not;</mo>', 'type' => 'op'),
97        array ('ascii' => 'AA', 'ml' => '<mo>&forall;</mo>', 'type' => 'op'),
98        array ('ascii' => 'EE', 'ml' => '<mo>&exist;</mo>', 'type' => 'op'),
99        array ('ascii' => '|--', 'ml' => '<mo>&#x22A2;</mo>', 'type' => 'op'),
100        array ('ascii' => '|==', 'ml' => '<mo>&#x22A8;</mo>', 'type' => 'op'),
101        array ('ascii' => '/_', 'ml' => '<mo>&ang;</mo>', 'type' => 'op'),
102        array ('ascii' => 'ang', 'ml' => '<mo>&ang;</mo>', 'type' => 'op'),
103        array ('ascii' => ':.', 'ml' => '<mo>&there4;</mo>', 'type' => 'op'),
104        array ('ascii' => '<-', 'ml' => '<mo>&larr;</mo>', 'type' => 'op'),
105        array ('ascii' => 'larr', 'ml' => '<mo>&larr;</mo>', 'type' => 'op'),
106        array ('ascii' => 'uarr', 'ml' => '<mo>&uarr;</mo>', 'type' => 'op'),
107        array ('ascii' => 'rarr', 'ml' => '<mo>&rarr;</mo>', 'type' => 'op'),
108        array ('ascii' => '->', 'ml' => '<mo>&rarr;</mo>', 'type' => 'op'),
109        array ('ascii' => 'darr', 'ml' => '<mo>&darr;</mo>', 'type' => 'op'),
110        array ('ascii' => '<->', 'ml' => '<mo>&harr;</mo>', 'type' => 'op'),
111        array ('ascii' => 'harr', 'ml' => '<mo>&harr;</mo>', 'type' => 'op'),
112        array ('ascii' => '|->', 'ml' => '<mo>&#x21A6;</mo>', 'type' => 'op'),
113        array ('ascii' => 'rArr', 'ml' => '<mo>&rArr;</mo>', 'type' => 'op'),
114        array ('ascii' => '=>', 'ml' => '<mo>&rArr;</mo>', 'type' => 'op'),
115        array ('ascii' => 'lArr', 'ml' => '<mo>&lArr;</mo>', 'type' => 'op'),
116        array ('ascii' => 'hArr', 'ml' => '<mo>&hArr;</mo>', 'type' => 'op'),
117        array ('ascii' => 'iff', 'ml' => '<mo>&hArr;</mo>', 'type' => 'op'),
118        array ('ascii' => '<=>', 'ml' => '<mo>&hArr;</mo>', 'type' => 'op'),
119        array ('ascii' => '|__', 'ml' => '<mo>&lfloor;</mo>', 'type' => 'op'),
120        array ('ascii' => '__|', 'ml' => '<mo>&rfloor;</mo>', 'type' => 'op'),
121        array ('ascii' => '|~', 'ml' => '<mo>&lceil;</mo>', 'type' => 'op'),
122        array ('ascii' => '~|', 'ml' => '<mo>&rceil;</mo>', 'type' => 'op'),
123        array ('ascii' => 'if', 'ml' => '<mi>if</mi>', 'type' => 'op'),
124        array ('ascii' => 'and', 'ml' => '<mi>and</mi>', 'type' => 'op'),
125        array ('ascii' => 'or', 'ml' => '<mi>or</mi>', 'type' => 'op'),
126
127        // Brackets ('leftbracket' and 'rightbracket')
128        array ('ascii' => '(', 'ml' => '<mo>(</mo>', 'type' => 'leftbracket'),
129        array ('ascii' => ')', 'ml' => '<mo>)</mo>', 'type' => 'rightbracket'),
130        array ('ascii' => '[', 'ml' => '<mo>[</mo>', 'type' => 'leftbracket'),
131        array ('ascii' => ']', 'ml' => '<mo>]</mo>', 'type' => 'rightbracket'),
132        array ('ascii' => '{', 'ml' => '<mo>{</mo>', 'type' => 'leftbracket'),
133        array ('ascii' => '}', 'ml' => '<mo>}</mo>', 'type' => 'rightbracket'),
134        array ('ascii' => '(:', 'ml' => '<mo>&lang;</mo>', 'type' => 'leftbracket'),
135        array ('ascii' => ':)', 'ml' => '<mo>&rang;</mo>', 'type' => 'rightbracket'),
136        array ('ascii' => '{:', 'ml' => '', 'type' => 'leftbracket'),
137        array ('ascii' => ':}', 'ml' => '', 'type' => 'rightbracket'),
138        array ('ascii' => '|', 'ml' => '<mo>|</mo>', 'ml_op' => '<mo form="infix" fence="false" separator="true">|</mo>'),
139
140        // Special operators
141        array ('ascii' => '^', 'ml' => '<mo>^</mo>', 'type' => 'infix', 'wrap_left' => '<msup>', 'wrap_right' => '</msup>'),
142        array ('ascii' => '_',  'ml' => '<mo>_</mo>', 'type' => 'infix', 'wrap_left' => '<msub>', 'wrap_right' => '</msub>'),
143        array ('ascii' => '/', 'ml' => '<mo>/</mo>', 'type' => 'infix', 'wrap_left' => '<mfrac>', 'wrap_right' => '</mfrac>'),
144        array ('ascii' => '//', 'ml' => '<mo>&frasl;</mo>', 'type' => 'infix', 'wrap_left' => '<mfrac bevelled="true">', 'wrap_right' => '</mfrac>'),
145        array ('ascii' => 'sqrt', 'type' => 'unary', 'wrap_left' => '<msqrt>', 'wrap_right' => '</msqrt>'),
146        array ('ascii' => 'hat', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>^</mo></mover>'),
147        array ('ascii' => 'bar', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>&macr;</mo></mover>'),
148        array ('ascii' => 'ul', 'type' => 'unary', 'wrap_left' => '<munder>', 'wrap_right' => '<mo>&UnderBar;</mo></munder>'),
149        array ('ascii' => 'vec', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>&rarr;</mo></mover>'),
150        array ('ascii' => 'dot', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>.</mo></mover>'),
151        array ('ascii' => 'ddot', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>..</mo></mover>'),
152        array ('ascii' => 'root', 'type' => 'binary', 'wrap_left' => '<mroot>', 'wrap_right' => '</mroot>'),
153        array ('ascii' => 'stackrel', 'type' => 'binary', 'wrap_left' => '<mover>', 'wrap_right' => '</mover>'),
154
155        // Mathvariant operators
156        array ('ascii' => 'bb', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="bold">', 'wrap_right' => '</mstyle>'),
157        array ('ascii' => 'bbb', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="double-struck">', 'wrap_right' => '</mstyle>'),
158        array ('ascii' => 'cc', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="script">', 'wrap_right' => '</mstyle>'),
159        array ('ascii' => 'fr', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="fraktur">', 'wrap_right' => '</mstyle>'),
160        array ('ascii' => 'tt', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="monospace">', 'wrap_right' => '</mstyle>'),
161        array ('ascii' => 'sf', 'type' => 'unary', 'wrap_left' => '<mstyle mathvariant="sans-serif">', 'wrap_right' => '</mstyle>'),
162
163        // Magnum operators
164        array ('ascii' => 'sum', 'ml' => '<mo>&sum;</mo>', 'type' => 'magnum', 'underover' => true),
165        array ('ascii' => 'prod', 'ml' => '<mo>&prod;</mo>', 'type' => 'magnum', 'underover' => true),
166        array ('ascii' => '^^^', 'ml' => '<mo>&#x22C0;</mo>', 'type' => 'magnum', 'underover' => true),
167        array ('ascii' => 'vvv', 'ml' => '<mo>&#x22C1;</mo>', 'type' => 'magnum', 'underover' => true),
168        array ('ascii' => 'nnn', 'ml' => '<mo>&#x22C2;</mo>', 'type' => 'magnum', 'underover' => true),
169        array ('ascii' => 'uuu', 'ml' => '<mo>&#x22C3;</mo>', 'type' => 'magnum', 'underover' => true),
170        array ('ascii' => 'int', 'ml' => '<mo>&int;</mo>', 'type' => 'magnum'),
171        array ('ascii' => 'oint', 'ml' => '<mo>&#x222E;</mo>', 'type' => 'magnum'),
172        array ('ascii' => 'ointoint', 'ml' => '<mo>&#x222F;</mo>', 'type' => 'magnum'),   # MortenB's idea
173
174        // Standard functions
175        array ('ascii' => 'sin', 'ml' => '<mi>sin</mi>', 'type' => 'func'),
176        array ('ascii' => 'cos', 'ml' => '<mi>cos</mi>', 'type' => 'func'),
177        array ('ascii' => 'tan', 'ml' => '<mi>tan</mi>', 'type' => 'func'),
178        array ('ascii' => 'arcsin', 'ml' => '<mi>arcsin</mi>', 'type' => 'func'),
179        array ('ascii' => 'arccos', 'ml' => '<mi>arccos</mi>', 'type' => 'func'),
180        array ('ascii' => 'arctan', 'ml' => '<mi>arctan</mi>', 'type' => 'func'),
181        array ('ascii' => 'sinh', 'ml' => '<mi>sinh</mi>', 'type' => 'func'),
182        array ('ascii' => 'cosh', 'ml' => '<mi>cosh</mi>', 'type' => 'func'),
183        array ('ascii' => 'tanh', 'ml' => '<mi>tanh</mi>', 'type' => 'func'),
184        array ('ascii' => 'cot', 'ml' => '<mi>cot</mi>', 'type' => 'func'),
185        array ('ascii' => 'sec', 'ml' => '<mi>sec</mi>', 'type' => 'func'),
186        array ('ascii' => 'csc', 'ml' => '<mi>csc</mi>', 'type' => 'func'),
187        array ('ascii' => 'coth', 'ml' => '<mi>coth</mi>', 'type' => 'func'),
188        array ('ascii' => 'sech', 'ml' => '<mi>sech</mi>', 'type' => 'func'),
189        array ('ascii' => 'csch', 'ml' => '<mi>csch</mi>', 'type' => 'func'),
190        array ('ascii' => 'exp', 'ml' => '<mi>exp</mi>', 'type' => 'func'),
191        array ('ascii' => 'log', 'ml' => '<mi>log</mi>', 'type' => 'func'),
192        array ('ascii' => 'ln', 'ml' => '<mi>ln</mi>', 'type' => 'func'),
193        array ('ascii' => 'det', 'ml' => '<mi>det</mi>', 'type' => 'func'),
194        array ('ascii' => 'dim', 'ml' => '<mi>dim</mi>', 'type' => 'func'),
195        array ('ascii' => 'mod', 'ml' => '<mi>mod</mi>', 'type' => 'func'),
196        array ('ascii' => 'min', 'ml' => '<mi>min</mi>', 'type' => 'func', 'underover' => true),
197        array ('ascii' => 'max', 'ml' => '<mi>max</mi>', 'type' => 'func', 'underover' => true),
198        array ('ascii' => 'lim', 'ml' => '<mi>lim</mi>', 'type' => 'func', 'underover' => true),
199
200        // Simple symbols
201        array ('ascii' => '\'', 'ml' => '<mo>\'</mo>'), # single quote
202        array ('ascii' => '!', 'ml' => '<mo>!</mo>'),
203        array ('ascii' => 'O/', 'ml' => '<mo>&empty;</mo>'),
204        array ('ascii' => 'del', 'ml' => '<mo>&part;</mo>'),
205        array ('ascii' => 'grad', 'ml' => '<mo>&nabla;</mo>'),
206        array ('ascii' => 'oo', 'ml' => '<mo>&infin;</mo>'),
207        array ('ascii' => 'aleph', 'ml' => '<mo>&alefsym;</mo>'),
208        array ('ascii' => '_|_', 'ml' => '<mo>&perp;</mo>'),
209        array ('ascii' => 'TT', 'ml' => '<mo>&#x22A4;</mo>'),
210        array ('ascii' => 'deg', 'ml' => '<mo>&deg;</mo>'),
211        array ('ascii' => 'diamond', 'ml' => '<mo>&#x22C4;</mo>'),
212        array ('ascii' => 'square', 'ml' => '<mo>&#x25A1;</mo>'),
213        array ('ascii' => 'ldots', 'ml' => '<mo>&hellip;</mo>'),
214        array ('ascii' => '...', 'ml' => '<mo>&hellip;</mo>'),
215        array ('ascii' => 'cdots', 'ml' => '<mo>&#x22EF;</mo>'),
216        array ('ascii' => 'vdots', 'ml' => '<mo>&vellip;</mo>'),
217        array ('ascii' => 'ddots', 'ml' => '<mo>&#x22F1;</mo>'),
218        array ('ascii' => 'CC', 'ml' => '<mi>&#x2102;</mi>'),
219        array ('ascii' => 'NN', 'ml' => '<mi>&#x2115;</mi>'),
220        array ('ascii' => 'QQ', 'ml' => '<mi>&#x211A;</mi>'),
221        array ('ascii' => 'RR', 'ml' => '<mi>&#x211D;</mi>'),
222        array ('ascii' => 'ZZ', 'ml' => '<mi>&#x2124;</mi>'),
223        array ('ascii' => '(1/4)', 'ml' => '<mn>&frac14;</mn>'),
224        array ('ascii' => '¼', 'ml' => '<mn>&frac14;</mn>'),
225        array ('ascii' => '(1/2)', 'ml' => '<mn>&frac12;</mn>'),
226        array ('ascii' => '½', 'ml' => '<mn>&frac12;</mn>'),
227        array ('ascii' => '(3/4)', 'ml' => '<mn>&frac34;</mn>'),
228        array ('ascii' => '¾', 'ml' => '<mn>&frac34;</mn>'),
229        array ('ascii' => 'dx', 'ml' => '<mrow><mspace width="0.3em"/><mi>d</mi><mi>x</mi></mrow>'),  # denne form fremtvinger italics, og nbsp pynter
230        array ('ascii' => 'dy', 'ml' => '<mrow><mspace width="0.3em"/><mi>d</mi><mi>y</mi></mrow>'),
231        array ('ascii' => 'dz', 'ml' => '<mrow><mspace width="0.3em"/><mi>d</mi><mi>z</mi></mrow>'),
232        array ('ascii' => 'dt', 'ml' => '<mrow><mspace width="0.3em"/><mi>d</mi><mi>t</mi></mrow>'),
233
234        // Greek letters
235        array ('ascii' => 'Alpha', 'ml' => '<mi>&Alpha;</mi>'),
236        array ('ascii' => 'Beta', 'ml' => '<mi>&Beta;</mi>'),
237        array ('ascii' => 'Gamma', 'ml' => '<mi>&Gamma;</mi>'),
238        array ('ascii' => 'Delta', 'ml' => '<mo rspace="1px">&#x2206;</mo>'),   # NB!
239        array ('ascii' => 'Epsilon', 'ml' => '<mi>&Epsilon;</mi>'),
240        array ('ascii' => 'Zeta', 'ml' => '<mi>&Zeta;</mi>'),
241        array ('ascii' => 'Eta', 'ml' => '<mi>&Eta;</mi>'),
242        array ('ascii' => 'Theta', 'ml' => '<mi>&Theta;</mi>'),
243        array ('ascii' => 'Iota', 'ml' => '<mi>&Iota;</mi>'),
244        array ('ascii' => 'Kappa', 'ml' => '<mi>&Kappa;</mi>'),
245        array ('ascii' => 'Lambda', 'ml' => '<mi>&Lambda;</mi>'),
246        array ('ascii' => 'Mu', 'ml' => '<mi>&Mu;</mi>'),
247        array ('ascii' => 'Nu', 'ml' => '<mi>&Nu;</mi>'),
248        array ('ascii' => 'Xi', 'ml' => '<mi>&Xi;</mi>'),
249        array ('ascii' => 'Omicron', 'ml' => '<mi>&Omicron;</mi>'),
250        array ('ascii' => 'Pi', 'ml' => '<mi>&Pi;</mi>'),
251        array ('ascii' => 'Rho', 'ml' => '<mi>&Rho;</mi>'),
252        array ('ascii' => 'Sigma', 'ml' => '<mi>&Sigma;</mi>'),
253        array ('ascii' => 'Tau', 'ml' => '<mi>&Tau;</mi>'),
254        array ('ascii' => 'Upsilon', 'ml' => '<mi>&Upsilon;</mi>'),
255        array ('ascii' => 'Phi', 'ml' => '<mi>&Phi;</mi>'),
256        array ('ascii' => 'Chi', 'ml' => '<mi>&Chi;</mi>'),
257        array ('ascii' => 'Psi', 'ml' => '<mi>&Psi;</mi>'),
258        array ('ascii' => 'Omega', 'ml' => '<mi>&Omega;</mi>'),
259        array ('ascii' => 'alpha', 'ml' => '<mi>&alpha;</mi>'),
260        array ('ascii' => 'beta', 'ml' => '<mi>&beta;</mi>'),
261        array ('ascii' => 'gamma', 'ml' => '<mi>&gamma;</mi>'),
262        array ('ascii' => 'delta', 'ml' => '<mi>&delta;</mi>'),
263        array ('ascii' => 'epsilon', 'ml' => '<mi>&epsilon;</mi>'),
264        array ('ascii' => 'zeta', 'ml' => '<mi>&zeta;</mi>'),
265        array ('ascii' => 'eta', 'ml' => '<mi>&eta;</mi>'),
266        array ('ascii' => 'theta', 'ml' => '<mi>&theta;</mi>'),
267        array ('ascii' => 'iota', 'ml' => '<mi>&iota;</mi>'),
268        array ('ascii' => 'kappa', 'ml' => '<mi>&kappa;</mi>'),
269        array ('ascii' => 'lambda', 'ml' => '<mi>&lambda;</mi>'),
270        array ('ascii' => 'mu', 'ml' => '<mi>&mu;</mi>'),
271        array ('ascii' => 'nu', 'ml' => '<mi>&nu;</mi>'),
272        array ('ascii' => 'xi', 'ml' => '<mi>&xi;</mi>'),
273        array ('ascii' => 'omicron', 'ml' => '<mi>&omicron;</mi>'),
274        array ('ascii' => 'pi', 'ml' => '<mi>&pi;</mi>'),
275        array ('ascii' => 'rho', 'ml' => '<mi>&rho;</mi>'),
276        array ('ascii' => 'sigma', 'ml' => '<mi>&sigma;</mi>'),
277        array ('ascii' => 'sigmaf', 'ml' => '<mi>&sigmaf;</mi>'),
278        array ('ascii' => 'tau', 'ml' => '<mi>&tau;</mi>'),
279        array ('ascii' => 'upsilon', 'ml' => '<mi>&upsilon;</mi>'),
280        array ('ascii' => 'phi', 'ml' => '<mi>&phi;</mi>'),
281        array ('ascii' => 'chi', 'ml' => '<mi>&chi;</mi>'),
282        array ('ascii' => 'psi', 'ml' => '<mi>&psi;</mi>'),
283        array ('ascii' => 'omega', 'ml' => '<mi>&omega;</mi>'),
284        array ('ascii' => 'thetasym', 'ml' => '<mi>&thetasym;</mi>')
285
286        );
287
288        # Sortér så de største ascii-values står forrest, og dermed parses først
289        usort ($this->syms, function($a, $b) { return (strlen($b['ascii']) - strlen($a['ascii'])); });
290        # Udfyld tomme felter
291        for($t = 0; $t < count($this->syms); $t++) { $this->syms[$t]['ml_nobr'] = $this->syms[$t]['ml']; }
292    }
293
294    function parse($prevsym, $situation) {
295        # $situation er ass. array med følgende mulige boolean keys:
296        #  'master' er true for den første parse() der kaldes af handle()
297        #  'in_row' er true når parse() kaldes af parantes-subrutinen, dvs når parse() ikke kaldes for at indhente at argument til en operator
298        #  'first_in_row' er true ved første kald i ovennævnte situation, dvs første symbol i parantesen
299        #  'found_pipe' er true når parse() kaldes af parantes-subrutine der leder efter højre-pendant til left-pipe
300
301        # Uddrag et token $sym af $this->parsestr
302        if (! $situation['master']) {
303            $this->parsestr = ltrim($this->parsestr);  # Fjern indledende whitespaces
304            #error_log ('PARSE: ' . $this->parsestr);
305            $sym = null;
306            if ($this->parsestr != '') {
307                # Test mod symboler i $this->syms, de længste først
308                foreach ($this->syms as $s) {
309                    if (strpos($this->parsestr, $s['ascii']) === 0) {
310                        $this->parsestr = substr($this->parsestr, strlen($s['ascii']));
311                        $sym = $s;
312                        break;
313                    }
314                }
315                # Tal (0-9 og punktum)
316                if ((! isset($sym)) and (preg_match ('/^[0-9.]+/', $this->parsestr, $m))) {
317                    $ml = $ml_nobr = '<mn>'.$m[0].'</mn>';
318                    $sym = compact('ml', 'ml_nobr');
319                    $this->parsestr = substr($this->parsestr, strlen($m[0]));
320                }
321                # Tekst i gåseøjne
322                if ((! isset($sym)) and (preg_match ('/^"(.*?)"/', $this->parsestr, $m))) {
323                    $ml = $ml_nobr = '<mtext>'.$m[1].'</mtext>';
324                    $sym = compact('ml', 'ml_nobr');
325                    $this->parsestr = substr($this->parsestr, strlen($m[0]));
326                }
327                # Ingen symboler matcher, så udtag første tegn
328                if (! isset($sym)) {
329                    $ml = $ml_nobr = '<mi>'.substr($this->parsestr, 0, 1).'</mi>';
330                    $sym = compact('ml', 'ml_nobr');
331                    $this->parsestr = substr($this->parsestr, 1);
332                }
333            } else {
334                $ml = $ml_nobr = '<mspace />';
335                $sym = compact('ml', 'ml_nobr');
336            }
337            #error_log ('SYM: ' . $sym['ascii'] . ' -> ' . $sym['ml']);
338        }
339
340        # Paranteser
341        #   Kalder parse() gentagne gange rekursivt for at lægge symboler ved siden af hinanden indtil right-bracket findes eller parsestr løber tør.
342        #     Når parse-resultatet har 'from' sat, er der fundet en infix-operator, hvorfor sidstkomne element (arg1) er forældet og skal erstattes (arg1-infix-arg2).
343        #     Derfor må jongleres med to variable ($old og $new) - for at sikre, at der først føjes til stakken når der ikke kommer korrektion tilbage.
344        #   Om matrix-logik:
345        #     Et udtryk som `((a,b),(c,d))` skal danne en matrix med a og b øverst, c og d nederst.
346        #     Når parse() tager den første indre parantes fyldes $row med to elementer: 'a' og 'b'. Denne array returneres sammen med mathml for parantesen "(a,b)" til den kaldende parse().
347        #     Når parse() tager den yderste parantes fyldes $row med '(a,b)' og '(c,d)', men dette er uvigtigt og bruges ikke.
348        #       Derimod genkendes en matrix fordi hvert komma-adskilt element har sin egen ['row'] med samme antal elementer (2). Mathml-koden for disse samles i $matrix.
349
350        if (($situation['master']) or ($sym['type'] == 'leftbracket') or (($sym['ascii'] == '|') and (! $situation['found_pipe']))) {
351            $new = null; $old = $sym; $leftbracket = ($situation['master'] ? null : $sym); $rightbracket = null; $row = array(); $matrix = ''; $field = ''; $fields_in_row = 0;
352            while (true) {
353                $new = $this->parse($new, array('in_row' => true, 'first_in_row' => ($new === null), 'found_pipe' => ($leftbracket['ascii'] == '|')));
354                if (! isset($new['from'])) {
355                    # Føj til stakken, ellers vent til næste iteration (hvor $new bliver til $old)
356                    $ml .= $old['ml'];
357                    if (($old['type'] != 'leftbracket') and ($old['ascii'] != '|')) {
358                        # Det fundne er hverken start- eller slutparantes (the meat of the bracket)
359                        $ml_nobr .= $old['ml'];
360                        if ($old['ascii'] == ',') {
361                            if ($field == '') $matrix = false;
362                            $row[] = $field;
363                            $field = '';
364                        } else {
365                            $field .= $old['ml'];
366                            if (($matrix !== false) and (isset($old['row'])) and (($fields_in_row == 0) or ($fields_in_row == count($old['row'])))) {
367                                # Gyldig matrix kræver at hver row har samme antal fields
368                                $fields_in_row = count($old['row']); # at den er 0 går kun een gang
369                                $matrix .= '<mtr><mtd>' . implode('</mtd><mtd>', $old['row']) . '</mtd></mtr>';
370                                #error_log ('ADDS TO MATRIX: ' . $fields_in_row . ' <mtr><mtd>' . implode('</mtd><mtd>', $old['row']) . '</mtd></mtr>');
371                            } else {
372                                $matrix = false;
373                            }
374                        }
375                    }
376                }
377                $old = $new;
378                if ((($old['type'] == 'rightbracket') or ($old['ascii'] == '|')) and (! $situation['master'])) { $rightbracket = $old; break; } # right bracket found
379                if ($old['ml'] == '<mspace />') { $matrix = false; break; } # parsestr løber tør uden right bracket
380            }
381            # Afsporet af pipe, der ikke står som parantes begynd? I så fald push rightbracket tilbage på parsestr og abortér. Den skal parres på niveauet bagude.
382            if (($leftbracket['ascii'] == '|') and ($rightbracket['ascii'] != '|')) {
383                $this->parsestr = $rightbracket['ascii'] . $this->parsestr;
384                $ml = $ml_nobr = $leftbracket['ml_op'] . $ml_nobr;
385                return compact('ml', 'ml_nobr');
386            }
387            # Tilføj sidste field (før slutparantesen) til $row
388            if ($field == '') $matrix = false; $row[] = $field; $field = '';
389            # Ved gyldig matrix erstattes alt med denne.
390            #  Kræver: Mindst 2 columns, ellers vil `f^((x))(t)` også tælle som matrix.
391            if (($matrix !== false) and (count($row) > 1)) {
392                $ml_nobr = '<mtable>' . $matrix . '</mtable>';
393            }
394            # Returner string med og uden paranteser
395            $ml = '<mrow>' . $leftbracket['ml'] . $ml_nobr . $rightbracket['ml'] . '</mrow>';
396            if (($leftbracket['ascii'] == '(') and ($rightbracket['ascii'] == ')')) {
397                $ml_nobr = '<mrow>' . $ml_nobr . '</mrow>';
398            } else {
399                $ml_nobr = $ml;
400            }
401            #error_log ('PARANTES: (row: ' . count($row) . ') ' . $ml);
402            return compact ('ml', 'ml_nobr', 'row');
403        }
404
405        # Unære operatorer
406        if ($sym['type'] == 'unary') {
407            $new = $this->parse($sym, null);
408            $ml = $ml_nobr = $sym['wrap_left'] . $new['ml_nobr'] . $sym['wrap_right'];
409            return compact ('ml', 'ml_nobr');
410        }
411
412        # Binære operatorer (uden infix, dvs kun root og stackrel)
413        if ($sym['type'] == 'binary') {
414            $new1 = $this->parse($sym, null);
415            $new2 = $this->parse($new1, null);
416            $ml = $ml_nobr = $sym['wrap_left'] . $new2['ml_nobr'] . $new1['ml_nobr'] . $sym['wrap_right'];
417            return compact ('ml', 'ml_nobr');
418        }
419
420        # Standardfunktioner (som sin og cos, binder stærkt)
421        if ($sym['type'] == 'func') {
422            $old = $sym;
423            $new = $this->parse($sym, null);
424            while (isset ($new['from'])) { # fang sub- og superscripts til funktioner før selve argumentet
425                $old = $new;
426                $new = $this->parse($new, null);
427            }
428            $ml = $ml_nobr = '<mrow>' . $old['ml'] . $new['ml'] . '</mrow>';
429            #error_log ('FUNKTION: ' . $ml);
430            return compact ('ml', 'ml_nobr');
431        }
432
433        # Infix-operatorer
434        if ($sym['type'] == 'infix') {
435            if ($situation['first_in_row']) {
436                # Ydertilfælde som `^x` og `_y`. Skal bare reddes.
437                $ml = $ml_nobr = $sym['ml'];
438                return compact ('ml', 'ml_nobr');
439            }
440            # Den simple stak er $prevsym, $sym og $new, svarende til de tre tegn i `y_z`.
441            # Den udvidede stak er:
442            $pos1 = $prevsym['from'][0];               #  `x    `
443            $pos2 = $prevsym['from'][1];               #  ` ^   `
444            $pos3 = $prevsym['from'][2];               #  `  y  `
445            $pos4 = $sym;                              #  `   _ `
446            $pos5 = $new = $this->parse($sym, null);   #  `    z`
447            $op = $sym['ascii'];
448            $bundtype = isset($prevsym['bundtype']) ? $prevsym['bundtype'] : $prevsym['type'];           # fx 'magnum' (går i arv)
449            if ((($op == '_') or ($op == '^')) and (($pos2['ascii'] == '/') or ($pos2['ascii'] == '//'))) {
450                # Tilfældet `x/y_z` eller `x/y^z`
451                $ml = $pos2['wrap_left'] . $pos1['ml_nobr'] . $pos4['wrap_left'] . $pos3['ml'] . $pos5['ml_nobr'] . $pos4['wrap_right'] . $pos2['wrap_right'];
452            } elseif (($op == '^') and ($pos2['ascii'] == '_') and ($pos1['underover'])) {
453                # Tilfældet `prod_y^z`
454                $ml = '<munderover>' . $pos1['ml'] . $pos3['ml_nobr'] . $pos5['ml_nobr'] . '</munderover>';
455            } elseif (($op == '_') and ($pos2['ascii'] == '^') and ($pos1['underover'])) {
456                # Tilfældet `prod^y_z`
457                $ml = '<munderover>' . $pos1['ml'] . $pos5['ml_nobr'] . $pos3['ml_nobr'] . '</munderover>';
458            } elseif (($op == '_') and ($prevsym['underover'])) {
459                # Tilfældet `prod_z`
460                $ml = '<munder>' . $prevsym['ml'] . $new['ml_nobr'] . '</munder>';
461            } elseif (($op == '^') and ($prevsym['underover'])) {
462                # Tilfældet `prod^z`
463                $ml = '<mover>' . $prevsym['ml'] . $new['ml_nobr'] . '</mover>';
464            } elseif (($op == '^') and ($pos2['ascii'] == '_')) {
465                # Tilfældet `x_y^z`  (eneste som asciimathml.js genkender)
466                $ml = '<msubsup>' . $pos1['ml'] . $pos3['ml_nobr'] . $pos5['ml_nobr'] . '</msubsup>';
467            } elseif (($op == '_') and ($pos2['ascii'] == '^')) {
468                # Tilfældet `x^y_z`  (kommer ud på det samme)
469                $ml = '<msubsup>' . $pos1['ml'] . $pos5['ml_nobr'] . $pos3['ml_nobr'] . '</msubsup>';
470            } elseif (($op == '_') or ($op == '^')) {
471                # Tilfældet `y_z` eller `y^z`
472                $ml = $sym['wrap_left'] . $prevsym['ml'] . $new['ml_nobr'] . $sym['wrap_right'];
473            } elseif (($op == '/') or ($op == '//')) {
474                $ml = $sym['wrap_left'] . $prevsym['ml_nobr'] . $new['ml_nobr'] . $sym['wrap_right'];
475            } else msg ('Dunno how I got here!');
476            $from = array($prevsym, $sym, $new);
477            $ml_nobr = $ml;
478            return compact ('ml', 'ml_nobr', 'from', 'bundtype');
479        }
480
481        # Signs (minus og plusminus)
482        if ($sym['type'] == 'sign') {
483            if (($situation['first_in_row']) or (! $situation['in_row']) or ($prevsym['type'] == 'op') or ($prevsym['bundtype'] == 'magnum')) {
484                # Brug fortegnsversionen af symbolet i tilfælde hvor 1) symbolet optræder allerførst i parantes/globalt, 2) symbolet optræder som argument til operator (!in_row),
485                # 3) symbolet optræder efter simpel inline-operator, eller 4) efter infix-symbolkæde med bundtype magnum (dvs i praksis magnum-operator med index)
486                $new = $this->parse($sym, null);
487                $ml = $ml_nobr = '<mrow>' . $sym['ml_sign'] . $new['ml'] . '</mrow>';
488                if ($new['ml'] != '<mspace />') return compact ('ml', 'ml_nobr');
489            }
490            return $sym;
491        }
492
493        # Løsslupne right brackets - puttes tilbage til næste parserunde, mens denne returnerer tom streng  (fx `(4/)_x`)
494        if (($sym['type'] == 'rightbracket') and (! $situation['in_row'])) {
495            #error_log ('LOES!');
496            $this->parsestr = $sym['ascii'] . $this->parsestr;
497            $ml = $ml_nobr = '<mspace />';
498            return compact ('ml', 'ml_nobr');
499        }
500
501        # Andre symboler
502        return ($sym);
503    }
504
505    function handle($match, $state, $pos, &$handler){
506        if (count($this->syms) == 0) $this->setup_syms();
507        $this->parsestr = substr ($match, 1, -1);
508        if (substr ($match, 1, 1) == ' ') { $displaystyle = 'true'; } else { $displaystyle = 'false'; }
509        #error_log ('------ HANDLE -------');
510        $parsed = $this->parse(null, array('master' => true));
511        $return = '<math xmlns="http://www.w3.org/1998/Math/MathML" title="'.htmlentities ($match).'" displaystyle="'.$displaystyle.'">'.$parsed['ml'].'</math>';
512        $return = '<span pos="' . $pos . '" len="' . strlen($match) . '">' . $return . '</span>';   // wrappes i span aht mdblclick
513	    return $return;
514
515	}
516
517    function render($mode, &$renderer, $data) {
518        if ($mode!='xhtml') return false;
519        $renderer->doc .= $data;
520        return true;
521    }
522}
523?>
524