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>+</mo>', 'type' => 'sign', 'ml_sign' => '<mo>+</mo>'), 43 array ('ascii' => '+ ', 'ml' => '<mo>+</mo>'), 44 array ('ascii' => '-', 'ml' => '<mo>−</mo>', 'type' => 'sign', 'ml_sign' => '<mo lspace="1px" rspace="1px">‐</mo>'), # ‐ is HYPHEN 45 array ('ascii' => '- ', 'ml' => '<mo>−</mo>'), 46 array ('ascii' => '+-', 'ml' => '<mo>±</mo>', 'type' => 'sign', 'ml_sign' => '<mo>±</mo>'), 47 array ('ascii' => '+- ', 'ml' => '<mo>±</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> </mo>', 'type' => 'op'), 56 array ('ascii' => '*', 'ml' => '<mo>⋅</mo>', 'type' => 'op'), 57 array ('ascii' => '**', 'ml' => '<mo>⋆</mo>', 'type' => 'op'), 58 array ('ascii' => 'xx', 'ml' => '<mo>×</mo>', 'type' => 'op'), 59 array ('ascii' => 'times', 'ml' => '<mo>×</mo>', 'type' => 'op'), 60 array ('ascii' => '-:', 'ml' => '<mo>÷</mo>', 'type' => 'op'), 61 array ('ascii' => '@', 'ml' => '<mo>∘</mo>', 'type' => 'op'), 62 array ('ascii' => 'o+', 'ml' => '<mo>⊕</mo>', 'type' => 'op'), 63 array ('ascii' => 'ox', 'ml' => '<mo>⊗</mo>', 'type' => 'op'), 64 array ('ascii' => 'o.', 'ml' => '<mo>⊙</mo>', 'type' => 'op'), 65 array ('ascii' => '^^', 'ml' => '<mo>∧</mo>', 'type' => 'op'), 66 array ('ascii' => 'vv', 'ml' => '<mo>∨</mo>', 'type' => 'op'), 67 array ('ascii' => 'nn', 'ml' => '<mo>∩</mo>', 'type' => 'op'), 68 array ('ascii' => 'uu', 'ml' => '<mo>∪</mo>', 'type' => 'op'), 69 array ('ascii' => '!=', 'ml' => '<mo>≠</mo>', 'type' => 'op'), 70 array ('ascii' => 'ne', 'ml' => '<mo>≠</mo>', 'type' => 'op'), 71 array ('ascii' => '<', 'ml' => '<mo><</mo>', 'type' => 'op'), 72 array ('ascii' => 'lt', 'ml' => '<mo><</mo>', 'type' => 'op'), 73 array ('ascii' => '>', 'ml' => '<mo>></mo>', 'type' => 'op'), 74 array ('ascii' => 'gt', 'ml' => '<mo>></mo>', 'type' => 'op'), 75 array ('ascii' => '<=', 'ml' => '<mo>≤</mo>', 'type' => 'op'), 76 array ('ascii' => 'le', 'ml' => '<mo>≤</mo>', 'type' => 'op'), 77 array ('ascii' => '>=', 'ml' => '<mo>≥</mo>', 'type' => 'op'), 78 array ('ascii' => 'ge', 'ml' => '<mo>≥</mo>', 'type' => 'op'), 79 array ('ascii' => '<<', 'ml' => '<mo>≪</mo>', 'type' => 'op'), 80 array ('ascii' => '>>', 'ml' => '<mo>≫</mo>', 'type' => 'op'), 81 array ('ascii' => '>-', 'ml' => '<mo>≻</mo>', 'type' => 'op'), 82 array ('ascii' => '-<', 'ml' => '<mo>≺</mo>', 'type' => 'op'), 83 array ('ascii' => 'in', 'ml' => '<mo>∈</mo>', 'type' => 'op'), 84 array ('ascii' => '!in', 'ml' => '<mo>∉</mo>', 'type' => 'op'), 85 array ('ascii' => 'sub', 'ml' => '<mo>⊂</mo>', 'type' => 'op'), 86 array ('ascii' => 'sup', 'ml' => '<mo>⊃</mo>', 'type' => 'op'), 87 array ('ascii' => '!sub', 'ml' => '<mo>⊄</mo>', 'type' => 'op'), 88 array ('ascii' => 'nsub', 'ml' => '<mo>⊄</mo>', 'type' => 'op'), 89 array ('ascii' => 'sube', 'ml' => '<mo>⊆</mo>', 'type' => 'op'), 90 array ('ascii' => 'supe', 'ml' => '<mo>⊇</mo>', 'type' => 'op'), 91 array ('ascii' => '-=', 'ml' => '<mo>≡</mo>', 'type' => 'op'), 92 array ('ascii' => '~=', 'ml' => '<mo>≅</mo>', 'type' => 'op'), 93 array ('ascii' => '~~', 'ml' => '<mo>≈</mo>', 'type' => 'op'), 94 array ('ascii' => 'approx', 'ml' => '<mo>≈</mo>', 'type' => 'op'), 95 array ('ascii' => 'prop', 'ml' => '<mo>∝</mo>', 'type' => 'op'), 96 array ('ascii' => 'not', 'ml' => '<mo>¬</mo>', 'type' => 'op'), 97 array ('ascii' => 'AA', 'ml' => '<mo>∀</mo>', 'type' => 'op'), 98 array ('ascii' => 'EE', 'ml' => '<mo>∃</mo>', 'type' => 'op'), 99 array ('ascii' => '|--', 'ml' => '<mo>⊢</mo>', 'type' => 'op'), 100 array ('ascii' => '|==', 'ml' => '<mo>⊨</mo>', 'type' => 'op'), 101 array ('ascii' => '/_', 'ml' => '<mo>∠</mo>', 'type' => 'op'), 102 array ('ascii' => 'ang', 'ml' => '<mo>∠</mo>', 'type' => 'op'), 103 array ('ascii' => ':.', 'ml' => '<mo>∴</mo>', 'type' => 'op'), 104 array ('ascii' => '<-', 'ml' => '<mo>←</mo>', 'type' => 'op'), 105 array ('ascii' => 'larr', 'ml' => '<mo>←</mo>', 'type' => 'op'), 106 array ('ascii' => 'uarr', 'ml' => '<mo>↑</mo>', 'type' => 'op'), 107 array ('ascii' => 'rarr', 'ml' => '<mo>→</mo>', 'type' => 'op'), 108 array ('ascii' => '->', 'ml' => '<mo>→</mo>', 'type' => 'op'), 109 array ('ascii' => 'darr', 'ml' => '<mo>↓</mo>', 'type' => 'op'), 110 array ('ascii' => '<->', 'ml' => '<mo>↔</mo>', 'type' => 'op'), 111 array ('ascii' => 'harr', 'ml' => '<mo>↔</mo>', 'type' => 'op'), 112 array ('ascii' => '|->', 'ml' => '<mo>↦</mo>', 'type' => 'op'), 113 array ('ascii' => 'rArr', 'ml' => '<mo>⇒</mo>', 'type' => 'op'), 114 array ('ascii' => '=>', 'ml' => '<mo>⇒</mo>', 'type' => 'op'), 115 array ('ascii' => 'lArr', 'ml' => '<mo>⇐</mo>', 'type' => 'op'), 116 array ('ascii' => 'hArr', 'ml' => '<mo>⇔</mo>', 'type' => 'op'), 117 array ('ascii' => 'iff', 'ml' => '<mo>⇔</mo>', 'type' => 'op'), 118 array ('ascii' => '<=>', 'ml' => '<mo>⇔</mo>', 'type' => 'op'), 119 array ('ascii' => '|__', 'ml' => '<mo>⌊</mo>', 'type' => 'op'), 120 array ('ascii' => '__|', 'ml' => '<mo>⌋</mo>', 'type' => 'op'), 121 array ('ascii' => '|~', 'ml' => '<mo>⌈</mo>', 'type' => 'op'), 122 array ('ascii' => '~|', 'ml' => '<mo>⌉</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>⟨</mo>', 'type' => 'leftbracket'), 135 array ('ascii' => ':)', 'ml' => '<mo>⟩</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>⁄</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>¯</mo></mover>'), 148 array ('ascii' => 'ul', 'type' => 'unary', 'wrap_left' => '<munder>', 'wrap_right' => '<mo>_</mo></munder>'), 149 array ('ascii' => 'vec', 'type' => 'unary', 'wrap_left' => '<mover accent=false>', 'wrap_right' => '<mo>→</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>∑</mo>', 'type' => 'magnum', 'underover' => true), 165 array ('ascii' => 'prod', 'ml' => '<mo>∏</mo>', 'type' => 'magnum', 'underover' => true), 166 array ('ascii' => '^^^', 'ml' => '<mo>⋀</mo>', 'type' => 'magnum', 'underover' => true), 167 array ('ascii' => 'vvv', 'ml' => '<mo>⋁</mo>', 'type' => 'magnum', 'underover' => true), 168 array ('ascii' => 'nnn', 'ml' => '<mo>⋂</mo>', 'type' => 'magnum', 'underover' => true), 169 array ('ascii' => 'uuu', 'ml' => '<mo>⋃</mo>', 'type' => 'magnum', 'underover' => true), 170 array ('ascii' => 'int', 'ml' => '<mo>∫</mo>', 'type' => 'magnum'), 171 array ('ascii' => 'oint', 'ml' => '<mo>∮</mo>', 'type' => 'magnum'), 172 array ('ascii' => 'ointoint', 'ml' => '<mo>∯</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>∅</mo>'), 204 array ('ascii' => 'del', 'ml' => '<mo>∂</mo>'), 205 array ('ascii' => 'grad', 'ml' => '<mo>∇</mo>'), 206 array ('ascii' => 'oo', 'ml' => '<mo>∞</mo>'), 207 array ('ascii' => 'aleph', 'ml' => '<mo>ℵ</mo>'), 208 array ('ascii' => '_|_', 'ml' => '<mo>⊥</mo>'), 209 array ('ascii' => 'TT', 'ml' => '<mo>⊤</mo>'), 210 array ('ascii' => 'deg', 'ml' => '<mo>°</mo>'), 211 array ('ascii' => 'diamond', 'ml' => '<mo>⋄</mo>'), 212 array ('ascii' => 'square', 'ml' => '<mo>□</mo>'), 213 array ('ascii' => 'ldots', 'ml' => '<mo>…</mo>'), 214 array ('ascii' => '...', 'ml' => '<mo>…</mo>'), 215 array ('ascii' => 'cdots', 'ml' => '<mo>⋯</mo>'), 216 array ('ascii' => 'vdots', 'ml' => '<mo>⋮</mo>'), 217 array ('ascii' => 'ddots', 'ml' => '<mo>⋱</mo>'), 218 array ('ascii' => 'CC', 'ml' => '<mi>ℂ</mi>'), 219 array ('ascii' => 'NN', 'ml' => '<mi>ℕ</mi>'), 220 array ('ascii' => 'QQ', 'ml' => '<mi>ℚ</mi>'), 221 array ('ascii' => 'RR', 'ml' => '<mi>ℝ</mi>'), 222 array ('ascii' => 'ZZ', 'ml' => '<mi>ℤ</mi>'), 223 array ('ascii' => '(1/4)', 'ml' => '<mn>¼</mn>'), 224 array ('ascii' => '¼', 'ml' => '<mn>¼</mn>'), 225 array ('ascii' => '(1/2)', 'ml' => '<mn>½</mn>'), 226 array ('ascii' => '½', 'ml' => '<mn>½</mn>'), 227 array ('ascii' => '(3/4)', 'ml' => '<mn>¾</mn>'), 228 array ('ascii' => '¾', 'ml' => '<mn>¾</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>Α</mi>'), 236 array ('ascii' => 'Beta', 'ml' => '<mi>Β</mi>'), 237 array ('ascii' => 'Gamma', 'ml' => '<mi>Γ</mi>'), 238 array ('ascii' => 'Delta', 'ml' => '<mo rspace="1px">∆</mo>'), # NB! 239 array ('ascii' => 'Epsilon', 'ml' => '<mi>Ε</mi>'), 240 array ('ascii' => 'Zeta', 'ml' => '<mi>Ζ</mi>'), 241 array ('ascii' => 'Eta', 'ml' => '<mi>Η</mi>'), 242 array ('ascii' => 'Theta', 'ml' => '<mi>Θ</mi>'), 243 array ('ascii' => 'Iota', 'ml' => '<mi>Ι</mi>'), 244 array ('ascii' => 'Kappa', 'ml' => '<mi>Κ</mi>'), 245 array ('ascii' => 'Lambda', 'ml' => '<mi>Λ</mi>'), 246 array ('ascii' => 'Mu', 'ml' => '<mi>Μ</mi>'), 247 array ('ascii' => 'Nu', 'ml' => '<mi>Ν</mi>'), 248 array ('ascii' => 'Xi', 'ml' => '<mi>Ξ</mi>'), 249 array ('ascii' => 'Omicron', 'ml' => '<mi>Ο</mi>'), 250 array ('ascii' => 'Pi', 'ml' => '<mi>Π</mi>'), 251 array ('ascii' => 'Rho', 'ml' => '<mi>Ρ</mi>'), 252 array ('ascii' => 'Sigma', 'ml' => '<mi>Σ</mi>'), 253 array ('ascii' => 'Tau', 'ml' => '<mi>Τ</mi>'), 254 array ('ascii' => 'Upsilon', 'ml' => '<mi>Υ</mi>'), 255 array ('ascii' => 'Phi', 'ml' => '<mi>Φ</mi>'), 256 array ('ascii' => 'Chi', 'ml' => '<mi>Χ</mi>'), 257 array ('ascii' => 'Psi', 'ml' => '<mi>Ψ</mi>'), 258 array ('ascii' => 'Omega', 'ml' => '<mi>Ω</mi>'), 259 array ('ascii' => 'alpha', 'ml' => '<mi>α</mi>'), 260 array ('ascii' => 'beta', 'ml' => '<mi>β</mi>'), 261 array ('ascii' => 'gamma', 'ml' => '<mi>γ</mi>'), 262 array ('ascii' => 'delta', 'ml' => '<mi>δ</mi>'), 263 array ('ascii' => 'epsilon', 'ml' => '<mi>ε</mi>'), 264 array ('ascii' => 'zeta', 'ml' => '<mi>ζ</mi>'), 265 array ('ascii' => 'eta', 'ml' => '<mi>η</mi>'), 266 array ('ascii' => 'theta', 'ml' => '<mi>θ</mi>'), 267 array ('ascii' => 'iota', 'ml' => '<mi>ι</mi>'), 268 array ('ascii' => 'kappa', 'ml' => '<mi>κ</mi>'), 269 array ('ascii' => 'lambda', 'ml' => '<mi>λ</mi>'), 270 array ('ascii' => 'mu', 'ml' => '<mi>μ</mi>'), 271 array ('ascii' => 'nu', 'ml' => '<mi>ν</mi>'), 272 array ('ascii' => 'xi', 'ml' => '<mi>ξ</mi>'), 273 array ('ascii' => 'omicron', 'ml' => '<mi>ο</mi>'), 274 array ('ascii' => 'pi', 'ml' => '<mi>π</mi>'), 275 array ('ascii' => 'rho', 'ml' => '<mi>ρ</mi>'), 276 array ('ascii' => 'sigma', 'ml' => '<mi>σ</mi>'), 277 array ('ascii' => 'sigmaf', 'ml' => '<mi>ς</mi>'), 278 array ('ascii' => 'tau', 'ml' => '<mi>τ</mi>'), 279 array ('ascii' => 'upsilon', 'ml' => '<mi>υ</mi>'), 280 array ('ascii' => 'phi', 'ml' => '<mi>φ</mi>'), 281 array ('ascii' => 'chi', 'ml' => '<mi>χ</mi>'), 282 array ('ascii' => 'psi', 'ml' => '<mi>ψ</mi>'), 283 array ('ascii' => 'omega', 'ml' => '<mi>ω</mi>'), 284 array ('ascii' => 'thetasym', 'ml' => '<mi>ϑ</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