LCOV - code coverage report
Current view: top level - starmath/source - parse.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 951 1117 85.1 %
Date: 2015-06-13 12:38:46 Functions: 42 45 93.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       5             :  * This Source Code Form is subject to the terms of the Mozilla Public
       6             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       7             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      11             :  *   Licensed to the Apache Software Foundation (ASF) under one or more
      12             :  *   contributor license agreements. See the NOTICE file distributed
      13             :  *   with this work for additional information regarding copyright
      14             :  *   ownership. The ASF licenses this file to you under the Apache
      15             :  *   License, Version 2.0 (the "License"); you may not use this file
      16             :  *   except in compliance with the License. You may obtain a copy of
      17             :  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
      18             :  */
      19             : 
      20             : #include <com/sun/star/i18n/UnicodeType.hpp>
      21             : #include <i18nlangtag/lang.h>
      22             : #include <unotools/charclass.hxx>
      23             : #include <editeng/unolingu.hxx>
      24             : #include <unotools/syslocale.hxx>
      25             : #include <sal/macros.h>
      26             : #include <vcl/settings.hxx>
      27             : #include "parse.hxx"
      28             : #include "starmath.hrc"
      29             : #include "smdll.hxx"
      30             : #include "smmod.hxx"
      31             : #include "cfgitem.hxx"
      32             : 
      33             : using namespace ::com::sun::star;
      34             : using namespace ::com::sun::star::i18n;
      35             : 
      36             : 
      37        1376 : SmToken::SmToken() :
      38             :     eType       (TUNKNOWN),
      39        1376 :     cMathChar   ('\0')
      40             : {
      41        1376 :     nGroup = nCol = nRow = nLevel = 0;
      42        1376 : }
      43             : 
      44           4 : SmToken::SmToken(SmTokenType eTokenType,
      45             :                  sal_Unicode cMath,
      46             :                  const sal_Char* pText,
      47             :                  sal_uLong nTokenGroup,
      48           4 :                  sal_uInt16 nTokenLevel) {
      49           4 :     eType = eTokenType;
      50           4 :     cMathChar = cMath;
      51           4 :     aText = OUString::createFromAscii(pText);
      52           4 :     nGroup = nTokenGroup;
      53           4 :     nLevel = nTokenLevel;
      54           4 :     nCol = nRow = 0;
      55           4 : }
      56             : 
      57             : 
      58             : 
      59             : 
      60             : static const SmTokenTableEntry aTokenTable[] =
      61             : {
      62             :     { "Im" , TIM, MS_IM, TGSTANDALONE, 5 },
      63             :     { "Re" , TRE, MS_RE, TGSTANDALONE, 5 },
      64             :     { "abs", TABS, '\0', TGUNOPER, 13 },
      65             :     { "arcosh", TACOSH, '\0', TGFUNCTION, 5 },
      66             :     { "arcoth", TACOTH, '\0', TGFUNCTION, 5 },
      67             :     { "acute", TACUTE, MS_ACUTE, TGATTRIBUT, 5 },
      68             :     { "aleph" , TALEPH, MS_ALEPH, TGSTANDALONE, 5 },
      69             :     { "alignb", TALIGNC, '\0', TGALIGN | TGDISCARDED, 0},
      70             :     { "alignc", TALIGNC, '\0', TGALIGN, 0},
      71             :     { "alignl", TALIGNL, '\0', TGALIGN, 0},
      72             :     { "alignm", TALIGNC, '\0', TGALIGN | TGDISCARDED, 0},
      73             :     { "alignr", TALIGNR, '\0', TGALIGN, 0},
      74             :     { "alignt", TALIGNC, '\0', TGALIGN | TGDISCARDED, 0},
      75             :     { "and", TAND, MS_AND, TGPRODUCT, 0},
      76             :     { "approx", TAPPROX, MS_APPROX, TGRELATION, 0},
      77             :     { "aqua", TAQUA, '\0', TGCOLOR, 0},
      78             :     { "arccos", TACOS, '\0', TGFUNCTION, 5},
      79             :     { "arccot", TACOT, '\0', TGFUNCTION, 5},
      80             :     { "arcsin", TASIN, '\0', TGFUNCTION, 5},
      81             :     { "arctan", TATAN, '\0', TGFUNCTION, 5},
      82             :     { "arsinh", TASINH, '\0', TGFUNCTION, 5},
      83             :     { "artanh", TATANH, '\0', TGFUNCTION, 5},
      84             :     { "backepsilon" , TBACKEPSILON, MS_BACKEPSILON, TGSTANDALONE, 5},
      85             :     { "bar", TBAR, MS_BAR, TGATTRIBUT, 5},
      86             :     { "binom", TBINOM, '\0', 0, 5 },
      87             :     { "black", TBLACK, '\0', TGCOLOR, 0},
      88             :     { "blue", TBLUE, '\0', TGCOLOR, 0},
      89             :     { "bold", TBOLD, '\0', TGFONTATTR, 5},
      90             :     { "boper", TBOPER, '\0', TGPRODUCT, 0},
      91             :     { "breve", TBREVE, MS_BREVE, TGATTRIBUT, 5},
      92             :     { "bslash", TBACKSLASH, MS_BACKSLASH, TGPRODUCT, 0 },
      93             :     { "cdot", TCDOT, MS_CDOT, TGPRODUCT, 0},
      94             :     { "check", TCHECK, MS_CHECK, TGATTRIBUT, 5},
      95             :     { "circ" , TCIRC, MS_CIRC, TGSTANDALONE, 5},
      96             :     { "circle", TCIRCLE, MS_CIRCLE, TGATTRIBUT, 5},
      97             :     { "color", TCOLOR, '\0', TGFONTATTR, 5},
      98             :     { "coprod", TCOPROD, MS_COPROD, TGOPER, 5},
      99             :     { "cos", TCOS, '\0', TGFUNCTION, 5},
     100             :     { "cosh", TCOSH, '\0', TGFUNCTION, 5},
     101             :     { "cot", TCOT, '\0', TGFUNCTION, 5},
     102             :     { "coth", TCOTH, '\0', TGFUNCTION, 5},
     103             :     { "csub", TCSUB, '\0', TGPOWER, 0},
     104             :     { "csup", TCSUP, '\0', TGPOWER, 0},
     105             :     { "cyan", TCYAN, '\0', TGCOLOR, 0},
     106             :     { "dddot", TDDDOT, MS_DDDOT, TGATTRIBUT, 5},
     107             :     { "ddot", TDDOT, MS_DDOT, TGATTRIBUT, 5},
     108             :     { "def", TDEF, MS_DEF, TGRELATION, 0},
     109             :     { "div", TDIV, MS_DIV, TGPRODUCT, 0},
     110             :     { "divides", TDIVIDES, MS_LINE, TGRELATION, 0},
     111             :     { "dlarrow" , TDLARROW, MS_DLARROW, TGSTANDALONE, 5},
     112             :     { "dlrarrow" , TDLRARROW, MS_DLRARROW, TGSTANDALONE, 5},
     113             :     { "dot", TDOT, MS_DOT, TGATTRIBUT, 5},
     114             :     { "dotsaxis", TDOTSAXIS, MS_DOTSAXIS, TGSTANDALONE, 5}, // 5 to continue expression
     115             :     { "dotsdiag", TDOTSDIAG, MS_DOTSUP, TGSTANDALONE, 5},
     116             :     { "dotsdown", TDOTSDOWN, MS_DOTSDOWN, TGSTANDALONE, 5},
     117             :     { "dotslow", TDOTSLOW, MS_DOTSLOW, TGSTANDALONE, 5},
     118             :     { "dotsup", TDOTSUP, MS_DOTSUP, TGSTANDALONE, 5},
     119             :     { "dotsvert", TDOTSVERT, MS_DOTSVERT, TGSTANDALONE, 5},
     120             :     { "downarrow" , TDOWNARROW, MS_DOWNARROW, TGSTANDALONE, 5},
     121             :     { "drarrow" , TDRARROW, MS_DRARROW, TGSTANDALONE, 5},
     122             :     { "emptyset" , TEMPTYSET, MS_EMPTYSET, TGSTANDALONE, 5},
     123             :     { "equiv", TEQUIV, MS_EQUIV, TGRELATION, 0},
     124             :     { "exists", TEXISTS, MS_EXISTS, TGSTANDALONE, 5},
     125             :     { "notexists", TNOTEXISTS, MS_NOTEXISTS, TGSTANDALONE, 5},
     126             :     { "exp", TEXP, '\0', TGFUNCTION, 5},
     127             :     { "fact", TFACT, MS_FACT, TGUNOPER, 5},
     128             :     { "fixed", TFIXED, '\0', TGFONT, 0},
     129             :     { "font", TFONT, '\0', TGFONTATTR, 5},
     130             :     { "forall", TFORALL, MS_FORALL, TGSTANDALONE, 5},
     131             :     { "from", TFROM, '\0', TGLIMIT, 0},
     132             :     { "fuchsia", TFUCHSIA, '\0', TGCOLOR, 0},
     133             :     { "func", TFUNC, '\0', TGFUNCTION, 5},
     134             :     { "ge", TGE, MS_GE, TGRELATION, 0},
     135             :     { "geslant", TGESLANT, MS_GESLANT, TGRELATION, 0 },
     136             :     { "gg", TGG, MS_GG, TGRELATION, 0},
     137             :     { "grave", TGRAVE, MS_GRAVE, TGATTRIBUT, 5},
     138             :     { "gray", TGRAY, '\0', TGCOLOR, 0},
     139             :     { "green", TGREEN, '\0', TGCOLOR, 0},
     140             :     { "gt", TGT, MS_GT, TGRELATION, 0},
     141             :     { "hat", THAT, MS_HAT, TGATTRIBUT, 5},
     142             :     { "hbar" , THBAR, MS_HBAR, TGSTANDALONE, 5},
     143             :     { "iiint", TIIINT, MS_IIINT, TGOPER, 5},
     144             :     { "iint", TIINT, MS_IINT, TGOPER, 5},
     145             :     { "in", TIN, MS_IN, TGRELATION, 0},
     146             :     { "infinity" , TINFINITY, MS_INFINITY, TGSTANDALONE, 5},
     147             :     { "infty" , TINFINITY, MS_INFINITY, TGSTANDALONE, 5},
     148             :     { "int", TINT, MS_INT, TGOPER, 5},
     149             :     { "intd", TINTD, MS_INT, TGUNOPER, 5},
     150             :     { "intersection", TINTERSECT, MS_INTERSECT, TGPRODUCT, 0},
     151             :     { "ital", TITALIC, '\0', TGFONTATTR, 5},
     152             :     { "italic", TITALIC, '\0', TGFONTATTR, 5},
     153             :     { "lambdabar" , TLAMBDABAR, MS_LAMBDABAR, TGSTANDALONE, 5},
     154             :     { "langle", TLANGLE, MS_LMATHANGLE, TGLBRACES, 5},
     155             :     { "lbrace", TLBRACE, MS_LBRACE, TGLBRACES, 5},
     156             :     { "lceil", TLCEIL, MS_LCEIL, TGLBRACES, 5},
     157             :     { "ldbracket", TLDBRACKET, MS_LDBRACKET, TGLBRACES, 5},
     158             :     { "ldline", TLDLINE, MS_DVERTLINE, TGLBRACES, 5},
     159             :     { "le", TLE, MS_LE, TGRELATION, 0},
     160             :     { "left", TLEFT, '\0', 0, 5},
     161             :     { "leftarrow" , TLEFTARROW, MS_LEFTARROW, TGSTANDALONE, 5},
     162             :     { "leslant", TLESLANT, MS_LESLANT, TGRELATION, 0 },
     163             :     { "lfloor", TLFLOOR, MS_LFLOOR, TGLBRACES, 5},
     164             :     { "lim", TLIM, '\0', TGOPER, 5},
     165             :     { "lime", TLIME, '\0', TGCOLOR, 0},
     166             :     { "liminf", TLIMINF, '\0', TGOPER, 5},
     167             :     { "limsup", TLIMSUP, '\0', TGOPER, 5},
     168             :     { "lint", TLINT, MS_LINT, TGOPER, 5},
     169             :     { "ll", TLL, MS_LL, TGRELATION, 0},
     170             :     { "lline", TLLINE, MS_VERTLINE, TGLBRACES, 5},
     171             :     { "llint", TLLINT, MS_LLINT, TGOPER, 5},
     172             :     { "lllint", TLLLINT, MS_LLLINT, TGOPER, 5},
     173             :     { "ln", TLN, '\0', TGFUNCTION, 5},
     174             :     { "log", TLOG, '\0', TGFUNCTION, 5},
     175             :     { "lsub", TLSUB, '\0', TGPOWER, 0},
     176             :     { "lsup", TLSUP, '\0', TGPOWER, 0},
     177             :     { "lt", TLT, MS_LT, TGRELATION, 0},
     178             :     { "magenta", TMAGENTA, '\0', TGCOLOR, 0},
     179             :     { "maroon", TMAROON, '\0', TGCOLOR, 0},
     180             :     { "matrix", TMATRIX, '\0', 0, 5},
     181             :     { "minusplus", TMINUSPLUS, MS_MINUSPLUS, TGUNOPER | TGSUM, 5},
     182             :     { "mline", TMLINE, MS_VERTLINE, 0, 0},      //! not in TGRBRACES, Level 0
     183             :     { "nabla", TNABLA, MS_NABLA, TGSTANDALONE, 5},
     184             :     { "navy", TNAVY, '\0', TGCOLOR, 0},
     185             :     { "nbold", TNBOLD, '\0', TGFONTATTR, 5},
     186             :     { "ndivides", TNDIVIDES, MS_NDIVIDES, TGRELATION, 0},
     187             :     { "neg", TNEG, MS_NEG, TGUNOPER, 5 },
     188             :     { "neq", TNEQ, MS_NEQ, TGRELATION, 0},
     189             :     { "newline", TNEWLINE, '\0', 0, 0},
     190             :     { "ni", TNI, MS_NI, TGRELATION, 0},
     191             :     { "nitalic", TNITALIC, '\0', TGFONTATTR, 5},
     192             :     { "none", TNONE, '\0', TGLBRACES | TGRBRACES, 0},
     193             :     { "nospace", TNOSPACE, '\0', TGSTANDALONE, 5},
     194             :     { "notin", TNOTIN, MS_NOTIN, TGRELATION, 0},
     195             :     { "nroot", TNROOT, MS_SQRT, TGUNOPER, 5},
     196             :     { "nsubset", TNSUBSET, MS_NSUBSET, TGRELATION, 0 },
     197             :     { "nsupset", TNSUPSET, MS_NSUPSET, TGRELATION, 0 },
     198             :     { "nsubseteq", TNSUBSETEQ, MS_NSUBSETEQ, TGRELATION, 0 },
     199             :     { "nsupseteq", TNSUPSETEQ, MS_NSUPSETEQ, TGRELATION, 0 },
     200             :     { "odivide", TODIVIDE, MS_ODIVIDE, TGPRODUCT, 0},
     201             :     { "odot", TODOT, MS_ODOT, TGPRODUCT, 0},
     202             :     { "olive", TOLIVE, '\0', TGCOLOR, 0},
     203             :     { "ominus", TOMINUS, MS_OMINUS, TGSUM, 0},
     204             :     { "oper", TOPER, '\0', TGOPER, 5},
     205             :     { "oplus", TOPLUS, MS_OPLUS, TGSUM, 0},
     206             :     { "or", TOR, MS_OR, TGSUM, 0},
     207             :     { "ortho", TORTHO, MS_ORTHO, TGRELATION, 0},
     208             :     { "otimes", TOTIMES, MS_OTIMES, TGPRODUCT, 0},
     209             :     { "over", TOVER, '\0', TGPRODUCT, 0},
     210             :     { "overbrace", TOVERBRACE, MS_OVERBRACE, TGPRODUCT, 5},
     211             :     { "overline", TOVERLINE, '\0', TGATTRIBUT, 5},
     212             :     { "overstrike", TOVERSTRIKE, '\0', TGATTRIBUT, 5},
     213             :     { "owns", TNI, MS_NI, TGRELATION, 0},
     214             :     { "parallel", TPARALLEL, MS_DLINE, TGRELATION, 0},
     215             :     { "partial", TPARTIAL, MS_PARTIAL, TGSTANDALONE, 5 },
     216             :     { "phantom", TPHANTOM, '\0', TGFONTATTR, 5},
     217             :     { "plusminus", TPLUSMINUS, MS_PLUSMINUS, TGUNOPER | TGSUM, 5},
     218             :     { "prec", TPRECEDES, MS_PRECEDES, TGRELATION, 0 },
     219             :     { "preccurlyeq", TPRECEDESEQUAL, MS_PRECEDESEQUAL, TGRELATION, 0 },
     220             :     { "precsim", TPRECEDESEQUIV, MS_PRECEDESEQUIV, TGRELATION, 0 },
     221             :     { "nprec", TNOTPRECEDES, MS_NOTPRECEDES, TGRELATION, 0 },
     222             :     { "prod", TPROD, MS_PROD, TGOPER, 5},
     223             :     { "prop", TPROP, MS_PROP, TGRELATION, 0},
     224             :     { "purple", TPURPLE, '\0', TGCOLOR, 0},
     225             :     { "rangle", TRANGLE, MS_RMATHANGLE, TGRBRACES, 0},  //! 0 to terminate expression
     226             :     { "rbrace", TRBRACE, MS_RBRACE, TGRBRACES, 0},
     227             :     { "rceil", TRCEIL, MS_RCEIL, TGRBRACES, 0},
     228             :     { "rdbracket", TRDBRACKET, MS_RDBRACKET, TGRBRACES, 0},
     229             :     { "rdline", TRDLINE, MS_DVERTLINE, TGRBRACES, 0},
     230             :     { "red", TRED, '\0', TGCOLOR, 0},
     231             :     { "rfloor", TRFLOOR, MS_RFLOOR, TGRBRACES, 0},  //! 0 to terminate expression
     232             :     { "right", TRIGHT, '\0', 0, 0},
     233             :     { "rightarrow" , TRIGHTARROW, MS_RIGHTARROW, TGSTANDALONE, 5},
     234             :     { "rline", TRLINE, MS_VERTLINE, TGRBRACES, 0},  //! 0 to terminate expression
     235             :     { "rsub", TRSUB, '\0', TGPOWER, 0},
     236             :     { "rsup", TRSUP, '\0', TGPOWER, 0},
     237             :     { "sans", TSANS, '\0', TGFONT, 0},
     238             :     { "serif", TSERIF, '\0', TGFONT, 0},
     239             :     { "setC" , TSETC, MS_SETC, TGSTANDALONE, 5},
     240             :     { "setN" , TSETN, MS_SETN, TGSTANDALONE, 5},
     241             :     { "setQ" , TSETQ, MS_SETQ, TGSTANDALONE, 5},
     242             :     { "setR" , TSETR, MS_SETR, TGSTANDALONE, 5},
     243             :     { "setZ" , TSETZ, MS_SETZ, TGSTANDALONE, 5},
     244             :     { "setminus", TBACKSLASH, MS_BACKSLASH, TGPRODUCT, 0 },
     245             :     { "silver", TSILVER, '\0', TGCOLOR, 0},
     246             :     { "sim", TSIM, MS_SIM, TGRELATION, 0},
     247             :     { "simeq", TSIMEQ, MS_SIMEQ, TGRELATION, 0},
     248             :     { "sin", TSIN, '\0', TGFUNCTION, 5},
     249             :     { "sinh", TSINH, '\0', TGFUNCTION, 5},
     250             :     { "size", TSIZE, '\0', TGFONTATTR, 5},
     251             :     { "slash", TSLASH, MS_SLASH, TGPRODUCT, 0 },
     252             :     { "sqrt", TSQRT, MS_SQRT, TGUNOPER, 5},
     253             :     { "stack", TSTACK, '\0', 0, 5},
     254             :     { "sub", TRSUB, '\0', TGPOWER, 0},
     255             :     { "subset", TSUBSET, MS_SUBSET, TGRELATION, 0},
     256             :     { "succ", TSUCCEEDS, MS_SUCCEEDS, TGRELATION, 0 },
     257             :     { "succcurlyeq", TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TGRELATION, 0 },
     258             :     { "succsim", TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TGRELATION, 0 },
     259             :     { "nsucc", TNOTSUCCEEDS, MS_NOTSUCCEEDS, TGRELATION, 0 },
     260             :     { "subseteq", TSUBSETEQ, MS_SUBSETEQ, TGRELATION, 0},
     261             :     { "sum", TSUM, MS_SUM, TGOPER, 5},
     262             :     { "sup", TRSUP, '\0', TGPOWER, 0},
     263             :     { "supset", TSUPSET, MS_SUPSET, TGRELATION, 0},
     264             :     { "supseteq", TSUPSETEQ, MS_SUPSETEQ, TGRELATION, 0},
     265             :     { "tan", TTAN, '\0', TGFUNCTION, 5},
     266             :     { "tanh", TTANH, '\0', TGFUNCTION, 5},
     267             :     { "teal", TTEAL, '\0', TGCOLOR, 0},
     268             :     { "tilde", TTILDE, MS_TILDE, TGATTRIBUT, 5},
     269             :     { "times", TTIMES, MS_TIMES, TGPRODUCT, 0},
     270             :     { "to", TTO, '\0', TGLIMIT, 0},
     271             :     { "toward", TTOWARD, MS_RIGHTARROW, TGRELATION, 0},
     272             :     { "transl", TTRANSL, MS_TRANSL, TGRELATION, 0},
     273             :     { "transr", TTRANSR, MS_TRANSR, TGRELATION, 0},
     274             :     { "underbrace", TUNDERBRACE, MS_UNDERBRACE, TGPRODUCT, 5},
     275             :     { "underline", TUNDERLINE, '\0', TGATTRIBUT, 5},
     276             :     { "union", TUNION, MS_UNION, TGSUM, 0},
     277             :     { "uoper", TUOPER, '\0', TGUNOPER, 5},
     278             :     { "uparrow" , TUPARROW, MS_UPARROW, TGSTANDALONE, 5},
     279             :     { "vec", TVEC, MS_VEC, TGATTRIBUT, 5},
     280             :     { "white", TWHITE, '\0', TGCOLOR, 0},
     281             :     { "widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TGPRODUCT, 0 },
     282             :     { "widehat", TWIDEHAT, MS_HAT, TGATTRIBUT, 5},
     283             :     { "widetilde", TWIDETILDE, MS_TILDE, TGATTRIBUT, 5},
     284             :     { "wideslash", TWIDESLASH, MS_SLASH, TGPRODUCT, 0 },
     285             :     { "widevec", TWIDEVEC, MS_VEC, TGATTRIBUT, 5},
     286             :     { "wp" , TWP, MS_WP, TGSTANDALONE, 5},
     287             :     { "yellow", TYELLOW, '\0', TGCOLOR, 0},
     288             :     { "", TEND, '\0', 0, 0}
     289             : };
     290             : 
     291        6556 : const SmTokenTableEntry * SmParser::GetTokenTableEntry( const OUString &rName )
     292             : {
     293        6556 :     const SmTokenTableEntry * pRes = 0;
     294        6556 :     if (!rName.isEmpty())
     295             :     {
     296     1164107 :         for (size_t i = 0; i < SAL_N_ELEMENTS(aTokenTable); ++i)
     297             :         {
     298     1161093 :             if (rName.equalsIgnoreAsciiCase( OUString::createFromAscii(aTokenTable[i].pIdent) ))
     299             :             {
     300        3542 :                 pRes = &aTokenTable[i];
     301        3542 :                 break;
     302             :             }
     303             :         }
     304             :     }
     305             : 
     306        6556 :     return pRes;
     307             : }
     308             : 
     309             : 
     310             : 
     311             : 
     312             : #if OSL_DEBUG_LEVEL > 1
     313             : 
     314             : static const sal_Unicode aDelimiterTable[] =
     315             : {
     316             :     ' ',    '\t',   '\n',   '\r',   '+',    '-',    '*',    '/',    '=',    '#',
     317             :     '%',    '\\',   '"',    '~',    '`',    '>',    '<',    '&',    '|',    '(',
     318             :     ')',    '{',    '}',    '[',    ']',    '^',    '_',
     319             :     '\0'    // end of list symbol
     320             : };
     321             : 
     322             : bool SmParser::IsDelimiter( const OUString &rTxt, sal_Int32 nPos )
     323             :     // returns 'true' iff cChar is '\0' or a delimiter
     324             : {
     325             :     assert(nPos <= rTxt.getLength()); //index out of range
     326             : 
     327             :     if (nPos == rTxt.getLength())
     328             :         return true;
     329             : 
     330             :     sal_Unicode cChar = rTxt[nPos];
     331             : 
     332             :     // check if 'cChar' is in the delimiter table
     333             :     const sal_Unicode *pDelim = &aDelimiterTable[0];
     334             :     for ( ;  *pDelim != 0;  pDelim++)
     335             :         if (*pDelim == cChar)
     336             :             break;
     337             : 
     338             : 
     339             :     sal_Int16 nTypJp = SM_MOD()->GetSysLocale().GetCharClass().getType( rTxt, nPos );
     340             :     bool bIsDelim = (*pDelim != 0 ||
     341             :             nTypJp == com::sun::star::i18n::UnicodeType::SPACE_SEPARATOR ||
     342             :             nTypJp == com::sun::star::i18n::UnicodeType::CONTROL);
     343             : 
     344             :     return bIsDelim;
     345             : }
     346             : 
     347             : #endif
     348             : 
     349           0 : void SmParser::Replace( sal_Int32 nPos, sal_Int32 nLen, const OUString &rText )
     350             : {
     351             :     OSL_ENSURE( nPos + nLen <= m_aBufferString.getLength(), "argument mismatch" );
     352             : 
     353           0 :     m_aBufferString = m_aBufferString.replaceAt( nPos, nLen, rText );
     354           0 :     sal_Int32 nChg = rText.getLength() - nLen;
     355           0 :     m_nBufferIndex = m_nBufferIndex + nChg;
     356           0 :     m_nTokenIndex = m_nTokenIndex + nChg;
     357           0 : }
     358             : 
     359             : 
     360             : // First character may be any alphabetic
     361             : const sal_Int32 coStartFlags =
     362             :         KParseTokens::ANY_LETTER |
     363             :         KParseTokens::IGNORE_LEADING_WS;
     364             : 
     365             : // Continuing characters may be any alphabetic
     366             : const sal_Int32 coContFlags =
     367             :     (coStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
     368             :     | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
     369             : 
     370             : // user-defined char continuing characters may be any alphanumeric or dot.
     371             : const sal_Int32 coUserDefinedCharContFlags =
     372             :     ((KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::IGNORE_LEADING_WS | KParseTokens::ASC_DOT)
     373             :         & ~KParseTokens::IGNORE_LEADING_WS)
     374             :     | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
     375             : 
     376             : // First character for numbers, may be any numeric or dot
     377             : const sal_Int32 coNumStartFlags =
     378             :         KParseTokens::ASC_DIGIT |
     379             :         KParseTokens::ASC_DOT |
     380             :         KParseTokens::IGNORE_LEADING_WS;
     381             : // Continuing characters for numbers, may be any numeric or dot.
     382             : const sal_Int32 coNumContFlags =
     383             :     coNumStartFlags & ~KParseTokens::IGNORE_LEADING_WS;
     384             : 
     385       19180 : void SmParser::NextToken()
     386             : {
     387       19180 :     static const OUString aEmptyStr;
     388             : 
     389       19180 :     sal_Int32   nBufLen = m_aBufferString.getLength();
     390       19180 :     ParseResult aRes;
     391             :     sal_Int32   nRealStart;
     392             :     bool        bCont;
     393       38360 :     CharClass   aCC(SM_MOD()->GetSysLocale().GetLanguageTag());
     394       19180 :     do
     395             :     {
     396             :         // skip white spaces
     397       48088 :         while (UnicodeType::SPACE_SEPARATOR ==
     398       28908 :                         aCC.getType( m_aBufferString, m_nBufferIndex ))
     399        9728 :            ++m_nBufferIndex;
     400             : 
     401             :         // Try to parse a number. This should be independent from the locale
     402             :         // setting, so temporarily set the language to English.
     403             :         // See https://bz.apache.org/ooo/show_bug.cgi?id=45779
     404       19180 :         LanguageTag aOldLoc(aCC.getLanguageTag());
     405       19180 :         aCC.setLanguageTag(LanguageTag(m_aDotLoc));
     406       38360 :         aRes = aCC.parsePredefinedToken(KParseType::ASC_NUMBER,
     407             :                                         m_aBufferString, m_nBufferIndex,
     408             :                                         coNumStartFlags, aEmptyStr,
     409       19180 :                                         coNumContFlags, aEmptyStr);
     410       19180 :         aCC.setLanguageTag(aOldLoc);
     411             : 
     412       19180 :         if (aRes.TokenType == 0)
     413             :         {
     414             :             // Try again with the default token parsing.
     415       35812 :             aRes = aCC.parseAnyToken(m_aBufferString, m_nBufferIndex,
     416             :                                      coStartFlags, aEmptyStr,
     417       17906 :                                      coContFlags, aEmptyStr);
     418             :         }
     419             : 
     420       19180 :         nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
     421       19180 :         m_nBufferIndex = nRealStart;
     422             : 
     423       19180 :         bCont = false;
     424       40459 :         if ( aRes.TokenType == 0  &&
     425       19479 :                 nRealStart < nBufLen &&
     426         299 :                 '\n' == m_aBufferString[ nRealStart ] )
     427             :         {
     428             :             // keep data needed for tokens row and col entry up to date
     429           0 :             ++m_Row;
     430           0 :             m_nBufferIndex = m_nColOff = nRealStart + 1;
     431           0 :             bCont = true;
     432             :         }
     433       19180 :         else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
     434             :         {
     435        8432 :             if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
     436             :             {
     437             :                 //SkipComment
     438           0 :                 m_nBufferIndex = nRealStart + 2;
     439           0 :                 while (m_nBufferIndex < nBufLen  &&
     440           0 :                     '\n' != m_aBufferString[ m_nBufferIndex ])
     441           0 :                     ++m_nBufferIndex;
     442           0 :                 bCont = true;
     443             :             }
     444       19180 :         }
     445             : 
     446             :     } while (bCont);
     447             : 
     448             :     // set index of current token
     449       19180 :     m_nTokenIndex = m_nBufferIndex;
     450             : 
     451       19180 :     m_aCurToken.nRow   = m_Row;
     452       19180 :     m_aCurToken.nCol   = nRealStart - m_nColOff + 1;
     453             : 
     454       19180 :     bool bHandled = true;
     455       19180 :     if (nRealStart >= nBufLen)
     456             :     {
     457        1800 :         m_aCurToken.eType    = TEND;
     458        1800 :         m_aCurToken.cMathChar = '\0';
     459        1800 :         m_aCurToken.nGroup       = 0;
     460        1800 :         m_aCurToken.nLevel       = 0;
     461        1800 :         m_aCurToken.aText.clear();
     462             :     }
     463       17380 :     else if (aRes.TokenType & KParseType::ANY_NUMBER)
     464             :     {
     465        1274 :         sal_Int32 n = aRes.EndPos - nRealStart;
     466             :         OSL_ENSURE( n >= 0, "length < 0" );
     467        1274 :         m_aCurToken.eType      = TNUMBER;
     468        1274 :         m_aCurToken.cMathChar  = '\0';
     469        1274 :         m_aCurToken.nGroup     = 0;
     470        1274 :         m_aCurToken.nLevel     = 5;
     471        1274 :         m_aCurToken.aText      = m_aBufferString.copy( nRealStart, n );
     472             : 
     473             : #if OSL_DEBUG_LEVEL > 1
     474             :         if (!IsDelimiter( m_aBufferString, aRes.EndPos ))
     475             :             SAL_WARN( "starmath", "identifier really finished? (compatibility!)" );
     476             : #endif
     477             :     }
     478       16106 :     else if (aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING)
     479             :     {
     480          94 :         m_aCurToken.eType      = TTEXT;
     481          94 :         m_aCurToken.cMathChar  = '\0';
     482          94 :         m_aCurToken.nGroup     = 0;
     483          94 :         m_aCurToken.nLevel     = 5;
     484          94 :         m_aCurToken.aText     = aRes.DequotedNameOrString;
     485          94 :         m_aCurToken.nRow       = m_Row;
     486          94 :         m_aCurToken.nCol       = nRealStart - m_nColOff + 2;
     487             :     }
     488       16012 :     else if (aRes.TokenType & KParseType::IDENTNAME)
     489             :     {
     490        6543 :         sal_Int32 n = aRes.EndPos - nRealStart;
     491             :         OSL_ENSURE( n >= 0, "length < 0" );
     492        6543 :         OUString aName( m_aBufferString.copy( nRealStart, n ) );
     493        6543 :         const SmTokenTableEntry *pEntry = GetTokenTableEntry( aName );
     494             : 
     495        6543 :         if (pEntry)
     496             :         {
     497        3542 :             m_aCurToken.eType      = pEntry->eType;
     498        3542 :             m_aCurToken.cMathChar  = pEntry->cMathChar;
     499        3542 :             m_aCurToken.nGroup     = pEntry->nGroup;
     500        3542 :             m_aCurToken.nLevel     = pEntry->nLevel;
     501        3542 :             m_aCurToken.aText      = OUString::createFromAscii( pEntry->pIdent );
     502             :         }
     503             :         else
     504             :         {
     505        3001 :             m_aCurToken.eType      = TIDENT;
     506        3001 :             m_aCurToken.cMathChar  = '\0';
     507        3001 :             m_aCurToken.nGroup     = 0;
     508        3001 :             m_aCurToken.nLevel     = 5;
     509        3001 :             m_aCurToken.aText      = aName;
     510             : 
     511             : #if OSL_DEBUG_LEVEL > 1
     512             :             if (!IsDelimiter( m_aBufferString, aRes.EndPos ))
     513             :                 SAL_WARN( "starmath", "identifier really finished? (compatibility!)" );
     514             : #endif
     515        6543 :         }
     516             :     }
     517        9469 :     else if (aRes.TokenType == 0  &&  '_' == m_aBufferString[ nRealStart ])
     518             :     {
     519          10 :         m_aCurToken.eType    = TRSUB;
     520          10 :         m_aCurToken.cMathChar = '\0';
     521          10 :         m_aCurToken.nGroup       = TGPOWER;
     522          10 :         m_aCurToken.nLevel       = 0;
     523          10 :         m_aCurToken.aText = "_";
     524             : 
     525          10 :         aRes.EndPos = nRealStart + 1;
     526             :     }
     527        9459 :     else if (aRes.TokenType & KParseType::BOOLEAN)
     528             :     {
     529         738 :         sal_Int32   &rnEndPos = aRes.EndPos;
     530         738 :         if (rnEndPos - nRealStart <= 2)
     531             :         {
     532         738 :             sal_Unicode ch = m_aBufferString[ nRealStart ];
     533         738 :             switch (ch)
     534             :             {
     535             :                 case '<':
     536             :                     {
     537         710 :                         if (m_aBufferString.match("<<", nRealStart))
     538             :                         {
     539           0 :                             m_aCurToken.eType    = TLL;
     540           0 :                             m_aCurToken.cMathChar = MS_LL;
     541           0 :                             m_aCurToken.nGroup       = TGRELATION;
     542           0 :                             m_aCurToken.nLevel       = 0;
     543           0 :                             m_aCurToken.aText = "<<";
     544             : 
     545           0 :                             rnEndPos = nRealStart + 2;
     546             :                         }
     547         710 :                         else if (m_aBufferString.match("<=", nRealStart))
     548             :                         {
     549           2 :                             m_aCurToken.eType    = TLE;
     550           2 :                             m_aCurToken.cMathChar = MS_LE;
     551           2 :                             m_aCurToken.nGroup       = TGRELATION;
     552           2 :                             m_aCurToken.nLevel       = 0;
     553           2 :                             m_aCurToken.aText = "<=";
     554             : 
     555           2 :                             rnEndPos = nRealStart + 2;
     556             :                         }
     557         708 :                         else if (m_aBufferString.match("<-", nRealStart))
     558             :                         {
     559           0 :                             m_aCurToken.eType    = TLEFTARROW;
     560           0 :                             m_aCurToken.cMathChar = MS_LEFTARROW;
     561           0 :                             m_aCurToken.nGroup       = TGSTANDALONE;
     562           0 :                             m_aCurToken.nLevel       = 5;
     563           0 :                             m_aCurToken.aText = "<-";
     564             : 
     565           0 :                             rnEndPos = nRealStart + 2;
     566             :                         }
     567         708 :                         else if (m_aBufferString.match("<>", nRealStart))
     568             :                         {
     569           4 :                             m_aCurToken.eType    = TNEQ;
     570           4 :                             m_aCurToken.cMathChar = MS_NEQ;
     571           4 :                             m_aCurToken.nGroup       = TGRELATION;
     572           4 :                             m_aCurToken.nLevel       = 0;
     573           4 :                             m_aCurToken.aText = "<>";
     574             : 
     575           4 :                             rnEndPos = nRealStart + 2;
     576             :                         }
     577         704 :                         else if (m_aBufferString.match("<?>", nRealStart))
     578             :                         {
     579         654 :                             m_aCurToken.eType    = TPLACE;
     580         654 :                             m_aCurToken.cMathChar = MS_PLACE;
     581         654 :                             m_aCurToken.nGroup       = 0;
     582         654 :                             m_aCurToken.nLevel       = 5;
     583         654 :                             m_aCurToken.aText = "<?>";
     584             : 
     585         654 :                             rnEndPos = nRealStart + 3;
     586             :                         }
     587             :                         else
     588             :                         {
     589          50 :                             m_aCurToken.eType    = TLT;
     590          50 :                             m_aCurToken.cMathChar = MS_LT;
     591          50 :                             m_aCurToken.nGroup       = TGRELATION;
     592          50 :                             m_aCurToken.nLevel       = 0;
     593          50 :                             m_aCurToken.aText = "<";
     594             :                         }
     595             :                     }
     596         710 :                     break;
     597             :                 case '>':
     598             :                     {
     599          28 :                         if (m_aBufferString.match(">=", nRealStart))
     600             :                         {
     601           2 :                             m_aCurToken.eType    = TGE;
     602           2 :                             m_aCurToken.cMathChar = MS_GE;
     603           2 :                             m_aCurToken.nGroup       = TGRELATION;
     604           2 :                             m_aCurToken.nLevel       = 0;
     605           2 :                             m_aCurToken.aText = ">=";
     606             : 
     607           2 :                             rnEndPos = nRealStart + 2;
     608             :                         }
     609          26 :                         else if (m_aBufferString.match(">>", nRealStart))
     610             :                         {
     611           0 :                             m_aCurToken.eType    = TGG;
     612           0 :                             m_aCurToken.cMathChar = MS_GG;
     613           0 :                             m_aCurToken.nGroup       = TGRELATION;
     614           0 :                             m_aCurToken.nLevel       = 0;
     615           0 :                             m_aCurToken.aText = ">>";
     616             : 
     617           0 :                             rnEndPos = nRealStart + 2;
     618             :                         }
     619             :                         else
     620             :                         {
     621          26 :                             m_aCurToken.eType    = TGT;
     622          26 :                             m_aCurToken.cMathChar = MS_GT;
     623          26 :                             m_aCurToken.nGroup       = TGRELATION;
     624          26 :                             m_aCurToken.nLevel       = 0;
     625          26 :                             m_aCurToken.aText = ">";
     626             :                         }
     627             :                     }
     628          28 :                     break;
     629             :                 default:
     630           0 :                     bHandled = false;
     631             :             }
     632             :         }
     633             :     }
     634        8721 :     else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
     635             :     {
     636        8432 :         sal_Int32   &rnEndPos = aRes.EndPos;
     637        8432 :         if (rnEndPos - nRealStart == 1)
     638             :         {
     639        8432 :             sal_Unicode ch = m_aBufferString[ nRealStart ];
     640        8432 :             switch (ch)
     641             :             {
     642             :                 case '%':
     643             :                     {
     644             :                         //! modifies aRes.EndPos
     645             : 
     646             :                         OSL_ENSURE( rnEndPos >= nBufLen  ||
     647             :                                     '%' != m_aBufferString[ rnEndPos ],
     648             :                                 "unexpected comment start" );
     649             : 
     650             :                         // get identifier of user-defined character
     651             :                         ParseResult aTmpRes = aCC.parseAnyToken(
     652             :                                 m_aBufferString, rnEndPos,
     653             :                                 KParseTokens::ANY_LETTER,
     654             :                                 aEmptyStr,
     655             :                                 coUserDefinedCharContFlags,
     656         137 :                                 aEmptyStr );
     657             : 
     658         137 :                         sal_Int32 nTmpStart = rnEndPos + aTmpRes.LeadingWhiteSpace;
     659             : 
     660             :                         // default setting for the case that no identifier
     661             :                         // i.e. a valid symbol-name is following the '%'
     662             :                         // character
     663         137 :                         m_aCurToken.eType      = TTEXT;
     664         137 :                         m_aCurToken.cMathChar  = '\0';
     665         137 :                         m_aCurToken.nGroup     = 0;
     666         137 :                         m_aCurToken.nLevel     = 5;
     667         137 :                         m_aCurToken.aText.clear();
     668         137 :                         m_aCurToken.nRow       = m_Row;
     669         137 :                         m_aCurToken.nCol       = nTmpStart - m_nColOff;
     670             : 
     671         137 :                         if (aTmpRes.TokenType & KParseType::IDENTNAME)
     672             :                         {
     673             : 
     674         131 :                             sal_Int32 n = aTmpRes.EndPos - nTmpStart;
     675         131 :                             m_aCurToken.eType      = TSPECIAL;
     676         131 :                             m_aCurToken.aText      = m_aBufferString.copy( nTmpStart-1, n+1 );
     677             : 
     678             :                             OSL_ENSURE( aTmpRes.EndPos > rnEndPos,
     679             :                                     "empty identifier" );
     680         131 :                             if (aTmpRes.EndPos > rnEndPos)
     681         131 :                                 rnEndPos = aTmpRes.EndPos;
     682             :                             else
     683           0 :                                 ++rnEndPos;
     684         137 :                         }
     685             : 
     686             :                         // if no symbol-name was found we start-over with
     687             :                         // finding the next token right afer the '%' sign.
     688             :                         // I.e. we leave rnEndPos unmodified.
     689             :                     }
     690         137 :                     break;
     691             :                 case '[':
     692             :                     {
     693          61 :                         m_aCurToken.eType    = TLBRACKET;
     694          61 :                         m_aCurToken.cMathChar = MS_LBRACKET;
     695          61 :                         m_aCurToken.nGroup       = TGLBRACES;
     696          61 :                         m_aCurToken.nLevel       = 5;
     697          61 :                         m_aCurToken.aText = "[";
     698             :                     }
     699          61 :                     break;
     700             :                 case '\\':
     701             :                     {
     702          30 :                         m_aCurToken.eType    = TESCAPE;
     703          30 :                         m_aCurToken.cMathChar = '\0';
     704          30 :                         m_aCurToken.nGroup       = 0;
     705          30 :                         m_aCurToken.nLevel       = 5;
     706          30 :                         m_aCurToken.aText = "\\";
     707             :                     }
     708          30 :                     break;
     709             :                 case ']':
     710             :                     {
     711          61 :                         m_aCurToken.eType    = TRBRACKET;
     712          61 :                         m_aCurToken.cMathChar = MS_RBRACKET;
     713          61 :                         m_aCurToken.nGroup       = TGRBRACES;
     714          61 :                         m_aCurToken.nLevel       = 0;
     715          61 :                         m_aCurToken.aText = "]";
     716             :                     }
     717          61 :                     break;
     718             :                 case '^':
     719             :                     {
     720         341 :                         m_aCurToken.eType    = TRSUP;
     721         341 :                         m_aCurToken.cMathChar = '\0';
     722         341 :                         m_aCurToken.nGroup       = TGPOWER;
     723         341 :                         m_aCurToken.nLevel       = 0;
     724         341 :                         m_aCurToken.aText = "^";
     725             :                     }
     726         341 :                     break;
     727             :                 case '`':
     728             :                     {
     729           2 :                         m_aCurToken.eType    = TSBLANK;
     730           2 :                         m_aCurToken.cMathChar = '\0';
     731           2 :                         m_aCurToken.nGroup       = TGBLANK;
     732           2 :                         m_aCurToken.nLevel       = 5;
     733           2 :                         m_aCurToken.aText = "`";
     734             :                     }
     735           2 :                     break;
     736             :                 case '{':
     737             :                     {
     738        3027 :                         m_aCurToken.eType    = TLGROUP;
     739        3027 :                         m_aCurToken.cMathChar = MS_LBRACE;
     740        3027 :                         m_aCurToken.nGroup       = 0;
     741        3027 :                         m_aCurToken.nLevel       = 5;
     742        3027 :                         m_aCurToken.aText = "{";
     743             :                     }
     744        3027 :                     break;
     745             :                 case '|':
     746             :                     {
     747           0 :                         m_aCurToken.eType    = TOR;
     748           0 :                         m_aCurToken.cMathChar = MS_OR;
     749           0 :                         m_aCurToken.nGroup       = TGSUM;
     750           0 :                         m_aCurToken.nLevel       = 0;
     751           0 :                         m_aCurToken.aText = "|";
     752             :                     }
     753           0 :                     break;
     754             :                 case '}':
     755             :                     {
     756        3021 :                         m_aCurToken.eType    = TRGROUP;
     757        3021 :                         m_aCurToken.cMathChar = MS_RBRACE;
     758        3021 :                         m_aCurToken.nGroup       = 0;
     759        3021 :                         m_aCurToken.nLevel       = 0;
     760        3021 :                         m_aCurToken.aText = "}";
     761             :                     }
     762        3021 :                     break;
     763             :                 case '~':
     764             :                     {
     765           2 :                         m_aCurToken.eType    = TBLANK;
     766           2 :                         m_aCurToken.cMathChar = '\0';
     767           2 :                         m_aCurToken.nGroup       = TGBLANK;
     768           2 :                         m_aCurToken.nLevel       = 5;
     769           2 :                         m_aCurToken.aText = "~";
     770             :                     }
     771           2 :                     break;
     772             :                 case '#':
     773             :                     {
     774         145 :                         if (m_aBufferString.match("##", nRealStart))
     775             :                         {
     776          26 :                             m_aCurToken.eType    = TDPOUND;
     777          26 :                             m_aCurToken.cMathChar = '\0';
     778          26 :                             m_aCurToken.nGroup       = 0;
     779          26 :                             m_aCurToken.nLevel       = 0;
     780          26 :                             m_aCurToken.aText = "##";
     781             : 
     782          26 :                             rnEndPos = nRealStart + 2;
     783             :                         }
     784             :                         else
     785             :                         {
     786         119 :                             m_aCurToken.eType    = TPOUND;
     787         119 :                             m_aCurToken.cMathChar = '\0';
     788         119 :                             m_aCurToken.nGroup       = 0;
     789         119 :                             m_aCurToken.nLevel       = 0;
     790         119 :                             m_aCurToken.aText = "#";
     791             :                         }
     792             :                     }
     793         145 :                     break;
     794             :                 case '&':
     795             :                     {
     796           0 :                         m_aCurToken.eType    = TAND;
     797           0 :                         m_aCurToken.cMathChar = MS_AND;
     798           0 :                         m_aCurToken.nGroup       = TGPRODUCT;
     799           0 :                         m_aCurToken.nLevel       = 0;
     800           0 :                         m_aCurToken.aText = "&";
     801             :                     }
     802           0 :                     break;
     803             :                 case '(':
     804             :                     {
     805         321 :                         m_aCurToken.eType    = TLPARENT;
     806         321 :                         m_aCurToken.cMathChar = MS_LPARENT;
     807         321 :                         m_aCurToken.nGroup       = TGLBRACES;
     808         321 :                         m_aCurToken.nLevel       = 5;     //! 0 to continue expression
     809         321 :                         m_aCurToken.aText = "(";
     810             :                     }
     811         321 :                     break;
     812             :                 case ')':
     813             :                     {
     814         321 :                         m_aCurToken.eType    = TRPARENT;
     815         321 :                         m_aCurToken.cMathChar = MS_RPARENT;
     816         321 :                         m_aCurToken.nGroup       = TGRBRACES;
     817         321 :                         m_aCurToken.nLevel       = 0;     //! 0 to terminate expression
     818         321 :                         m_aCurToken.aText = ")";
     819             :                     }
     820         321 :                     break;
     821             :                 case '*':
     822             :                     {
     823          19 :                         m_aCurToken.eType    = TMULTIPLY;
     824          19 :                         m_aCurToken.cMathChar = MS_MULTIPLY;
     825          19 :                         m_aCurToken.nGroup       = TGPRODUCT;
     826          19 :                         m_aCurToken.nLevel       = 0;
     827          19 :                         m_aCurToken.aText = "*";
     828             :                     }
     829          19 :                     break;
     830             :                 case '+':
     831             :                     {
     832         381 :                         if (m_aBufferString.match("+-", nRealStart))
     833             :                         {
     834          19 :                             m_aCurToken.eType    = TPLUSMINUS;
     835          19 :                             m_aCurToken.cMathChar = MS_PLUSMINUS;
     836          19 :                             m_aCurToken.nGroup       = TGUNOPER | TGSUM;
     837          19 :                             m_aCurToken.nLevel       = 5;
     838          19 :                             m_aCurToken.aText = "+-";
     839             : 
     840          19 :                             rnEndPos = nRealStart + 2;
     841             :                         }
     842             :                         else
     843             :                         {
     844         362 :                             m_aCurToken.eType    = TPLUS;
     845         362 :                             m_aCurToken.cMathChar = MS_PLUS;
     846         362 :                             m_aCurToken.nGroup       = TGUNOPER | TGSUM;
     847         362 :                             m_aCurToken.nLevel       = 5;
     848         362 :                             m_aCurToken.aText = "+";
     849             :                         }
     850             :                     }
     851         381 :                     break;
     852             :                 case '-':
     853             :                     {
     854         164 :                         if (m_aBufferString.match("-+", nRealStart))
     855             :                         {
     856          19 :                             m_aCurToken.eType    = TMINUSPLUS;
     857          19 :                             m_aCurToken.cMathChar = MS_MINUSPLUS;
     858          19 :                             m_aCurToken.nGroup       = TGUNOPER | TGSUM;
     859          19 :                             m_aCurToken.nLevel       = 5;
     860          19 :                             m_aCurToken.aText = "-+";
     861             : 
     862          19 :                             rnEndPos = nRealStart + 2;
     863             :                         }
     864         145 :                         else if (m_aBufferString.match("->", nRealStart))
     865             :                         {
     866           0 :                             m_aCurToken.eType    = TRIGHTARROW;
     867           0 :                             m_aCurToken.cMathChar = MS_RIGHTARROW;
     868           0 :                             m_aCurToken.nGroup       = TGSTANDALONE;
     869           0 :                             m_aCurToken.nLevel       = 5;
     870           0 :                             m_aCurToken.aText = "->";
     871             : 
     872           0 :                             rnEndPos = nRealStart + 2;
     873             :                         }
     874             :                         else
     875             :                         {
     876         145 :                             m_aCurToken.eType    = TMINUS;
     877         145 :                             m_aCurToken.cMathChar = MS_MINUS;
     878         145 :                             m_aCurToken.nGroup       = TGUNOPER | TGSUM;
     879         145 :                             m_aCurToken.nLevel       = 5;
     880         145 :                             m_aCurToken.aText = "-";
     881             :                         }
     882             :                     }
     883         164 :                     break;
     884             :                 case '.':
     885             :                     {
     886             :                         // Only one character? Then it can't be a number.
     887           9 :                         if (m_nBufferIndex < m_aBufferString.getLength() - 1)
     888             :                         {
     889             :                             // for compatibility with SO5.2
     890             :                             // texts like .34 ...56 ... h ...78..90
     891             :                             // will be treated as numbers
     892           3 :                             m_aCurToken.eType     = TNUMBER;
     893           3 :                             m_aCurToken.cMathChar = '\0';
     894           3 :                             m_aCurToken.nGroup       = 0;
     895           3 :                             m_aCurToken.nLevel    = 5;
     896             : 
     897           3 :                             sal_Int32 nTxtStart = m_nBufferIndex;
     898             :                             sal_Unicode cChar;
     899             :                             // if the equation ends with dot(.) then increment m_nBufferIndex till end of string only
     900           3 :                             do
     901             :                             {
     902           3 :                                 cChar = m_aBufferString[ ++m_nBufferIndex ];
     903             :                             }
     904           6 :                             while ( (cChar == '.' || rtl::isAsciiDigit( cChar )) &&
     905           3 :                                      ( m_nBufferIndex < m_aBufferString.getLength() - 1 ) );
     906             : 
     907           3 :                             m_aCurToken.aText = m_aBufferString.copy( nTxtStart, m_nBufferIndex - nTxtStart );
     908           3 :                             aRes.EndPos = m_nBufferIndex;
     909             :                         }
     910             :                         else
     911           6 :                             bHandled = false;
     912             :                     }
     913           9 :                     break;
     914             :                 case '/':
     915             :                     {
     916          35 :                         m_aCurToken.eType    = TDIVIDEBY;
     917          35 :                         m_aCurToken.cMathChar = MS_SLASH;
     918          35 :                         m_aCurToken.nGroup       = TGPRODUCT;
     919          35 :                         m_aCurToken.nLevel       = 0;
     920          35 :                         m_aCurToken.aText = "/";
     921             :                     }
     922          35 :                     break;
     923             :                 case '=':
     924             :                     {
     925         232 :                         m_aCurToken.eType    = TASSIGN;
     926         232 :                         m_aCurToken.cMathChar = MS_ASSIGN;
     927         232 :                         m_aCurToken.nGroup       = TGRELATION;
     928         232 :                         m_aCurToken.nLevel       = 0;
     929         232 :                         m_aCurToken.aText = "=";
     930             :                     }
     931         232 :                     break;
     932             :                 default:
     933         123 :                     bHandled = false;
     934             :             }
     935             :         }
     936             :     }
     937             :     else
     938         289 :         bHandled = false;
     939             : 
     940       19180 :     if (!bHandled)
     941             :     {
     942         418 :         m_aCurToken.eType      = TCHARACTER;
     943         418 :         m_aCurToken.cMathChar  = '\0';
     944         418 :         m_aCurToken.nGroup     = 0;
     945         418 :         m_aCurToken.nLevel     = 5;
     946         418 :         m_aCurToken.aText      = m_aBufferString.copy( nRealStart, 1 );
     947             : 
     948         418 :         aRes.EndPos = nRealStart + 1;
     949             :     }
     950             : 
     951       19180 :     if (TEND != m_aCurToken.eType)
     952       36560 :         m_nBufferIndex = aRes.EndPos;
     953       19180 : }
     954             : 
     955             : 
     956             : 
     957             : // grammar
     958             : 
     959             : 
     960             : 
     961         910 : void SmParser::Table()
     962             : {
     963         910 :     SmNodeArray  LineArray;
     964             : 
     965         910 :     Line();
     966        1820 :     while (m_aCurToken.eType == TNEWLINE)
     967             :     {
     968           0 :         NextToken();
     969           0 :         Line();
     970             :     }
     971             : 
     972         910 :     if (m_aCurToken.eType != TEND)
     973           0 :         Error(PE_UNEXPECTED_CHAR);
     974             : 
     975         910 :     auto n = m_aNodeStack.size();
     976             : 
     977         910 :     LineArray.resize(n);
     978             : 
     979        1822 :     for (size_t i = 0; i < n; i++)
     980             :     {
     981         912 :         auto pNode = m_aNodeStack.pop_front();
     982         912 :         LineArray[n - (i + 1)] = pNode.release();
     983         912 :     }
     984             : 
     985         910 :     SmStructureNode *pSNode = new SmTableNode(m_aCurToken);
     986         910 :     pSNode->SetSubNodes(LineArray);
     987         910 :     m_aNodeStack.push_front(pSNode);
     988         910 : }
     989             : 
     990             : 
     991        4543 : void SmParser::Align()
     992             :     // parse alignment info (if any), then go on with rest of expression
     993             : {
     994        4543 :     SmStructureNode *pSNode = 0;
     995             : 
     996        4543 :     if (TokenInGroup(TGALIGN))
     997             :     {
     998          14 :         pSNode = new SmAlignNode(m_aCurToken);
     999             : 
    1000          14 :         NextToken();
    1001             : 
    1002             :         // allow for just one align statement in 5.0
    1003          14 :         if (TokenInGroup(TGALIGN))
    1004             :         {
    1005           0 :             Error(PE_DOUBLE_ALIGN);
    1006           0 :             delete pSNode;
    1007        4543 :             return;
    1008             :         }
    1009             :     }
    1010             : 
    1011        4543 :     Expression();
    1012             : 
    1013        4543 :     if (pSNode)
    1014             :     {
    1015          14 :         pSNode->SetSubNodes(popOrZero(m_aNodeStack), 0);
    1016          14 :         m_aNodeStack.push_front(pSNode);
    1017             :     }
    1018             : }
    1019             : 
    1020             : 
    1021         910 : void SmParser::Line()
    1022             : {
    1023         910 :     sal_uInt16  n = 0;
    1024         910 :     SmNodeArray  ExpressionArray;
    1025             : 
    1026         910 :     ExpressionArray.resize(n);
    1027             : 
    1028             :     // start with single expression that may have an alignment statement
    1029             :     // (and go on with expressions that must not have alignment
    1030             :     // statements in 'while' loop below. See also 'Expression()'.)
    1031         910 :     if (m_aCurToken.eType != TEND  &&  m_aCurToken.eType != TNEWLINE)
    1032         878 :     {   Align();
    1033         878 :         ExpressionArray.resize(++n);
    1034         878 :         ExpressionArray[n - 1] = popOrZero(m_aNodeStack);
    1035             :     }
    1036             : 
    1037        1820 :     while (m_aCurToken.eType != TEND  &&  m_aCurToken.eType != TNEWLINE)
    1038             :     {
    1039           0 :         Expression();
    1040           0 :         ExpressionArray.resize(++n);
    1041           0 :         ExpressionArray[n - 1] = popOrZero(m_aNodeStack);
    1042             :     }
    1043             : 
    1044             :     //If there's no expression, add an empty one.
    1045             :     //this is to avoid a formula tree without any caret
    1046             :     //positions, in visual formula editor.
    1047         910 :     if(ExpressionArray.empty())
    1048             :     {
    1049          32 :         SmToken aTok = SmToken();
    1050          32 :         aTok.eType = TNEWLINE;
    1051          32 :         ExpressionArray.push_back(new SmExpressionNode(aTok));
    1052             :     }
    1053             : 
    1054         910 :     SmStructureNode *pSNode = new SmLineNode(m_aCurToken);
    1055         910 :     pSNode->SetSubNodes(ExpressionArray);
    1056         910 :     m_aNodeStack.push_front(pSNode);
    1057         910 : }
    1058             : 
    1059             : 
    1060        5409 : void SmParser::Expression()
    1061             : {
    1062        5409 :     bool bUseExtraSpaces = true;
    1063        5409 :     if (!m_aNodeStack.empty())
    1064             :     {
    1065         288 :         auto pNode = m_aNodeStack.pop_front();
    1066         288 :         if (pNode->GetToken().eType == TNOSPACE)
    1067           0 :             bUseExtraSpaces = false;
    1068             :         else
    1069         288 :             m_aNodeStack.push_front(pNode.release());  // push the node from above again (now to be used as argument to this current 'nospace' node)
    1070             :     }
    1071             : 
    1072        5409 :     sal_uInt16       n = 0;
    1073        5409 :     SmNodeArray  RelationArray;
    1074             : 
    1075        5409 :     RelationArray.resize(n);
    1076             : 
    1077        5409 :     Relation();
    1078        5409 :     RelationArray.resize(++n);
    1079        5409 :     RelationArray[n - 1] = popOrZero(m_aNodeStack);
    1080             : 
    1081       12375 :     while (m_aCurToken.nLevel >= 4)
    1082        1557 :     {   Relation();
    1083        1557 :         RelationArray.resize(++n);
    1084        1557 :         RelationArray[n - 1] = popOrZero(m_aNodeStack);
    1085             :     }
    1086             : 
    1087        5409 :     if (n > 1)
    1088             :     {
    1089         713 :         SmExpressionNode *pSNode = new SmExpressionNode(m_aCurToken);
    1090         713 :         pSNode->SetSubNodes(RelationArray);
    1091         713 :         pSNode->SetUseExtraSpaces(bUseExtraSpaces);
    1092         713 :         m_aNodeStack.push_front(pSNode);
    1093             :     }
    1094             :     else
    1095             :     {
    1096             :         // This expression has only one node so just push this node.
    1097        4696 :         m_aNodeStack.push_front(RelationArray[0]);
    1098        5409 :     }
    1099        5409 : }
    1100             : 
    1101             : 
    1102        7249 : void SmParser::Relation()
    1103             : {
    1104        7249 :     Sum();
    1105       14860 :     while (TokenInGroup(TGRELATION))
    1106             :     {
    1107         362 :         SmStructureNode *pSNode  = new SmBinHorNode(m_aCurToken);
    1108         362 :         SmNode *pFirst = popOrZero(m_aNodeStack);
    1109             : 
    1110         362 :         OpSubSup();
    1111         362 :         SmNode *pSecond = popOrZero(m_aNodeStack);
    1112             : 
    1113         362 :         Sum();
    1114             : 
    1115         362 :         pSNode->SetSubNodes(pFirst, pSecond, popOrZero(m_aNodeStack));
    1116         362 :         m_aNodeStack.push_front(pSNode);
    1117             :     }
    1118        7249 : }
    1119             : 
    1120             : 
    1121        7707 : void SmParser::Sum()
    1122             : {
    1123        7707 :     Product();
    1124       15829 :     while (TokenInGroup(TGSUM))
    1125             :     {
    1126         415 :         SmStructureNode *pSNode  = new SmBinHorNode(m_aCurToken);
    1127         415 :         SmNode *pFirst = popOrZero(m_aNodeStack);
    1128             : 
    1129         415 :         OpSubSup();
    1130         415 :         SmNode *pSecond = popOrZero(m_aNodeStack);
    1131             : 
    1132         415 :         Product();
    1133             : 
    1134         415 :         pSNode->SetSubNodes(pFirst, pSecond, popOrZero(m_aNodeStack));
    1135         415 :         m_aNodeStack.push_front(pSNode);
    1136             :     }
    1137        7707 : }
    1138             : 
    1139             : 
    1140        8122 : void SmParser::Product()
    1141             : {
    1142        8122 :     Power();
    1143             : 
    1144       16727 :     while (TokenInGroup(TGPRODUCT))
    1145             :     {   SmStructureNode *pSNode;
    1146         483 :         SmNode *pFirst = popOrZero(m_aNodeStack),
    1147             :                *pOper;
    1148         483 :         bool bSwitchArgs = false;
    1149             : 
    1150         483 :         SmTokenType eType = m_aCurToken.eType;
    1151         483 :         switch (eType)
    1152             :         {
    1153             :             case TOVER:
    1154         255 :                 pSNode = new SmBinVerNode(m_aCurToken);
    1155         255 :                 pOper = new SmRectangleNode(m_aCurToken);
    1156         255 :                 NextToken();
    1157         255 :                 break;
    1158             : 
    1159             :             case TBOPER:
    1160           0 :                 pSNode = new SmBinHorNode(m_aCurToken);
    1161             : 
    1162           0 :                 NextToken();
    1163             : 
    1164             :                 //Let the glyph node know it's a binary operation
    1165           0 :                 m_aCurToken.eType = TBOPER;
    1166           0 :                 m_aCurToken.nGroup = TGPRODUCT;
    1167             : 
    1168           0 :                 GlyphSpecial();
    1169           0 :                 pOper = popOrZero(m_aNodeStack);
    1170           0 :                 break;
    1171             : 
    1172             :             case TOVERBRACE :
    1173             :             case TUNDERBRACE :
    1174          40 :                 pSNode = new SmVerticalBraceNode(m_aCurToken);
    1175          40 :                 pOper = new SmMathSymbolNode(m_aCurToken);
    1176             : 
    1177          40 :                 NextToken();
    1178          40 :                 break;
    1179             : 
    1180             :             case TWIDEBACKSLASH:
    1181             :             case TWIDESLASH:
    1182             :             {
    1183          34 :                 SmBinDiagonalNode *pSTmp = new SmBinDiagonalNode(m_aCurToken);
    1184          34 :                 pSTmp->SetAscending(eType == TWIDESLASH);
    1185          34 :                 pSNode = pSTmp;
    1186             : 
    1187          34 :                 pOper = new SmPolyLineNode(m_aCurToken);
    1188          34 :                 NextToken();
    1189             : 
    1190          34 :                 bSwitchArgs = true;
    1191          34 :                 break;
    1192             :             }
    1193             : 
    1194             :             default:
    1195         154 :                 pSNode = new SmBinHorNode(m_aCurToken);
    1196             : 
    1197         154 :                 OpSubSup();
    1198         154 :                 pOper = popOrZero(m_aNodeStack);
    1199             :         }
    1200             : 
    1201         483 :         Power();
    1202             : 
    1203         483 :         if (bSwitchArgs)
    1204             :         {
    1205             :             //! vgl siehe SmBinDiagonalNode::Arrange
    1206          34 :             pSNode->SetSubNodes(pFirst, popOrZero(m_aNodeStack), pOper);
    1207             :         }
    1208             :         else
    1209             :         {
    1210         449 :             pSNode->SetSubNodes(pFirst, pOper, popOrZero(m_aNodeStack));
    1211             :         }
    1212         483 :         m_aNodeStack.push_front(pSNode);
    1213             :     }
    1214        8122 : }
    1215             : 
    1216             : 
    1217        9646 : void SmParser::SubSup(sal_uLong nActiveGroup)
    1218             : {
    1219             :     OSL_ENSURE(nActiveGroup == TGPOWER  ||  nActiveGroup == TGLIMIT,
    1220             :                "Sm: wrong token group");
    1221             : 
    1222        9646 :     if (!TokenInGroup(nActiveGroup))
    1223             :         // already finish
    1224       18505 :         return;
    1225             : 
    1226         787 :     SmSubSupNode *pNode = new SmSubSupNode(m_aCurToken);
    1227             :     //! Of course 'm_aCurToken' is just the first sub-/supscript token.
    1228             :     //! It should be of no further interest. The positions of the
    1229             :     //! sub-/supscripts will be identified by the corresponding subnodes
    1230             :     //! index in the 'aSubNodes' array (enum value from 'SmSubSup').
    1231             : 
    1232         787 :     pNode->SetUseLimits(nActiveGroup == TGLIMIT);
    1233             : 
    1234             :     // initialize subnodes array
    1235         787 :     SmNodeArray  aSubNodes;
    1236         787 :     aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
    1237         787 :     aSubNodes[0] = popOrZero(m_aNodeStack);
    1238        5509 :     for (size_t i = 1;  i < aSubNodes.size();  i++)
    1239        4722 :         aSubNodes[i] = NULL;
    1240             : 
    1241             :     // process all sub-/supscripts
    1242         787 :     int  nIndex = 0;
    1243        2622 :     while (TokenInGroup(nActiveGroup))
    1244        1048 :     {   SmTokenType  eType (m_aCurToken.eType);
    1245             : 
    1246             :         // skip sub-/supscript token
    1247        1048 :         NextToken();
    1248             : 
    1249             :         // get sub-/supscript node on top of stack
    1250        1048 :         if (eType == TFROM  ||  eType == TTO)
    1251             :         {
    1252             :             // parse limits in old 4.0 and 5.0 style
    1253         283 :             Relation();
    1254             :         }
    1255             :         else
    1256         765 :             Term(true);
    1257             : 
    1258        1048 :         switch (eType)
    1259         218 :         {   case TRSUB :    nIndex = (int) RSUB;    break;
    1260         429 :             case TRSUP :    nIndex = (int) RSUP;    break;
    1261             :             case TFROM :
    1262         171 :             case TCSUB :    nIndex = (int) CSUB;    break;
    1263             :             case TTO :
    1264         154 :             case TCSUP :    nIndex = (int) CSUP;    break;
    1265          38 :             case TLSUB :    nIndex = (int) LSUB;    break;
    1266          38 :             case TLSUP :    nIndex = (int) LSUP;    break;
    1267             :             default :
    1268             :                 SAL_WARN( "starmath", "unknown case");
    1269             :         }
    1270        1048 :         nIndex++;
    1271             :         OSL_ENSURE(1 <= nIndex  &&  nIndex <= 1 + SUBSUP_NUM_ENTRIES,
    1272             :                    "SmParser::Power() : sub-/supscript index falsch");
    1273             : 
    1274             :         // set sub-/supscript if not already done
    1275        1048 :         if (aSubNodes[nIndex] != NULL)
    1276           0 :             Error(PE_DOUBLE_SUBSUPSCRIPT);
    1277        1048 :         aSubNodes[nIndex] = popOrZero(m_aNodeStack);
    1278             :     }
    1279             : 
    1280         787 :     pNode->SetSubNodes(aSubNodes);
    1281         787 :     m_aNodeStack.push_front(pNode);
    1282             : }
    1283             : 
    1284             : 
    1285        1103 : void SmParser::OpSubSup()
    1286             : {
    1287             :     // push operator symbol
    1288        1103 :     m_aNodeStack.push_front(new SmMathSymbolNode(m_aCurToken));
    1289             :     // skip operator token
    1290        1103 :     NextToken();
    1291             :     // get sub- supscripts if any
    1292        1103 :     if (TokenInGroup(TGPOWER))
    1293           0 :         SubSup(TGPOWER);
    1294        1103 : }
    1295             : 
    1296             : 
    1297        9480 : void SmParser::Power()
    1298             : {
    1299             :     // get body for sub- supscripts on top of stack
    1300        9480 :     Term(false);
    1301             : 
    1302        9480 :     SubSup(TGPOWER);
    1303        9480 : }
    1304             : 
    1305             : 
    1306           4 : void SmParser::Blank()
    1307             : {
    1308             :     OSL_ENSURE(TokenInGroup(TGBLANK), "Sm : wrong token");
    1309           4 :     SmBlankNode *pBlankNode = new SmBlankNode(m_aCurToken);
    1310             : 
    1311          12 :     while (TokenInGroup(TGBLANK))
    1312             :     {
    1313           4 :         pBlankNode->IncreaseBy(m_aCurToken);
    1314           4 :         NextToken();
    1315             :     }
    1316             : 
    1317             :     // Ignore trailing spaces, if corresponding option is set
    1318           8 :     if ( m_aCurToken.eType == TNEWLINE ||
    1319           4 :              (m_aCurToken.eType == TEND && SM_MOD()->GetConfig()->IsIgnoreSpacesRight()) )
    1320             :     {
    1321           0 :         pBlankNode->Clear();
    1322             :     }
    1323             : 
    1324           4 :     m_aNodeStack.push_front(pBlankNode);
    1325           4 : }
    1326             : 
    1327             : 
    1328       10245 : void SmParser::Term(bool bGroupNumberIdent)
    1329             : {
    1330       10245 :     switch (m_aCurToken.eType)
    1331             :     {
    1332             :         case TESCAPE :
    1333          30 :             Escape();
    1334          30 :             break;
    1335             : 
    1336             :         case TNOSPACE :
    1337             :         case TLGROUP :
    1338             :         {
    1339        2924 :             bool bNoSpace = m_aCurToken.eType == TNOSPACE;
    1340        2924 :             if (bNoSpace)   // push 'no space' node and continue to parse expression
    1341             :             {
    1342           0 :                 m_aNodeStack.push_front(new SmExpressionNode(m_aCurToken));
    1343           0 :                 NextToken();
    1344             :             }
    1345        2924 :             if (m_aCurToken.eType != TLGROUP)
    1346             :             {
    1347           0 :                 m_aNodeStack.pop_front();    // get rid of the 'no space' node pushed above
    1348           0 :                 Term(false);
    1349             :             }
    1350             :             else
    1351             :             {
    1352        2924 :                 NextToken();
    1353             : 
    1354             :                 // allow for empty group
    1355        2924 :                 if (m_aCurToken.eType == TRGROUP)
    1356             :                 {
    1357          36 :                     if (bNoSpace)   // get rid of the 'no space' node pushed above
    1358           0 :                         m_aNodeStack.pop_front();
    1359          36 :                     SmStructureNode *pSNode = new SmExpressionNode(m_aCurToken);
    1360          36 :                     pSNode->SetSubNodes(NULL, NULL);
    1361          36 :                     m_aNodeStack.push_front(pSNode);
    1362             : 
    1363          36 :                     NextToken();
    1364             :                 }
    1365             :                 else    // go as usual
    1366             :                 {
    1367        2888 :                     Align();
    1368        2888 :                     if (m_aCurToken.eType != TRGROUP)
    1369           1 :                         Error(PE_RGROUP_EXPECTED);
    1370             :                     else
    1371        2887 :                         NextToken();
    1372             :                 }
    1373             :             }
    1374             :         }
    1375        2924 :         break;
    1376             : 
    1377             :         case TLEFT :
    1378         448 :             Brace();
    1379         448 :             break;
    1380             : 
    1381             :         case TBLANK :
    1382             :         case TSBLANK :
    1383           4 :             Blank();
    1384           4 :             break;
    1385             : 
    1386             :         case TTEXT :
    1387         100 :             m_aNodeStack.push_front(new SmTextNode(m_aCurToken, FNT_TEXT));
    1388         100 :             NextToken();
    1389         100 :             break;
    1390             :         case TCHARACTER :
    1391         418 :             m_aNodeStack.push_front(new SmTextNode(m_aCurToken, FNT_VARIABLE));
    1392         418 :             NextToken();
    1393         418 :             break;
    1394             :         case TIDENT :
    1395             :         case TNUMBER :
    1396             :         {
    1397             :             m_aNodeStack.push_front(new SmTextNode(m_aCurToken,
    1398        4208 :                                              m_aCurToken.eType == TNUMBER ?
    1399             :                                              FNT_NUMBER :
    1400        8416 :                                              FNT_VARIABLE));
    1401        4208 :             if (!bGroupNumberIdent)
    1402             :             {
    1403        4190 :                 NextToken();
    1404             :             }
    1405             :             else
    1406             :             {
    1407             :                 // Some people want to be able to write "x_2n" for "x_{2n}"
    1408             :                 // although e.g. LaTeX or AsciiMath interpret that as "x_2 n".
    1409             :                 // The tokenizer skips whitespaces so we need some additional
    1410             :                 // work to distinguish from "x_2 n".
    1411             :                 // See https://bz.apache.org/ooo/show_bug.cgi?id=11752 and
    1412             :                 // https://bugs.libreoffice.org/show_bug.cgi?id=55853
    1413          18 :                 sal_Int32 nBufLen = m_aBufferString.getLength();
    1414          18 :                 CharClass aCC(SM_MOD()->GetSysLocale().GetLanguageTag());
    1415          18 :                 sal_Int32 nTokens = 1;
    1416             : 
    1417             :                 // We need to be careful to call NextToken() only after having
    1418             :                 // tested for a whitespace separator (otherwise it will be
    1419             :                 // skipped!)
    1420          18 :                 bool moveToNextToken = true;
    1421          54 :                 while (m_nBufferIndex < nBufLen &&
    1422          16 :                        aCC.getType(m_aBufferString, m_nBufferIndex) !=
    1423             :                        UnicodeType::SPACE_SEPARATOR)
    1424             :                 {
    1425           6 :                     NextToken();
    1426          12 :                     if (m_aCurToken.eType != TNUMBER &&
    1427           6 :                         m_aCurToken.eType != TIDENT)
    1428             :                     {
    1429             :                         // Neither a number nor an indentifier. We just moved to
    1430             :                         // the next token, so no need to do that again.
    1431           4 :                         moveToNextToken = false;
    1432           4 :                         break;
    1433             :                     }
    1434             :                     m_aNodeStack.push_front(new SmTextNode(m_aCurToken,
    1435           2 :                                                      m_aCurToken.eType ==
    1436             :                                                      TNUMBER ?
    1437             :                                                      FNT_NUMBER :
    1438           4 :                                                      FNT_VARIABLE));
    1439           2 :                     nTokens++;
    1440             :                 }
    1441          18 :                 if (moveToNextToken) NextToken();
    1442          18 :                 if (nTokens > 1)
    1443             :                 {
    1444             :                     // We have several concatenated identifiers and numbers.
    1445             :                     // Let's group them into one SmExpressionNode.
    1446           2 :                     SmNodeArray nodeArray;
    1447           2 :                     nodeArray.resize(nTokens);
    1448           8 :                     while (nTokens > 0)
    1449             :                     {
    1450           4 :                         nodeArray[nTokens-1] = popOrZero(m_aNodeStack);
    1451           4 :                         nTokens--;
    1452             :                     }
    1453           2 :                     SmExpressionNode* pNode = new SmExpressionNode(SmToken());
    1454           2 :                     pNode->SetSubNodes(nodeArray);
    1455           2 :                     m_aNodeStack.push_front(pNode);
    1456          18 :                 }
    1457             :             }
    1458        4208 :             break;
    1459             :         }
    1460             :         case TLEFTARROW :
    1461             :         case TRIGHTARROW :
    1462             :         case TUPARROW :
    1463             :         case TDOWNARROW :
    1464             :         case TCIRC :
    1465             :         case TDRARROW :
    1466             :         case TDLARROW :
    1467             :         case TDLRARROW :
    1468             :         case TEXISTS :
    1469             :         case TNOTEXISTS :
    1470             :         case TFORALL :
    1471             :         case TPARTIAL :
    1472             :         case TNABLA :
    1473             :         case TTOWARD :
    1474             :         case TDOTSAXIS :
    1475             :         case TDOTSDIAG :
    1476             :         case TDOTSDOWN :
    1477             :         case TDOTSLOW :
    1478             :         case TDOTSUP :
    1479             :         case TDOTSVERT :
    1480          53 :             m_aNodeStack.push_front(new SmMathSymbolNode(m_aCurToken));
    1481          53 :             NextToken();
    1482          53 :             break;
    1483             : 
    1484             :         case TSETN :
    1485             :         case TSETZ :
    1486             :         case TSETQ :
    1487             :         case TSETR :
    1488             :         case TSETC :
    1489             :         case THBAR :
    1490             :         case TLAMBDABAR :
    1491             :         case TBACKEPSILON :
    1492             :         case TALEPH :
    1493             :         case TIM :
    1494             :         case TRE :
    1495             :         case TWP :
    1496             :         case TEMPTYSET :
    1497             :         case TINFINITY :
    1498          26 :             m_aNodeStack.push_front(new SmMathIdentifierNode(m_aCurToken));
    1499          26 :             NextToken();
    1500          26 :             break;
    1501             : 
    1502             :         case TPLACE:
    1503         654 :             m_aNodeStack.push_front(new SmPlaceNode(m_aCurToken));
    1504         654 :             NextToken();
    1505         654 :             break;
    1506             : 
    1507             :         case TSPECIAL:
    1508         131 :             Special();
    1509         131 :             break;
    1510             : 
    1511             :         case TBINOM:
    1512          48 :             Binom();
    1513          48 :             break;
    1514             : 
    1515             :         case TSTACK:
    1516          61 :             Stack();
    1517          61 :             break;
    1518             : 
    1519             :         case TMATRIX:
    1520          24 :             Matrix();
    1521          24 :             break;
    1522             : 
    1523             :         default:
    1524        1116 :             if (TokenInGroup(TGLBRACES))
    1525          74 :             {   Brace();
    1526             :             }
    1527        1042 :             else if (TokenInGroup(TGOPER))
    1528         189 :             {   Operator();
    1529             :             }
    1530         853 :             else if (TokenInGroup(TGUNOPER))
    1531         258 :             {   UnOper();
    1532             :             }
    1533        1190 :             else if (    TokenInGroup(TGATTRIBUT)
    1534         595 :                      ||  TokenInGroup(TGFONTATTR))
    1535         411 :             {   SmStructureNodeArray  aArray;
    1536             : 
    1537             :                 bool    bIsAttr;
    1538         411 :                 sal_uInt16  n = 0;
    1539        1233 :                 while ( (bIsAttr = TokenInGroup(TGATTRIBUT))
    1540         822 :                        ||  TokenInGroup(TGFONTATTR))
    1541         411 :                 {   aArray.resize(n + 1);
    1542             : 
    1543         411 :                     if (bIsAttr)
    1544         290 :                         Attribut();
    1545             :                     else
    1546         121 :                         FontAttribut();
    1547             : 
    1548         411 :                     SmNode* pTmp = popOrZero(m_aNodeStack);
    1549             : 
    1550             :                     // check if casting in following line is ok
    1551             :                     OSL_ENSURE(pTmp && !pTmp->IsVisible(), "Sm : Ooops...");
    1552             : 
    1553         411 :                     aArray[n] = static_cast<SmStructureNode *>(pTmp);
    1554         411 :                     n++;
    1555             :                 }
    1556             : 
    1557         411 :                 Power();
    1558             : 
    1559         411 :                 SmNode *pFirstNode = popOrZero(m_aNodeStack);
    1560        1233 :                 while (n > 0)
    1561         411 :                 {   aArray[n - 1]->SetSubNodes(0, pFirstNode);
    1562         411 :                     pFirstNode = aArray[n - 1];
    1563         411 :                     n--;
    1564             :                 }
    1565         411 :                 m_aNodeStack.push_front(pFirstNode);
    1566             :             }
    1567         184 :             else if (TokenInGroup(TGFUNCTION))
    1568             :             {
    1569         159 :                 Function();
    1570             :             }
    1571             :             else
    1572          25 :                 Error(PE_UNEXPECTED_CHAR);
    1573             :     }
    1574       10245 : }
    1575             : 
    1576             : 
    1577          30 : void SmParser::Escape()
    1578             : {
    1579          30 :     NextToken();
    1580             : 
    1581          30 :     switch (m_aCurToken.eType)
    1582             :     {
    1583             :         case TLPARENT :
    1584             :         case TRPARENT :
    1585             :         case TLBRACKET :
    1586             :         case TRBRACKET :
    1587             :         case TLDBRACKET :
    1588             :         case TRDBRACKET :
    1589             :         case TLBRACE :
    1590             :         case TLGROUP :
    1591             :         case TRBRACE :
    1592             :         case TRGROUP :
    1593             :         case TLANGLE :
    1594             :         case TRANGLE :
    1595             :         case TLCEIL :
    1596             :         case TRCEIL :
    1597             :         case TLFLOOR :
    1598             :         case TRFLOOR :
    1599             :         case TLLINE :
    1600             :         case TRLINE :
    1601             :         case TLDLINE :
    1602             :         case TRDLINE :
    1603          30 :             break;
    1604             :         default:
    1605           0 :             Error(PE_UNEXPECTED_TOKEN);
    1606             :     }
    1607             : 
    1608          30 :     SmNode *pNode = new SmMathSymbolNode(m_aCurToken);
    1609          30 :     m_aNodeStack.push_front(pNode);
    1610             : 
    1611          30 :     NextToken();
    1612          30 : }
    1613             : 
    1614             : 
    1615         189 : void SmParser::Operator()
    1616             : {
    1617         189 :     if (TokenInGroup(TGOPER))
    1618         189 :     {   SmStructureNode *pSNode = new SmOperNode(m_aCurToken);
    1619             : 
    1620             :         // put operator on top of stack
    1621         189 :         Oper();
    1622             : 
    1623         189 :         if (TokenInGroup(TGLIMIT) || TokenInGroup(TGPOWER))
    1624         166 :             SubSup(m_aCurToken.nGroup);
    1625         189 :         SmNode *pOperator = popOrZero(m_aNodeStack);
    1626             : 
    1627             :         // get argument
    1628         189 :         Power();
    1629             : 
    1630         189 :         pSNode->SetSubNodes(pOperator, popOrZero(m_aNodeStack));
    1631         189 :         m_aNodeStack.push_front(pSNode);
    1632             :     }
    1633         189 : }
    1634             : 
    1635             : 
    1636         189 : void SmParser::Oper()
    1637             : {
    1638         189 :     SmTokenType  eType (m_aCurToken.eType);
    1639         189 :     SmNode      *pNode = NULL;
    1640             : 
    1641         189 :     switch (eType)
    1642             :     {
    1643             :         case TSUM :
    1644             :         case TPROD :
    1645             :         case TCOPROD :
    1646             :         case TINT :
    1647             :         case TIINT :
    1648             :         case TIIINT :
    1649             :         case TLINT :
    1650             :         case TLLINT :
    1651             :         case TLLLINT :
    1652         172 :             pNode = new SmMathSymbolNode(m_aCurToken);
    1653         172 :             break;
    1654             : 
    1655             :         case TLIM :
    1656             :         case TLIMSUP :
    1657             :         case TLIMINF :
    1658             :             {
    1659          17 :                 const sal_Char* pLim = 0;
    1660          17 :                 switch (eType)
    1661             :                 {
    1662          17 :                     case TLIM :     pLim = "lim";       break;
    1663           0 :                     case TLIMSUP :  pLim = "lim sup";   break;
    1664           0 :                     case TLIMINF :  pLim = "lim inf";   break;
    1665             :                     default:
    1666           0 :                         break;
    1667             :                 }
    1668          17 :                 if( pLim )
    1669          17 :                     m_aCurToken.aText = OUString::createFromAscii(pLim);
    1670          17 :                 pNode = new SmTextNode(m_aCurToken, FNT_TEXT);
    1671             :             }
    1672          17 :             break;
    1673             : 
    1674             :         case TOPER :
    1675           0 :             NextToken();
    1676             : 
    1677             :             OSL_ENSURE(m_aCurToken.eType == TSPECIAL, "Sm: wrong token");
    1678           0 :             pNode = new SmGlyphSpecialNode(m_aCurToken);
    1679           0 :             break;
    1680             : 
    1681             :         default :
    1682             :             assert(false && "unknown case");
    1683             :     }
    1684         189 :     m_aNodeStack.push_front(pNode);
    1685             : 
    1686         189 :     NextToken();
    1687         189 : }
    1688             : 
    1689             : 
    1690         258 : void SmParser::UnOper()
    1691             : {
    1692             :     OSL_ENSURE(TokenInGroup(TGUNOPER), "Sm: wrong token");
    1693             : 
    1694         258 :     SmToken      aNodeToken = m_aCurToken;
    1695         258 :     SmTokenType  eType      = m_aCurToken.eType;
    1696         258 :     bool         bIsPostfix = eType == TFACT;
    1697             : 
    1698             :     SmStructureNode *pSNode;
    1699         258 :     SmNode *pOper   = 0,
    1700         258 :            *pExtra  = 0,
    1701             :            *pArg;
    1702             : 
    1703         258 :     switch (eType)
    1704             :     {
    1705             :         case TABS :
    1706             :         case TSQRT :
    1707             :            /* Dynamic integrals are handled as unary operators so we can wrap
    1708             :              the symbol together with the body in a upper level node and make
    1709             :              proper graphic arrangements */
    1710             :         case TINTD:
    1711          69 :             NextToken();
    1712          69 :             break;
    1713             : 
    1714             :         case TNROOT :
    1715          17 :             NextToken();
    1716          17 :             Power();
    1717          17 :             pExtra = popOrZero(m_aNodeStack);
    1718          17 :             break;
    1719             : 
    1720             :         case TUOPER :
    1721           0 :             NextToken();
    1722             :             //Let the glyph know what it is...
    1723           0 :             m_aCurToken.eType = TUOPER;
    1724           0 :             m_aCurToken.nGroup = TGUNOPER;
    1725           0 :             GlyphSpecial();
    1726           0 :             pOper = popOrZero(m_aNodeStack);
    1727           0 :             break;
    1728             : 
    1729             :         case TPLUS :
    1730             :         case TMINUS :
    1731             :         case TPLUSMINUS :
    1732             :         case TMINUSPLUS :
    1733             :         case TNEG :
    1734             :         case TFACT :
    1735         172 :             OpSubSup();
    1736         172 :             pOper = popOrZero(m_aNodeStack);
    1737         172 :             break;
    1738             : 
    1739             :         default :
    1740           0 :             Error(PE_UNOPER_EXPECTED);
    1741             :     }
    1742             : 
    1743             :     // get argument
    1744         258 :     Power();
    1745         258 :     pArg = popOrZero(m_aNodeStack);
    1746             : 
    1747         258 :     if (eType == TABS)
    1748           4 :     {   pSNode = new SmBraceNode(aNodeToken);
    1749           4 :         pSNode->SetScaleMode(SCALE_HEIGHT);
    1750             : 
    1751             :         // build nodes for left & right lines
    1752             :         // (text, group, level of the used token are of no interrest here)
    1753             :         // we'll use row & column of the keyword for abs
    1754           4 :         aNodeToken.eType = TABS;
    1755             : 
    1756           4 :         aNodeToken.cMathChar = MS_VERTLINE;
    1757           4 :         SmNode* pLeft = new SmMathSymbolNode(aNodeToken);
    1758             : 
    1759           4 :         aNodeToken.cMathChar = MS_VERTLINE;
    1760           4 :         SmNode* pRight = new SmMathSymbolNode(aNodeToken);
    1761             : 
    1762           4 :         pSNode->SetSubNodes(pLeft, pArg, pRight);
    1763             :     }
    1764         254 :     else if (eType == TSQRT  ||  eType == TNROOT)
    1765          82 :     {  pSNode = new SmRootNode(aNodeToken);
    1766          82 :         pOper = new SmRootSymbolNode(aNodeToken);
    1767          82 :         pSNode->SetSubNodes(pExtra, pOper, pArg);
    1768             :     }
    1769         172 :     else if(eType == TINTD)
    1770           0 :     {  pSNode = new SmDynIntegralNode(aNodeToken);
    1771           0 :         pOper = new SmDynIntegralSymbolNode(aNodeToken);
    1772           0 :         pSNode->SetSubNodes(pOper, pArg);
    1773             :     }
    1774             :     else
    1775         172 :     {   pSNode = new SmUnHorNode(aNodeToken);
    1776             : 
    1777         172 :         if (bIsPostfix)
    1778           2 :             pSNode->SetSubNodes(pArg, pOper);
    1779             :         else
    1780             :             // prefix operator
    1781         170 :             pSNode->SetSubNodes(pOper, pArg);
    1782             :     }
    1783             : 
    1784         258 :     m_aNodeStack.push_front(pSNode);
    1785         258 : }
    1786             : 
    1787             : 
    1788         290 : void SmParser::Attribut()
    1789             : {
    1790             :     OSL_ENSURE(TokenInGroup(TGATTRIBUT), "Sm: wrong token group");
    1791             : 
    1792         290 :     SmStructureNode *pSNode = new SmAttributNode(m_aCurToken);
    1793             :     SmNode      *pAttr;
    1794         290 :     SmScaleMode  eScaleMode = SCALE_NONE;
    1795             : 
    1796             :     // get appropriate node for the attribute itself
    1797         290 :     switch (m_aCurToken.eType)
    1798             :     {   case TUNDERLINE :
    1799             :         case TOVERLINE :
    1800             :         case TOVERSTRIKE :
    1801          42 :             pAttr = new SmRectangleNode(m_aCurToken);
    1802          42 :             eScaleMode = SCALE_WIDTH;
    1803          42 :             break;
    1804             : 
    1805             :         case TWIDEVEC :
    1806             :         case TWIDEHAT :
    1807             :         case TWIDETILDE :
    1808         114 :             pAttr = new SmMathSymbolNode(m_aCurToken);
    1809         114 :             eScaleMode = SCALE_WIDTH;
    1810         114 :             break;
    1811             : 
    1812             :         default :
    1813         134 :             pAttr = new SmMathSymbolNode(m_aCurToken);
    1814             :     }
    1815             : 
    1816         290 :     NextToken();
    1817             : 
    1818         290 :     pSNode->SetSubNodes(pAttr, 0);
    1819         290 :     pSNode->SetScaleMode(eScaleMode);
    1820         290 :     m_aNodeStack.push_front(pSNode);
    1821         290 : }
    1822             : 
    1823             : 
    1824         121 : void SmParser::FontAttribut()
    1825             : {
    1826             :     OSL_ENSURE(TokenInGroup(TGFONTATTR), "Sm: wrong token group");
    1827             : 
    1828         121 :     switch (m_aCurToken.eType)
    1829             :     {
    1830             :         case TITALIC :
    1831             :         case TNITALIC :
    1832             :         case TBOLD :
    1833             :         case TNBOLD :
    1834             :         case TPHANTOM :
    1835          32 :             m_aNodeStack.push_front(new SmFontNode(m_aCurToken));
    1836          32 :             NextToken();
    1837          32 :             break;
    1838             : 
    1839             :         case TSIZE :
    1840          66 :             FontSize();
    1841          66 :             break;
    1842             : 
    1843             :         case TFONT :
    1844           6 :             Font();
    1845           6 :             break;
    1846             : 
    1847             :         case TCOLOR :
    1848          17 :             Color();
    1849          17 :             break;
    1850             : 
    1851             :         default :
    1852             :             SAL_WARN("starmath", "unknown case");
    1853             :     }
    1854         121 : }
    1855             : 
    1856             : 
    1857          17 : void SmParser::Color()
    1858             : {
    1859             :     OSL_ENSURE(m_aCurToken.eType == TCOLOR, "Sm : Ooops...");
    1860             : 
    1861             :     // last color rules, get that one
    1862          17 :     SmToken  aToken;
    1863          17 :     do
    1864          17 :     {   NextToken();
    1865             : 
    1866          17 :         if (TokenInGroup(TGCOLOR))
    1867          16 :         {   aToken = m_aCurToken;
    1868          16 :             NextToken();
    1869             :         }
    1870             :         else
    1871           1 :             Error(PE_COLOR_EXPECTED);
    1872          17 :     } while (m_aCurToken.eType == TCOLOR);
    1873             : 
    1874          17 :     m_aNodeStack.push_front(new SmFontNode(aToken));
    1875          17 : }
    1876             : 
    1877             : 
    1878           6 : void SmParser::Font()
    1879             : {
    1880             :     OSL_ENSURE(m_aCurToken.eType == TFONT, "Sm : Ooops...");
    1881             : 
    1882             :     // last font rules, get that one
    1883           6 :     SmToken  aToken;
    1884           6 :     do
    1885           6 :     {   NextToken();
    1886             : 
    1887           6 :         if (TokenInGroup(TGFONT))
    1888           6 :         {   aToken = m_aCurToken;
    1889           6 :             NextToken();
    1890             :         }
    1891             :         else
    1892           0 :             Error(PE_FONT_EXPECTED);
    1893           6 :     } while (m_aCurToken.eType == TFONT);
    1894             : 
    1895           6 :     m_aNodeStack.push_front(new SmFontNode(aToken));
    1896           6 : }
    1897             : 
    1898             : 
    1899             : // gets number used as arguments in Math formulas (e.g. 'size' command)
    1900             : // Format: no negative numbers, must start with a digit, no exponent notation, ...
    1901          66 : static bool lcl_IsNumber(const OUString& rText)
    1902             : {
    1903          66 :     bool bPoint = false;
    1904          66 :     const sal_Unicode* pBuffer = rText.getStr();
    1905         168 :     for(sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
    1906             :     {
    1907         102 :         const sal_Unicode cChar = *pBuffer;
    1908         102 :         if(cChar == '.')
    1909             :         {
    1910           0 :             if(bPoint)
    1911           0 :                 return false;
    1912             :             else
    1913           0 :                 bPoint = true;
    1914             :         }
    1915         102 :         else if ( !rtl::isAsciiDigit( cChar ) )
    1916           0 :             return false;
    1917             :     }
    1918          66 :     return true;
    1919             : }
    1920             : 
    1921          66 : void SmParser::FontSize()
    1922             : {
    1923             :     OSL_ENSURE(m_aCurToken.eType == TSIZE, "Sm : Ooops...");
    1924             : 
    1925             :     FontSizeType   Type;
    1926          66 :     SmFontNode *pFontNode = new SmFontNode(m_aCurToken);
    1927             : 
    1928          66 :     NextToken();
    1929             : 
    1930          66 :     switch (m_aCurToken.eType)
    1931             :     {
    1932          66 :         case TNUMBER:   Type = FontSizeType::ABSOLUT;  break;
    1933           0 :         case TPLUS:     Type = FontSizeType::PLUS;     break;
    1934           0 :         case TMINUS:    Type = FontSizeType::MINUS;    break;
    1935           0 :         case TMULTIPLY: Type = FontSizeType::MULTIPLY; break;
    1936           0 :         case TDIVIDEBY: Type = FontSizeType::DIVIDE;   break;
    1937             : 
    1938             :         default:
    1939           0 :             delete pFontNode;
    1940           0 :             Error(PE_SIZE_EXPECTED);
    1941           0 :             return;
    1942             :     }
    1943             : 
    1944          66 :     if (Type != FontSizeType::ABSOLUT)
    1945             :     {
    1946           0 :         NextToken();
    1947           0 :         if (m_aCurToken.eType != TNUMBER)
    1948             :         {
    1949           0 :             delete pFontNode;
    1950           0 :             Error(PE_SIZE_EXPECTED);
    1951           0 :             return;
    1952             :         }
    1953             :     }
    1954             : 
    1955             :     // get number argument
    1956          66 :     Fraction  aValue( 1L );
    1957          66 :     if (lcl_IsNumber( m_aCurToken.aText ))
    1958             :     {
    1959          66 :         double fTmp = OUString(m_aCurToken.aText).toDouble();
    1960          66 :         if (fTmp != 0.0)
    1961             :         {
    1962          66 :             aValue = fTmp;
    1963             : 
    1964             :             //!! keep the numerator and denominator from being to large
    1965             :             //!! otherwise ongoing multiplications may result in overflows
    1966             :             //!! (for example in SmNode::SetFontSize the font size calculated
    1967             :             //!! may become 0 because of this!!! Happens e.g. for ftmp = 2.9 with Linux
    1968             :             //!! or ftmp = 1.11111111111111111... (11/9) on every platform.)
    1969          66 :             if (aValue.GetDenominator() > 1000)
    1970             :             {
    1971           0 :                 long nNum   = aValue.GetNumerator();
    1972           0 :                 long nDenom = aValue.GetDenominator();
    1973           0 :                 while (nDenom > 1000)
    1974             :                 {
    1975           0 :                     nNum    /= 10;
    1976           0 :                     nDenom  /= 10;
    1977             :                 }
    1978           0 :                 aValue = Fraction( nNum, nDenom );
    1979             :             }
    1980             :         }
    1981             :     }
    1982             : 
    1983          66 :     NextToken();
    1984             : 
    1985          66 :     pFontNode->SetSizeParameter(aValue, Type);
    1986          66 :     m_aNodeStack.push_front(pFontNode);
    1987             : }
    1988             : 
    1989             : 
    1990         522 : void SmParser::Brace()
    1991             : {
    1992             :     OSL_ENSURE(m_aCurToken.eType == TLEFT  ||  TokenInGroup(TGLBRACES),
    1993             :         "Sm: kein Klammer Ausdruck");
    1994             : 
    1995         522 :     SmStructureNode *pSNode  = new SmBraceNode(m_aCurToken);
    1996         522 :     SmNode *pBody   = 0,
    1997         522 :            *pLeft   = 0,
    1998         522 :            *pRight  = 0;
    1999         522 :     SmScaleMode   eScaleMode = SCALE_NONE;
    2000         522 :     SmParseError  eError     = PE_NONE;
    2001             : 
    2002         522 :     if (m_aCurToken.eType == TLEFT)
    2003         448 :     {   NextToken();
    2004             : 
    2005         448 :         eScaleMode = SCALE_HEIGHT;
    2006             : 
    2007             :         // check for left bracket
    2008         448 :         if (TokenInGroup(TGLBRACES) || TokenInGroup(TGRBRACES))
    2009             :         {
    2010         448 :             pLeft = new SmMathSymbolNode(m_aCurToken);
    2011             : 
    2012         448 :             NextToken();
    2013         448 :             Bracebody(true);
    2014         448 :             pBody = popOrZero(m_aNodeStack);
    2015             : 
    2016         448 :             if (m_aCurToken.eType == TRIGHT)
    2017         448 :             {   NextToken();
    2018             : 
    2019             :                 // check for right bracket
    2020         448 :                 if (TokenInGroup(TGLBRACES) || TokenInGroup(TGRBRACES))
    2021             :                 {
    2022         448 :                     pRight = new SmMathSymbolNode(m_aCurToken);
    2023         448 :                     NextToken();
    2024             :                 }
    2025             :                 else
    2026           0 :                     eError = PE_RBRACE_EXPECTED;
    2027             :             }
    2028             :             else
    2029           0 :                 eError = PE_RIGHT_EXPECTED;
    2030             :         }
    2031             :         else
    2032           0 :             eError = PE_LBRACE_EXPECTED;
    2033             :     }
    2034             :     else
    2035             :     {
    2036          74 :         if (TokenInGroup(TGLBRACES))
    2037             :         {
    2038          74 :             pLeft = new SmMathSymbolNode(m_aCurToken);
    2039             : 
    2040          74 :             NextToken();
    2041          74 :             Bracebody(false);
    2042          74 :             pBody = popOrZero(m_aNodeStack);
    2043             : 
    2044          74 :             SmTokenType  eExpectedType = TUNKNOWN;
    2045          74 :             switch (pLeft->GetToken().eType)
    2046          54 :             {   case TLPARENT :     eExpectedType = TRPARENT;   break;
    2047           8 :                 case TLBRACKET :    eExpectedType = TRBRACKET;  break;
    2048           2 :                 case TLBRACE :      eExpectedType = TRBRACE;    break;
    2049           2 :                 case TLDBRACKET :   eExpectedType = TRDBRACKET; break;
    2050           2 :                 case TLLINE :       eExpectedType = TRLINE;     break;
    2051           2 :                 case TLDLINE :      eExpectedType = TRDLINE;    break;
    2052           4 :                 case TLANGLE :      eExpectedType = TRANGLE;    break;
    2053           0 :                 case TLFLOOR :      eExpectedType = TRFLOOR;    break;
    2054           0 :                 case TLCEIL :       eExpectedType = TRCEIL;     break;
    2055             :                 default :
    2056             :                     SAL_WARN("starmath", "unknown case");
    2057             :             }
    2058             : 
    2059          74 :             if (m_aCurToken.eType == eExpectedType)
    2060             :             {
    2061          74 :                 pRight = new SmMathSymbolNode(m_aCurToken);
    2062          74 :                 NextToken();
    2063             :             }
    2064             :             else
    2065           0 :                 eError = PE_PARENT_MISMATCH;
    2066             :         }
    2067             :         else
    2068           0 :             eError = PE_LBRACE_EXPECTED;
    2069             :     }
    2070             : 
    2071         522 :     if (eError == PE_NONE)
    2072             :     {   OSL_ENSURE(pLeft,  "Sm: NULL pointer");
    2073             :         OSL_ENSURE(pRight, "Sm: NULL pointer");
    2074         522 :         pSNode->SetSubNodes(pLeft, pBody, pRight);
    2075         522 :         pSNode->SetScaleMode(eScaleMode);
    2076         522 :         m_aNodeStack.push_front(pSNode);
    2077             :     }
    2078             :     else
    2079           0 :     {   delete pSNode;
    2080           0 :         delete pBody;
    2081           0 :         delete pLeft;
    2082           0 :         delete pRight;
    2083             : 
    2084           0 :         Error(eError);
    2085             :     }
    2086         522 : }
    2087             : 
    2088             : 
    2089         522 : void SmParser::Bracebody(bool bIsLeftRight)
    2090             : {
    2091         522 :     SmStructureNode *pBody = new SmBracebodyNode(m_aCurToken);
    2092         522 :     SmNodeArray      aNodes;
    2093         522 :     sal_uInt16           nNum = 0;
    2094             : 
    2095             :     // get body if any
    2096         522 :     if (bIsLeftRight)
    2097             :     {
    2098         560 :         do
    2099             :         {
    2100         560 :             if (m_aCurToken.eType == TMLINE)
    2101             :             {
    2102          56 :                 m_aNodeStack.push_front(new SmMathSymbolNode(m_aCurToken));
    2103          56 :                 NextToken();
    2104          56 :                 nNum++;
    2105             :             }
    2106         504 :             else if (m_aCurToken.eType != TRIGHT)
    2107         483 :             {   Align();
    2108         483 :                 nNum++;
    2109             : 
    2110         483 :                 if (m_aCurToken.eType != TMLINE  &&  m_aCurToken.eType != TRIGHT)
    2111           0 :                     Error(PE_RIGHT_EXPECTED);
    2112             :             }
    2113        1120 :         } while (m_aCurToken.eType != TEND  &&  m_aCurToken.eType != TRIGHT);
    2114             :     }
    2115             :     else
    2116             :     {
    2117          78 :         do
    2118             :         {
    2119          78 :             if (m_aCurToken.eType == TMLINE)
    2120             :             {
    2121           2 :                 m_aNodeStack.push_front(new SmMathSymbolNode(m_aCurToken));
    2122           2 :                 NextToken();
    2123           2 :                 nNum++;
    2124             :             }
    2125          76 :             else if (!TokenInGroup(TGRBRACES))
    2126          64 :             {   Align();
    2127          64 :                 nNum++;
    2128             : 
    2129          64 :                 if (m_aCurToken.eType != TMLINE  &&  !TokenInGroup(TGRBRACES))
    2130           0 :                     Error(PE_RBRACE_EXPECTED);
    2131             :             }
    2132          78 :         } while (m_aCurToken.eType != TEND  &&  !TokenInGroup(TGRBRACES));
    2133             :     }
    2134             : 
    2135             :     // build argument vector in parsing order
    2136         522 :     aNodes.resize(nNum);
    2137        1127 :     for (sal_uInt16 i = 0;  i < nNum;  i++)
    2138             :     {
    2139         605 :         aNodes[nNum - 1 - i] = popOrZero(m_aNodeStack);
    2140             :     }
    2141             : 
    2142         522 :     pBody->SetSubNodes(aNodes);
    2143         522 :     pBody->SetScaleMode(bIsLeftRight ? SCALE_HEIGHT : SCALE_NONE);
    2144         522 :     m_aNodeStack.push_front(pBody);
    2145         522 : }
    2146             : 
    2147             : 
    2148         159 : void SmParser::Function()
    2149             : {
    2150         159 :     switch (m_aCurToken.eType)
    2151             :     {
    2152             :         case TFUNC:
    2153           1 :             NextToken();    // skip "FUNC"-statement
    2154             :             // fall through
    2155             : 
    2156             :         case TSIN :
    2157             :         case TCOS :
    2158             :         case TTAN :
    2159             :         case TCOT :
    2160             :         case TASIN :
    2161             :         case TACOS :
    2162             :         case TATAN :
    2163             :         case TACOT :
    2164             :         case TSINH :
    2165             :         case TCOSH :
    2166             :         case TTANH :
    2167             :         case TCOTH :
    2168             :         case TASINH :
    2169             :         case TACOSH :
    2170             :         case TATANH :
    2171             :         case TACOTH :
    2172             :         case TLN :
    2173             :         case TLOG :
    2174             :         case TEXP :
    2175         159 :             m_aNodeStack.push_front(new SmTextNode(m_aCurToken, FNT_FUNCTION));
    2176         159 :             NextToken();
    2177         159 :             break;
    2178             : 
    2179             :         default:
    2180           0 :             Error(PE_FUNC_EXPECTED);
    2181             :     }
    2182         159 : }
    2183             : 
    2184             : 
    2185          48 : void SmParser::Binom()
    2186             : {
    2187          48 :     SmNodeArray  ExpressionArray;
    2188          48 :     SmStructureNode *pSNode = new SmTableNode(m_aCurToken);
    2189             : 
    2190          48 :     NextToken();
    2191             : 
    2192          48 :     Sum();
    2193          48 :     Sum();
    2194             : 
    2195          48 :     ExpressionArray.resize(2);
    2196             : 
    2197         144 :     for (int i = 0;  i < 2;  i++)
    2198             :     {
    2199          96 :         ExpressionArray[2 - (i + 1)] = popOrZero(m_aNodeStack);
    2200             :     }
    2201             : 
    2202          48 :     pSNode->SetSubNodes(ExpressionArray);
    2203          48 :     m_aNodeStack.push_front(pSNode);
    2204          48 : }
    2205             : 
    2206             : 
    2207          61 : void SmParser::Stack()
    2208             : {
    2209          61 :     SmNodeArray  ExpressionArray;
    2210          61 :     NextToken();
    2211          61 :     if (m_aCurToken.eType == TLGROUP)
    2212             :     {
    2213          61 :         sal_uInt16 n = 0;
    2214             : 
    2215         132 :         do
    2216             :         {
    2217         132 :             NextToken();
    2218         132 :             Align();
    2219         132 :             n++;
    2220             :         }
    2221         132 :         while (m_aCurToken.eType == TPOUND);
    2222             : 
    2223          61 :         ExpressionArray.resize(n);
    2224             : 
    2225         193 :         for (sal_uInt16 i = 0; i < n; i++)
    2226             :         {
    2227         132 :             ExpressionArray[n - (i + 1)] = popOrZero(m_aNodeStack);
    2228             :         }
    2229             : 
    2230          61 :         if (m_aCurToken.eType != TRGROUP)
    2231           0 :             Error(PE_RGROUP_EXPECTED);
    2232             : 
    2233          61 :         NextToken();
    2234             : 
    2235             :         //We need to let the table node know it context
    2236             :         //it's used in SmNodeToTextVisitor
    2237          61 :         SmToken aTok = m_aCurToken;
    2238          61 :         aTok.eType = TSTACK;
    2239          61 :         SmStructureNode *pSNode = new SmTableNode(aTok);
    2240          61 :         pSNode->SetSubNodes(ExpressionArray);
    2241          61 :         m_aNodeStack.push_front(pSNode);
    2242             :     }
    2243             :     else
    2244           0 :         Error(PE_LGROUP_EXPECTED);
    2245          61 : }
    2246             : 
    2247             : 
    2248          24 : void SmParser::Matrix()
    2249             : {
    2250          24 :     SmNodeArray  ExpressionArray;
    2251             : 
    2252          24 :     NextToken();
    2253          24 :     if (m_aCurToken.eType == TLGROUP)
    2254             :     {
    2255          24 :         sal_uInt16 c = 0;
    2256             : 
    2257          48 :         do
    2258             :         {
    2259          48 :             NextToken();
    2260          48 :             Align();
    2261          48 :             c++;
    2262             :         }
    2263          48 :         while (m_aCurToken.eType == TPOUND);
    2264             : 
    2265          24 :         sal_uInt16 r = 1;
    2266             : 
    2267          74 :         while (m_aCurToken.eType == TDPOUND)
    2268             :         {
    2269          26 :             NextToken();
    2270          76 :             for (sal_uInt16 i = 0; i < c; i++)
    2271             :             {
    2272          50 :                 Align();
    2273          50 :                 if (i < (c - 1))
    2274             :                 {
    2275          24 :                     if (m_aCurToken.eType == TPOUND)
    2276             :                     {
    2277          24 :                         NextToken();
    2278             :                     }
    2279             :                     else
    2280           0 :                         Error(PE_POUND_EXPECTED);
    2281             :                 }
    2282             :             }
    2283             : 
    2284          26 :             r++;
    2285             :         }
    2286             : 
    2287          24 :         size_t nRC = static_cast<size_t>(r) * c;
    2288             : 
    2289          24 :         ExpressionArray.resize(nRC);
    2290             : 
    2291         122 :         for (size_t i = 0; i < (nRC); ++i)
    2292             :         {
    2293          98 :             ExpressionArray[(nRC) - (i + 1)] = popOrZero(m_aNodeStack);
    2294             :         }
    2295             : 
    2296          24 :         if (m_aCurToken.eType != TRGROUP)
    2297           0 :             Error(PE_RGROUP_EXPECTED);
    2298             : 
    2299          24 :         NextToken();
    2300             : 
    2301          24 :         SmMatrixNode *pMNode = new SmMatrixNode(m_aCurToken);
    2302          24 :         pMNode->SetSubNodes(ExpressionArray);
    2303          24 :         pMNode->SetRowCol(r, c);
    2304          24 :         m_aNodeStack.push_front(pMNode);
    2305             :     }
    2306             :     else
    2307           0 :         Error(PE_LGROUP_EXPECTED);
    2308          24 : }
    2309             : 
    2310             : 
    2311         131 : void SmParser::Special()
    2312             : {
    2313         131 :     bool bReplace = false;
    2314         131 :     OUString &rName = m_aCurToken.aText;
    2315         131 :     OUString aNewName;
    2316             : 
    2317             :     // conversion of symbol names for 6.0 (XML) file format
    2318             :     // (name change on import / export.
    2319             :     // UI uses localized names XML file format does not.)
    2320         131 :     if( rName.startsWith("%") )
    2321             :     {
    2322         131 :         if (IsImportSymbolNames())
    2323             :         {
    2324           0 :             aNewName = SmLocalizedSymbolData::GetUiSymbolName(rName.copy(1));
    2325           0 :             bReplace = true;
    2326             :         }
    2327         131 :         else if (IsExportSymbolNames())
    2328             :         {
    2329           0 :             aNewName = SmLocalizedSymbolData::GetExportSymbolName(rName.copy(1));
    2330           0 :             bReplace = true;
    2331             :         }
    2332             :     }
    2333         131 :     if (!aNewName.isEmpty())
    2334           0 :         aNewName = "%" + aNewName;
    2335             : 
    2336             : 
    2337         131 :     if (bReplace && !aNewName.isEmpty() && rName != aNewName)
    2338             :     {
    2339           0 :         Replace(GetTokenIndex(), rName.getLength(), aNewName);
    2340           0 :         rName = aNewName;
    2341             :     }
    2342             : 
    2343             :     // add symbol name to list of used symbols
    2344         262 :     const OUString aSymbolName(m_aCurToken.aText.copy(1));
    2345         131 :     if (!aSymbolName.isEmpty())
    2346         131 :         AddToUsedSymbols( aSymbolName );
    2347             : 
    2348         131 :     m_aNodeStack.push_front(new SmSpecialNode(m_aCurToken));
    2349         262 :     NextToken();
    2350         131 : }
    2351             : 
    2352             : 
    2353           0 : void SmParser::GlyphSpecial()
    2354             : {
    2355           0 :     m_aNodeStack.push_front(new SmGlyphSpecialNode(m_aCurToken));
    2356           0 :     NextToken();
    2357           0 : }
    2358             : 
    2359             : 
    2360          27 : void SmParser::Error(SmParseError eError)
    2361             : {
    2362          27 :     SmStructureNode *pSNode = new SmExpressionNode(m_aCurToken);
    2363          27 :     SmErrorNode     *pErr   = new SmErrorNode(eError, m_aCurToken);
    2364          27 :     pSNode->SetSubNodes(pErr, 0);
    2365             : 
    2366             :     //! put a structure node on the stack (instead of the error node itself)
    2367             :     //! because sometimes such a node is expected in order to attach some
    2368             :     //! subnodes
    2369          27 :     m_aNodeStack.push_front(pSNode);
    2370             : 
    2371          27 :     AddError(eError, pSNode);
    2372             : 
    2373          27 :     NextToken();
    2374          27 : }
    2375             : 
    2376             : 
    2377             : // end grammar
    2378             : 
    2379             : 
    2380        1219 : SmParser::SmParser()
    2381             :     : m_nCurError( 0 )
    2382             :     , m_nBufferIndex( 0 )
    2383             :     , m_nTokenIndex( 0 )
    2384             :     , m_Row( 0 )
    2385             :     , m_nColOff( 0 )
    2386        1219 :     , m_aDotLoc( LanguageTag::convertToLocale( LANGUAGE_ENGLISH_US ) )
    2387             : {
    2388        1219 :     bImportSymNames = m_bExportSymNames = false;
    2389        1219 :     m_nLang = Application::GetSettings().GetUILanguageTag().getLanguageType();
    2390        1219 : }
    2391             : 
    2392         910 : SmNode *SmParser::Parse(const OUString &rBuffer)
    2393             : {
    2394         910 :     ClearUsedSymbols();
    2395             : 
    2396         910 :     m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
    2397         910 :     m_nBufferIndex  = 0;
    2398         910 :     m_nTokenIndex   = 0;
    2399         910 :     m_Row           = 1;
    2400         910 :     m_nColOff       = 0;
    2401         910 :     m_nCurError     = -1;
    2402             : 
    2403         910 :     m_aErrDescList.clear();
    2404             : 
    2405         910 :     m_aNodeStack.clear();
    2406             : 
    2407         910 :     SetLanguage( Application::GetSettings().GetUILanguageTag().getLanguageType() );
    2408         910 :     NextToken();
    2409         910 :     Table();
    2410             : 
    2411         910 :     SmNode* result = popOrZero(m_aNodeStack);
    2412         910 :     return result;
    2413             : }
    2414             : 
    2415         866 : SmNode *SmParser::ParseExpression(const OUString &rBuffer)
    2416             : {
    2417         866 :     m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
    2418         866 :     m_nBufferIndex  = 0;
    2419         866 :     m_nTokenIndex   = 0;
    2420         866 :     m_Row           = 1;
    2421         866 :     m_nColOff       = 0;
    2422         866 :     m_nCurError     = -1;
    2423             : 
    2424         866 :     m_aErrDescList.clear();
    2425             : 
    2426         866 :     m_aNodeStack.clear();
    2427             : 
    2428         866 :     SetLanguage( Application::GetSettings().GetUILanguageTag().getLanguageType() );
    2429         866 :     NextToken();
    2430         866 :     Expression();
    2431             : 
    2432         866 :     SmNode* result = popOrZero(m_aNodeStack);
    2433         866 :     return result;
    2434             : }
    2435             : 
    2436             : 
    2437          27 : size_t SmParser::AddError(SmParseError Type, SmNode *pNode)
    2438             : {
    2439          27 :     SmErrorDesc *pErrDesc = new SmErrorDesc;
    2440             : 
    2441          27 :     pErrDesc->Type  = Type;
    2442          27 :     pErrDesc->pNode = pNode;
    2443          27 :     pErrDesc->Text  = SM_RESSTR(RID_ERR_IDENT);
    2444             : 
    2445             :     sal_uInt16  nRID;
    2446          27 :     switch (Type)
    2447             :     {
    2448          25 :         case PE_UNEXPECTED_CHAR:     nRID = RID_ERR_UNEXPECTEDCHARACTER;    break;
    2449           0 :         case PE_LGROUP_EXPECTED:     nRID = RID_ERR_LGROUPEXPECTED;         break;
    2450           1 :         case PE_RGROUP_EXPECTED:     nRID = RID_ERR_RGROUPEXPECTED;         break;
    2451           0 :         case PE_LBRACE_EXPECTED:     nRID = RID_ERR_LBRACEEXPECTED;         break;
    2452           0 :         case PE_RBRACE_EXPECTED:     nRID = RID_ERR_RBRACEEXPECTED;         break;
    2453           0 :         case PE_FUNC_EXPECTED:       nRID = RID_ERR_FUNCEXPECTED;           break;
    2454           0 :         case PE_UNOPER_EXPECTED:     nRID = RID_ERR_UNOPEREXPECTED;         break;
    2455           0 :         case PE_BINOPER_EXPECTED:    nRID = RID_ERR_BINOPEREXPECTED;        break;
    2456           0 :         case PE_SYMBOL_EXPECTED:     nRID = RID_ERR_SYMBOLEXPECTED;         break;
    2457           0 :         case PE_IDENTIFIER_EXPECTED: nRID = RID_ERR_IDENTEXPECTED;          break;
    2458           0 :         case PE_POUND_EXPECTED:      nRID = RID_ERR_POUNDEXPECTED;          break;
    2459           1 :         case PE_COLOR_EXPECTED:      nRID = RID_ERR_COLOREXPECTED;          break;
    2460           0 :         case PE_RIGHT_EXPECTED:      nRID = RID_ERR_RIGHTEXPECTED;          break;
    2461             : 
    2462             :         default:
    2463           0 :             nRID = RID_ERR_UNKNOWN;
    2464             :     }
    2465          27 :     pErrDesc->Text += SM_RESSTR(nRID);
    2466             : 
    2467          27 :     m_aErrDescList.push_back( pErrDesc );
    2468             : 
    2469          27 :     return m_aErrDescList.size()-1;
    2470             : }
    2471             : 
    2472             : 
    2473           1 : const SmErrorDesc *SmParser::NextError()
    2474             : {
    2475           1 :     if ( !m_aErrDescList.empty() )
    2476           1 :         if (m_nCurError > 0) return &m_aErrDescList[ --m_nCurError ];
    2477             :         else
    2478             :         {
    2479           1 :             m_nCurError = 0;
    2480           1 :             return &m_aErrDescList[ m_nCurError ];
    2481             :         }
    2482           0 :     else return NULL;
    2483             : }
    2484             : 
    2485             : 
    2486           3 : const SmErrorDesc *SmParser::PrevError()
    2487             : {
    2488           3 :     if ( !m_aErrDescList.empty() )
    2489           3 :         if (m_nCurError < (int) (m_aErrDescList.size() - 1)) return &m_aErrDescList[ ++m_nCurError ];
    2490             :         else
    2491             :         {
    2492           1 :             m_nCurError = (int) (m_aErrDescList.size() - 1);
    2493           1 :             return &m_aErrDescList[ m_nCurError ];
    2494             :         }
    2495           0 :     else return NULL;
    2496             : }
    2497             : 
    2498             : 
    2499           0 : const SmErrorDesc *SmParser::GetError(size_t i)
    2500             : {
    2501           0 :     if ( i < m_aErrDescList.size() )
    2502           0 :         return &m_aErrDescList[ i ];
    2503             : 
    2504           0 :     if ( (size_t)m_nCurError < m_aErrDescList.size() )
    2505           0 :         return &m_aErrDescList[ m_nCurError ];
    2506             : 
    2507           0 :     return NULL;
    2508          42 : }
    2509             : 
    2510             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11