9ec9ace3f0
Change-Id: I3569d8dd41bf1c2db6a1fd379ca5596342922e45 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140786 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
2773 lines
94 KiB
C++
2773 lines
94 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <com/sun/star/i18n/UnicodeType.hpp>
|
|
#include <com/sun/star/i18n/KParseTokens.hpp>
|
|
#include <com/sun/star/i18n/KParseType.hpp>
|
|
#include <i18nlangtag/lang.h>
|
|
#include <tools/lineend.hxx>
|
|
#include <unotools/configmgr.hxx>
|
|
#include <unotools/syslocale.hxx>
|
|
#include <osl/diagnose.h>
|
|
#include <rtl/character.hxx>
|
|
#include <parse5.hxx>
|
|
#include <strings.hrc>
|
|
#include <smmod.hxx>
|
|
#include <cfgitem.hxx>
|
|
#include <starmathdatabase.hxx>
|
|
|
|
#include <stack>
|
|
|
|
using namespace ::com::sun::star::i18n;
|
|
|
|
//Definition of math keywords
|
|
const SmTokenTableEntry aTokenTable[]
|
|
= { { u"abs", TABS, '\0', TG::UnOper, 13 },
|
|
{ u"acute", TACUTE, MS_ACUTE, TG::Attribute, 5 },
|
|
{ u"aleph", TALEPH, MS_ALEPH, TG::Standalone, 5 },
|
|
{ u"alignb", TALIGNC, '\0', TG::Align, 0 },
|
|
{ u"alignc", TALIGNC, '\0', TG::Align, 0 },
|
|
{ u"alignl", TALIGNL, '\0', TG::Align, 0 },
|
|
{ u"alignm", TALIGNC, '\0', TG::Align, 0 },
|
|
{ u"alignr", TALIGNR, '\0', TG::Align, 0 },
|
|
{ u"alignt", TALIGNC, '\0', TG::Align, 0 },
|
|
{ u"and", TAND, MS_AND, TG::Product, 0 },
|
|
{ u"approx", TAPPROX, MS_APPROX, TG::Relation, 0 },
|
|
{ u"arccos", TACOS, '\0', TG::Function, 5 },
|
|
{ u"arccot", TACOT, '\0', TG::Function, 5 },
|
|
{ u"arcosh", TACOSH, '\0', TG::Function, 5 },
|
|
{ u"arcoth", TACOTH, '\0', TG::Function, 5 },
|
|
{ u"arcsin", TASIN, '\0', TG::Function, 5 },
|
|
{ u"arctan", TATAN, '\0', TG::Function, 5 },
|
|
{ u"arsinh", TASINH, '\0', TG::Function, 5 },
|
|
{ u"artanh", TATANH, '\0', TG::Function, 5 },
|
|
{ u"backepsilon", TBACKEPSILON, MS_BACKEPSILON, TG::Standalone, 5 },
|
|
{ u"bar", TBAR, MS_BAR, TG::Attribute, 5 },
|
|
{ u"binom", TBINOM, '\0', TG::NONE, 5 },
|
|
{ u"bold", TBOLD, '\0', TG::FontAttr, 5 },
|
|
{ u"boper", TBOPER, '\0', TG::Product, 0 },
|
|
{ u"breve", TBREVE, MS_BREVE, TG::Attribute, 5 },
|
|
{ u"bslash", TBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
|
|
{ u"cdot", TCDOT, MS_CDOT, TG::Product, 0 },
|
|
{ u"check", TCHECK, MS_CHECK, TG::Attribute, 5 },
|
|
{ u"circ", TCIRC, MS_CIRC, TG::Standalone, 5 },
|
|
{ u"circle", TCIRCLE, MS_CIRCLE, TG::Attribute, 5 },
|
|
{ u"color", TCOLOR, '\0', TG::FontAttr, 5 },
|
|
{ u"coprod", TCOPROD, MS_COPROD, TG::Oper, 5 },
|
|
{ u"cos", TCOS, '\0', TG::Function, 5 },
|
|
{ u"cosh", TCOSH, '\0', TG::Function, 5 },
|
|
{ u"cot", TCOT, '\0', TG::Function, 5 },
|
|
{ u"coth", TCOTH, '\0', TG::Function, 5 },
|
|
{ u"csub", TCSUB, '\0', TG::Power, 0 },
|
|
{ u"csup", TCSUP, '\0', TG::Power, 0 },
|
|
{ u"dddot", TDDDOT, MS_DDDOT, TG::Attribute, 5 },
|
|
{ u"ddot", TDDOT, MS_DDOT, TG::Attribute, 5 },
|
|
{ u"def", TDEF, MS_DEF, TG::Relation, 0 },
|
|
{ u"div", TDIV, MS_DIV, TG::Product, 0 },
|
|
{ u"divides", TDIVIDES, MS_LINE, TG::Relation, 0 },
|
|
{ u"dlarrow", TDLARROW, MS_DLARROW, TG::Standalone, 5 },
|
|
{ u"dlrarrow", TDLRARROW, MS_DLRARROW, TG::Standalone, 5 },
|
|
{ u"dot", TDOT, MS_DOT, TG::Attribute, 5 },
|
|
{ u"dotsaxis", TDOTSAXIS, MS_DOTSAXIS, TG::Standalone, 5 }, // 5 to continue expression
|
|
{ u"dotsdiag", TDOTSDIAG, MS_DOTSUP, TG::Standalone, 5 },
|
|
{ u"dotsdown", TDOTSDOWN, MS_DOTSDOWN, TG::Standalone, 5 },
|
|
{ u"dotslow", TDOTSLOW, MS_DOTSLOW, TG::Standalone, 5 },
|
|
{ u"dotsup", TDOTSUP, MS_DOTSUP, TG::Standalone, 5 },
|
|
{ u"dotsvert", TDOTSVERT, MS_DOTSVERT, TG::Standalone, 5 },
|
|
{ u"downarrow", TDOWNARROW, MS_DOWNARROW, TG::Standalone, 5 },
|
|
{ u"drarrow", TDRARROW, MS_DRARROW, TG::Standalone, 5 },
|
|
{ u"emptyset", TEMPTYSET, MS_EMPTYSET, TG::Standalone, 5 },
|
|
{ u"equiv", TEQUIV, MS_EQUIV, TG::Relation, 0 },
|
|
{ u"evaluate", TEVALUATE, '\0', TG::NONE, 0 },
|
|
{ u"exists", TEXISTS, MS_EXISTS, TG::Standalone, 5 },
|
|
{ u"exp", TEXP, '\0', TG::Function, 5 },
|
|
{ u"fact", TFACT, MS_FACT, TG::UnOper, 5 },
|
|
{ u"fixed", TFIXED, '\0', TG::Font, 0 },
|
|
{ u"font", TFONT, '\0', TG::FontAttr, 5 },
|
|
{ u"forall", TFORALL, MS_FORALL, TG::Standalone, 5 },
|
|
{ u"fourier", TFOURIER, MS_FOURIER, TG::Standalone, 5 },
|
|
{ u"frac", TFRAC, '\0', TG::NONE, 5 },
|
|
{ u"from", TFROM, '\0', TG::Limit, 0 },
|
|
{ u"func", TFUNC, '\0', TG::Function, 5 },
|
|
{ u"ge", TGE, MS_GE, TG::Relation, 0 },
|
|
{ u"geslant", TGESLANT, MS_GESLANT, TG::Relation, 0 },
|
|
{ u"gg", TGG, MS_GG, TG::Relation, 0 },
|
|
{ u"grave", TGRAVE, MS_GRAVE, TG::Attribute, 5 },
|
|
{ u"gt", TGT, MS_GT, TG::Relation, 0 },
|
|
{ u"harpoon", THARPOON, MS_HARPOON, TG::Attribute, 5 },
|
|
{ u"hat", THAT, MS_HAT, TG::Attribute, 5 },
|
|
{ u"hbar", THBAR, MS_HBAR, TG::Standalone, 5 },
|
|
{ u"hex", THEX, '\0', TG::NONE, 5 },
|
|
{ u"iiint", TIIINT, MS_IIINT, TG::Oper, 5 },
|
|
{ u"iint", TIINT, MS_IINT, TG::Oper, 5 },
|
|
{ u"im", TIM, MS_IM, TG::Standalone, 5 },
|
|
{ u"in", TIN, MS_IN, TG::Relation, 0 },
|
|
{ u"infinity", TINFINITY, MS_INFINITY, TG::Standalone, 5 },
|
|
{ u"infty", TINFINITY, MS_INFINITY, TG::Standalone, 5 },
|
|
{ u"int", TINT, MS_INT, TG::Oper, 5 },
|
|
{ u"intd", TINTD, MS_INT, TG::Oper, 5 },
|
|
{ u"intersection", TINTERSECT, MS_INTERSECT, TG::Product, 0 },
|
|
{ u"it", TIT, '\0', TG::Product, 0 },
|
|
{ u"ital", TITALIC, '\0', TG::FontAttr, 5 },
|
|
{ u"italic", TITALIC, '\0', TG::FontAttr, 5 },
|
|
{ u"lambdabar", TLAMBDABAR, MS_LAMBDABAR, TG::Standalone, 5 },
|
|
{ u"langle", TLANGLE, MS_LMATHANGLE, TG::LBrace, 5 },
|
|
{ u"laplace", TLAPLACE, MS_LAPLACE, TG::Standalone, 5 },
|
|
{ u"lbrace", TLBRACE, MS_LBRACE, TG::LBrace, 5 },
|
|
{ u"lceil", TLCEIL, MS_LCEIL, TG::LBrace, 5 },
|
|
{ u"ldbracket", TLDBRACKET, MS_LDBRACKET, TG::LBrace, 5 },
|
|
{ u"ldline", TLDLINE, MS_DVERTLINE, TG::LBrace, 5 },
|
|
{ u"le", TLE, MS_LE, TG::Relation, 0 },
|
|
{ u"left", TLEFT, '\0', TG::NONE, 5 },
|
|
{ u"leftarrow", TLEFTARROW, MS_LEFTARROW, TG::Standalone, 5 },
|
|
{ u"leslant", TLESLANT, MS_LESLANT, TG::Relation, 0 },
|
|
{ u"lfloor", TLFLOOR, MS_LFLOOR, TG::LBrace, 5 },
|
|
{ u"lim", TLIM, '\0', TG::Oper, 5 },
|
|
{ u"liminf", TLIMINF, '\0', TG::Oper, 5 },
|
|
{ u"limsup", TLIMSUP, '\0', TG::Oper, 5 },
|
|
{ u"lint", TLINT, MS_LINT, TG::Oper, 5 },
|
|
{ u"ll", TLL, MS_LL, TG::Relation, 0 },
|
|
{ u"lline", TLLINE, MS_VERTLINE, TG::LBrace, 5 },
|
|
{ u"llint", TLLINT, MS_LLINT, TG::Oper, 5 },
|
|
{ u"lllint", TLLLINT, MS_LLLINT, TG::Oper, 5 },
|
|
{ u"ln", TLN, '\0', TG::Function, 5 },
|
|
{ u"log", TLOG, '\0', TG::Function, 5 },
|
|
{ u"lrline", TLRLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
|
|
{ u"lrdline", TLRDLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
|
|
{ u"lsub", TLSUB, '\0', TG::Power, 0 },
|
|
{ u"lsup", TLSUP, '\0', TG::Power, 0 },
|
|
{ u"lt", TLT, MS_LT, TG::Relation, 0 },
|
|
{ u"matrix", TMATRIX, '\0', TG::NONE, 5 },
|
|
{ u"minusplus", TMINUSPLUS, MS_MINUSPLUS, TG::UnOper | TG::Sum, 5 },
|
|
{ u"mline", TMLINE, MS_VERTLINE, TG::NONE, 0 }, //! not in TG::RBrace, Level 0
|
|
{ u"nabla", TNABLA, MS_NABLA, TG::Standalone, 5 },
|
|
{ u"nbold", TNBOLD, '\0', TG::FontAttr, 5 },
|
|
{ u"ndivides", TNDIVIDES, MS_NDIVIDES, TG::Relation, 0 },
|
|
{ u"neg", TNEG, MS_NEG, TG::UnOper, 5 },
|
|
{ u"neq", TNEQ, MS_NEQ, TG::Relation, 0 },
|
|
{ u"newline", TNEWLINE, '\0', TG::NONE, 0 },
|
|
{ u"ni", TNI, MS_NI, TG::Relation, 0 },
|
|
{ u"nitalic", TNITALIC, '\0', TG::FontAttr, 5 },
|
|
{ u"none", TNONE, '\0', TG::LBrace | TG::RBrace, 0 },
|
|
{ u"nospace", TNOSPACE, '\0', TG::Standalone, 5 },
|
|
{ u"notexists", TNOTEXISTS, MS_NOTEXISTS, TG::Standalone, 5 },
|
|
{ u"notin", TNOTIN, MS_NOTIN, TG::Relation, 0 },
|
|
{ u"nprec", TNOTPRECEDES, MS_NOTPRECEDES, TG::Relation, 0 },
|
|
{ u"nroot", TNROOT, MS_SQRT, TG::UnOper, 5 },
|
|
{ u"nsubset", TNSUBSET, MS_NSUBSET, TG::Relation, 0 },
|
|
{ u"nsubseteq", TNSUBSETEQ, MS_NSUBSETEQ, TG::Relation, 0 },
|
|
{ u"nsucc", TNOTSUCCEEDS, MS_NOTSUCCEEDS, TG::Relation, 0 },
|
|
{ u"nsupset", TNSUPSET, MS_NSUPSET, TG::Relation, 0 },
|
|
{ u"nsupseteq", TNSUPSETEQ, MS_NSUPSETEQ, TG::Relation, 0 },
|
|
{ u"odivide", TODIVIDE, MS_ODIVIDE, TG::Product, 0 },
|
|
{ u"odot", TODOT, MS_ODOT, TG::Product, 0 },
|
|
{ u"ominus", TOMINUS, MS_OMINUS, TG::Sum, 0 },
|
|
{ u"oper", TOPER, '\0', TG::Oper, 5 },
|
|
{ u"oplus", TOPLUS, MS_OPLUS, TG::Sum, 0 },
|
|
{ u"or", TOR, MS_OR, TG::Sum, 0 },
|
|
{ u"ortho", TORTHO, MS_ORTHO, TG::Relation, 0 },
|
|
{ u"otimes", TOTIMES, MS_OTIMES, TG::Product, 0 },
|
|
{ u"over", TOVER, '\0', TG::Product, 0 },
|
|
{ u"overbrace", TOVERBRACE, MS_OVERBRACE, TG::Product, 5 },
|
|
{ u"overline", TOVERLINE, '\0', TG::Attribute, 5 },
|
|
{ u"overstrike", TOVERSTRIKE, '\0', TG::Attribute, 5 },
|
|
{ u"owns", TNI, MS_NI, TG::Relation, 0 },
|
|
{ u"parallel", TPARALLEL, MS_DLINE, TG::Relation, 0 },
|
|
{ u"partial", TPARTIAL, MS_PARTIAL, TG::Standalone, 5 },
|
|
{ u"phantom", TPHANTOM, '\0', TG::FontAttr, 5 },
|
|
{ u"plusminus", TPLUSMINUS, MS_PLUSMINUS, TG::UnOper | TG::Sum, 5 },
|
|
{ u"prec", TPRECEDES, MS_PRECEDES, TG::Relation, 0 },
|
|
{ u"preccurlyeq", TPRECEDESEQUAL, MS_PRECEDESEQUAL, TG::Relation, 0 },
|
|
{ u"precsim", TPRECEDESEQUIV, MS_PRECEDESEQUIV, TG::Relation, 0 },
|
|
{ u"prod", TPROD, MS_PROD, TG::Oper, 5 },
|
|
{ u"prop", TPROP, MS_PROP, TG::Relation, 0 },
|
|
{ u"rangle", TRANGLE, MS_RMATHANGLE, TG::RBrace, 0 }, //! 0 to terminate expression
|
|
{ u"rbrace", TRBRACE, MS_RBRACE, TG::RBrace, 0 },
|
|
{ u"rceil", TRCEIL, MS_RCEIL, TG::RBrace, 0 },
|
|
{ u"rdbracket", TRDBRACKET, MS_RDBRACKET, TG::RBrace, 0 },
|
|
{ u"rdline", TRDLINE, MS_DVERTLINE, TG::RBrace, 0 },
|
|
{ u"re", TRE, MS_RE, TG::Standalone, 5 },
|
|
{ u"rfloor", TRFLOOR, MS_RFLOOR, TG::RBrace, 0 }, //! 0 to terminate expression
|
|
{ u"right", TRIGHT, '\0', TG::NONE, 0 },
|
|
{ u"rightarrow", TRIGHTARROW, MS_RIGHTARROW, TG::Standalone, 5 },
|
|
{ u"rline", TRLINE, MS_VERTLINE, TG::RBrace, 0 }, //! 0 to terminate expression
|
|
{ u"rsub", TRSUB, '\0', TG::Power, 0 },
|
|
{ u"rsup", TRSUP, '\0', TG::Power, 0 },
|
|
{ u"sans", TSANS, '\0', TG::Font, 0 },
|
|
{ u"serif", TSERIF, '\0', TG::Font, 0 },
|
|
{ u"setC", TSETC, MS_SETC, TG::Standalone, 5 },
|
|
{ u"setminus", TSETMINUS, MS_BACKSLASH, TG::Product, 0 },
|
|
{ u"setN", TSETN, MS_SETN, TG::Standalone, 5 },
|
|
{ u"setQ", TSETQ, MS_SETQ, TG::Standalone, 5 },
|
|
{ u"setquotient", TSETQUOTIENT, MS_SLASH, TG::Product, 0 },
|
|
{ u"setR", TSETR, MS_SETR, TG::Standalone, 5 },
|
|
{ u"setZ", TSETZ, MS_SETZ, TG::Standalone, 5 },
|
|
{ u"sim", TSIM, MS_SIM, TG::Relation, 0 },
|
|
{ u"simeq", TSIMEQ, MS_SIMEQ, TG::Relation, 0 },
|
|
{ u"sin", TSIN, '\0', TG::Function, 5 },
|
|
{ u"sinh", TSINH, '\0', TG::Function, 5 },
|
|
{ u"size", TSIZE, '\0', TG::FontAttr, 5 },
|
|
{ u"slash", TSLASH, MS_SLASH, TG::Product, 0 },
|
|
{ u"sqrt", TSQRT, MS_SQRT, TG::UnOper, 5 },
|
|
{ u"stack", TSTACK, '\0', TG::NONE, 5 },
|
|
{ u"sub", TRSUB, '\0', TG::Power, 0 },
|
|
{ u"subset", TSUBSET, MS_SUBSET, TG::Relation, 0 },
|
|
{ u"subseteq", TSUBSETEQ, MS_SUBSETEQ, TG::Relation, 0 },
|
|
{ u"succ", TSUCCEEDS, MS_SUCCEEDS, TG::Relation, 0 },
|
|
{ u"succcurlyeq", TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TG::Relation, 0 },
|
|
{ u"succsim", TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TG::Relation, 0 },
|
|
{ u"sum", TSUM, MS_SUM, TG::Oper, 5 },
|
|
{ u"sup", TRSUP, '\0', TG::Power, 0 },
|
|
{ u"supset", TSUPSET, MS_SUPSET, TG::Relation, 0 },
|
|
{ u"supseteq", TSUPSETEQ, MS_SUPSETEQ, TG::Relation, 0 },
|
|
{ u"tan", TTAN, '\0', TG::Function, 5 },
|
|
{ u"tanh", TTANH, '\0', TG::Function, 5 },
|
|
{ u"tilde", TTILDE, MS_TILDE, TG::Attribute, 5 },
|
|
{ u"times", TTIMES, MS_TIMES, TG::Product, 0 },
|
|
{ u"to", TTO, '\0', TG::Limit, 0 },
|
|
{ u"toward", TTOWARD, MS_RIGHTARROW, TG::Relation, 0 },
|
|
{ u"transl", TTRANSL, MS_TRANSL, TG::Relation, 0 },
|
|
{ u"transr", TTRANSR, MS_TRANSR, TG::Relation, 0 },
|
|
{ u"underbrace", TUNDERBRACE, MS_UNDERBRACE, TG::Product, 5 },
|
|
{ u"underline", TUNDERLINE, '\0', TG::Attribute, 5 },
|
|
{ u"union", TUNION, MS_UNION, TG::Sum, 0 },
|
|
{ u"uoper", TUOPER, '\0', TG::UnOper, 5 },
|
|
{ u"uparrow", TUPARROW, MS_UPARROW, TG::Standalone, 5 },
|
|
{ u"vec", TVEC, MS_VEC, TG::Attribute, 5 },
|
|
{ u"widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
|
|
{ u"wideharpoon", TWIDEHARPOON, MS_HARPOON, TG::Attribute, 5 },
|
|
{ u"widehat", TWIDEHAT, MS_HAT, TG::Attribute, 5 },
|
|
{ u"wideslash", TWIDESLASH, MS_SLASH, TG::Product, 0 },
|
|
{ u"widetilde", TWIDETILDE, MS_TILDE, TG::Attribute, 5 },
|
|
{ u"widevec", TWIDEVEC, MS_VEC, TG::Attribute, 5 },
|
|
{ u"wp", TWP, MS_WP, TG::Standalone, 5 } };
|
|
|
|
// First character may be any alphabetic
|
|
const sal_Int32 coStartFlags = KParseTokens::ANY_LETTER | KParseTokens::IGNORE_LEADING_WS;
|
|
|
|
// Continuing characters may be any alphabetic
|
|
const sal_Int32 coContFlags = (coStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
|
|
| KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
|
|
// First character for numbers, may be any numeric or dot
|
|
const sal_Int32 coNumStartFlags
|
|
= KParseTokens::ASC_DIGIT | KParseTokens::ASC_DOT | KParseTokens::IGNORE_LEADING_WS;
|
|
// Continuing characters for numbers, may be any numeric or dot or comma.
|
|
// tdf#127873: additionally accept ',' comma group separator as too many
|
|
// existing documents unwittingly may have used that as decimal separator
|
|
// in such locales (though it never was as this is always the en-US locale
|
|
// and the group separator is only parsed away).
|
|
const sal_Int32 coNumContFlags = (coNumStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
|
|
| KParseTokens::GROUP_SEPARATOR_IN_NUMBER;
|
|
// First character for numbers hexadecimal
|
|
const sal_Int32 coNum16StartFlags
|
|
= KParseTokens::ASC_DIGIT | KParseTokens::ASC_UPALPHA | KParseTokens::IGNORE_LEADING_WS;
|
|
|
|
// Continuing characters for numbers hexadecimal
|
|
const sal_Int32 coNum16ContFlags = (coNum16StartFlags & ~KParseTokens::IGNORE_LEADING_WS);
|
|
// user-defined char continuing characters may be any alphanumeric or dot.
|
|
const sal_Int32 coUserDefinedCharContFlags = KParseTokens::ANY_LETTER_OR_NUMBER
|
|
| KParseTokens::ASC_DOT
|
|
| KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
|
|
|
|
//Checks if keyword is in the list.
|
|
static inline bool findCompare(const SmTokenTableEntry& lhs, const OUString& s)
|
|
{
|
|
return s.compareToIgnoreAsciiCase(lhs.aIdent) > 0;
|
|
}
|
|
|
|
//Returns the SmTokenTableEntry for a keyword
|
|
static const SmTokenTableEntry* GetTokenTableEntry(const OUString& rName)
|
|
{
|
|
if (rName.isEmpty())
|
|
return nullptr; //avoid null pointer exceptions
|
|
//Looks for the first keyword after or equal to rName in alphabetical order.
|
|
auto findIter
|
|
= std::lower_bound(std::begin(aTokenTable), std::end(aTokenTable), rName, findCompare);
|
|
if (findIter != std::end(aTokenTable) && rName.equalsIgnoreAsciiCase(findIter->aIdent))
|
|
return &*findIter; //check is equal
|
|
return nullptr; //not found
|
|
}
|
|
|
|
static bool IsDelimiter(const OUString& rTxt, sal_Int32 nPos)
|
|
{ // returns 'true' iff cChar is '\0' or a delimiter
|
|
|
|
assert(nPos <= rTxt.getLength()); //index out of range
|
|
if (nPos == rTxt.getLength())
|
|
return true; //This is EOF
|
|
sal_Unicode cChar = rTxt[nPos];
|
|
|
|
// check if 'cChar' is in the delimiter table
|
|
static const sal_Unicode aDelimiterTable[] = {
|
|
' ', '{', '}', '(', ')', '\t', '\n', '\r', '+', '-', '*', '/', '=', '[',
|
|
']', '^', '_', '#', '%', '>', '<', '&', '|', '\\', '"', '~', '`'
|
|
}; //reordered by usage (by eye) for nanoseconds saving.
|
|
|
|
//checks the array
|
|
for (auto const& cDelimiter : aDelimiterTable)
|
|
{
|
|
if (cDelimiter == cChar)
|
|
return true;
|
|
}
|
|
|
|
//special chars support
|
|
sal_Int16 nTypJp = SM_MOD()->GetSysLocale().GetCharClass().getType(rTxt, nPos);
|
|
return (nTypJp == css::i18n::UnicodeType::SPACE_SEPARATOR
|
|
|| nTypJp == css::i18n::UnicodeType::CONTROL);
|
|
}
|
|
|
|
// checks number used as arguments in Math formulas (e.g. 'size' command)
|
|
// Format: no negative numbers, must start with a digit, no exponent notation, ...
|
|
static bool lcl_IsNumber(const OUString& rText)
|
|
{
|
|
bool bPoint = false;
|
|
const sal_Unicode* pBuffer = rText.getStr();
|
|
for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
|
|
{
|
|
const sal_Unicode cChar = *pBuffer;
|
|
if (cChar == '.')
|
|
{
|
|
if (bPoint)
|
|
return false;
|
|
else
|
|
bPoint = true;
|
|
}
|
|
else if (!rtl::isAsciiDigit(cChar))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
// checks number used as arguments in Math formulas (e.g. 'size' command)
|
|
// Format: no negative numbers, must start with a digit, no exponent notation, ...
|
|
static bool lcl_IsNotWholeNumber(const OUString& rText)
|
|
{
|
|
const sal_Unicode* pBuffer = rText.getStr();
|
|
for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
|
|
if (!rtl::isAsciiDigit(*pBuffer))
|
|
return true;
|
|
return false;
|
|
}
|
|
// checks hex number used as arguments in Math formulas (e.g. 'hex' command)
|
|
// Format: no negative numbers, must start with a digit, no exponent notation, ...
|
|
static bool lcl_IsNotWholeNumber16(const OUString& rText)
|
|
{
|
|
const sal_Unicode* pBuffer = rText.getStr();
|
|
for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
|
|
if (!rtl::isAsciiCanonicHexDigit(*pBuffer))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
//Text replace onto m_aBufferString
|
|
void SmParser5::Replace(sal_Int32 nPos, sal_Int32 nLen, std::u16string_view aText)
|
|
{
|
|
assert(nPos + nLen <= m_aBufferString.getLength()); //checks if length allows text replace
|
|
|
|
m_aBufferString = m_aBufferString.replaceAt(nPos, nLen, aText); //replace and reindex
|
|
sal_Int32 nChg = aText.size() - nLen;
|
|
m_nBufferIndex = m_nBufferIndex + nChg;
|
|
m_nTokenIndex = m_nTokenIndex + nChg;
|
|
}
|
|
|
|
void SmParser5::NextToken() //Central part of the parser
|
|
{
|
|
sal_Int32 nBufLen = m_aBufferString.getLength();
|
|
ParseResult aRes;
|
|
sal_Int32 nRealStart;
|
|
bool bCont;
|
|
do
|
|
{
|
|
// skip white spaces
|
|
while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
|
|
++m_nBufferIndex;
|
|
|
|
// Try to parse a number in a locale-independent manner using
|
|
// '.' as decimal separator.
|
|
// See https://bz.apache.org/ooo/show_bug.cgi?id=45779
|
|
aRes
|
|
= m_aNumCC.parsePredefinedToken(KParseType::ASC_NUMBER, m_aBufferString, m_nBufferIndex,
|
|
coNumStartFlags, "", coNumContFlags, "");
|
|
|
|
if (aRes.TokenType == 0)
|
|
{
|
|
// Try again with the default token parsing.
|
|
aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
|
|
coContFlags, "");
|
|
}
|
|
|
|
nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
|
|
m_nBufferIndex = nRealStart;
|
|
|
|
bCont = false;
|
|
if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
|
|
{
|
|
// keep data needed for tokens row and col entry up to date
|
|
++m_nRow;
|
|
m_nBufferIndex = m_nColOff = nRealStart + 1;
|
|
bCont = true;
|
|
}
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
|
|
{
|
|
//SkipComment
|
|
m_nBufferIndex = nRealStart + 2;
|
|
while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
|
|
++m_nBufferIndex;
|
|
bCont = true;
|
|
}
|
|
}
|
|
|
|
} while (bCont);
|
|
|
|
// set index of current token
|
|
m_nTokenIndex = m_nBufferIndex;
|
|
sal_uInt32 nCol = nRealStart - m_nColOff;
|
|
|
|
bool bHandled = true;
|
|
if (nRealStart >= nBufLen)
|
|
{
|
|
m_aCurToken.eType = TEND;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText.clear();
|
|
}
|
|
else if (aRes.TokenType & KParseType::ANY_NUMBER)
|
|
{
|
|
assert(aRes.EndPos > 0);
|
|
if (m_aBufferString[aRes.EndPos - 1] == ',' && aRes.EndPos < nBufLen
|
|
&& m_pSysCC->getType(m_aBufferString, aRes.EndPos) != UnicodeType::SPACE_SEPARATOR)
|
|
{
|
|
// Comma followed by a non-space char is unlikely for decimal/thousands separator.
|
|
--aRes.EndPos;
|
|
}
|
|
sal_Int32 n = aRes.EndPos - nRealStart;
|
|
assert(n >= 0);
|
|
m_aCurToken.eType = TNUMBER;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = m_aBufferString.copy(nRealStart, n);
|
|
|
|
SAL_WARN_IF(!IsDelimiter(m_aBufferString, aRes.EndPos), "starmath",
|
|
"identifier really finished? (compatibility!)");
|
|
}
|
|
else if (aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING)
|
|
{
|
|
m_aCurToken.eType = TTEXT;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = aRes.DequotedNameOrString;
|
|
nCol++;
|
|
}
|
|
else if (aRes.TokenType & KParseType::IDENTNAME)
|
|
{
|
|
sal_Int32 n = aRes.EndPos - nRealStart;
|
|
assert(n >= 0);
|
|
OUString aName(m_aBufferString.copy(nRealStart, n));
|
|
const SmTokenTableEntry* pEntry = GetTokenTableEntry(aName);
|
|
|
|
if (pEntry)
|
|
{
|
|
m_aCurToken.eType = pEntry->eType;
|
|
m_aCurToken.setChar(pEntry->cMathChar);
|
|
m_aCurToken.nGroup = pEntry->nGroup;
|
|
m_aCurToken.nLevel = pEntry->nLevel;
|
|
m_aCurToken.aText = pEntry->aIdent;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TIDENT;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = aName;
|
|
|
|
SAL_WARN_IF(!IsDelimiter(m_aBufferString, aRes.EndPos), "starmath",
|
|
"identifier really finished? (compatibility!)");
|
|
}
|
|
}
|
|
else if (aRes.TokenType == 0 && '_' == m_aBufferString[nRealStart])
|
|
{
|
|
m_aCurToken.eType = TRSUB;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::Power;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "_";
|
|
|
|
aRes.EndPos = nRealStart + 1;
|
|
}
|
|
else if (aRes.TokenType & KParseType::BOOLEAN)
|
|
{
|
|
sal_Int32& rnEndPos = aRes.EndPos;
|
|
if (rnEndPos - nRealStart <= 2)
|
|
{
|
|
sal_Unicode ch = m_aBufferString[nRealStart];
|
|
switch (ch)
|
|
{
|
|
case '<':
|
|
{
|
|
if (m_aBufferString.match("<<", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TLL;
|
|
m_aCurToken.setChar(MS_LL);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "<<";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match("<=", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TLE;
|
|
m_aCurToken.setChar(MS_LE);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "<=";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match("<-", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TLEFTARROW;
|
|
m_aCurToken.setChar(MS_LEFTARROW);
|
|
m_aCurToken.nGroup = TG::Standalone;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "<-";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match("<>", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TNEQ;
|
|
m_aCurToken.setChar(MS_NEQ);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "<>";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match("<?>", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TPLACE;
|
|
m_aCurToken.setChar(MS_PLACE);
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "<?>";
|
|
|
|
rnEndPos = nRealStart + 3;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TLT;
|
|
m_aCurToken.setChar(MS_LT);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "<";
|
|
}
|
|
}
|
|
break;
|
|
case '>':
|
|
{
|
|
if (m_aBufferString.match(">=", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TGE;
|
|
m_aCurToken.setChar(MS_GE);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = ">=";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match(">>", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TGG;
|
|
m_aCurToken.setChar(MS_GG);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = ">>";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TGT;
|
|
m_aCurToken.setChar(MS_GT);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = ">";
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
bHandled = false;
|
|
}
|
|
}
|
|
}
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
sal_Int32& rnEndPos = aRes.EndPos;
|
|
if (rnEndPos - nRealStart == 1)
|
|
{
|
|
sal_Unicode ch = m_aBufferString[nRealStart];
|
|
switch (ch)
|
|
{
|
|
case '%':
|
|
{
|
|
//! modifies aRes.EndPos
|
|
|
|
OSL_ENSURE(rnEndPos >= nBufLen || '%' != m_aBufferString[rnEndPos],
|
|
"unexpected comment start");
|
|
|
|
// get identifier of user-defined character
|
|
ParseResult aTmpRes = m_pSysCC->parseAnyToken(m_aBufferString, rnEndPos,
|
|
KParseTokens::ANY_LETTER, "",
|
|
coUserDefinedCharContFlags, "");
|
|
|
|
sal_Int32 nTmpStart = rnEndPos + aTmpRes.LeadingWhiteSpace;
|
|
|
|
// default setting for the case that no identifier
|
|
// i.e. a valid symbol-name is following the '%'
|
|
// character
|
|
m_aCurToken.eType = TTEXT;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "%";
|
|
|
|
if (aTmpRes.TokenType & KParseType::IDENTNAME)
|
|
{
|
|
sal_Int32 n = aTmpRes.EndPos - nTmpStart;
|
|
m_aCurToken.eType = TSPECIAL;
|
|
m_aCurToken.aText = m_aBufferString.copy(nTmpStart - 1, n + 1);
|
|
|
|
OSL_ENSURE(aTmpRes.EndPos > rnEndPos, "empty identifier");
|
|
if (aTmpRes.EndPos > rnEndPos)
|
|
rnEndPos = aTmpRes.EndPos;
|
|
else
|
|
++rnEndPos;
|
|
}
|
|
|
|
// if no symbol-name was found we start-over with
|
|
// finding the next token right after the '%' sign.
|
|
// I.e. we leave rnEndPos unmodified.
|
|
}
|
|
break;
|
|
case '[':
|
|
{
|
|
m_aCurToken.eType = TLBRACKET;
|
|
m_aCurToken.setChar(MS_LBRACKET);
|
|
m_aCurToken.nGroup = TG::LBrace;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "[";
|
|
}
|
|
break;
|
|
case '\\':
|
|
{
|
|
m_aCurToken.eType = TESCAPE;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "\\";
|
|
}
|
|
break;
|
|
case ']':
|
|
{
|
|
m_aCurToken.eType = TRBRACKET;
|
|
m_aCurToken.setChar(MS_RBRACKET);
|
|
m_aCurToken.nGroup = TG::RBrace;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "]";
|
|
}
|
|
break;
|
|
case '^':
|
|
{
|
|
m_aCurToken.eType = TRSUP;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::Power;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "^";
|
|
}
|
|
break;
|
|
case '`':
|
|
{
|
|
m_aCurToken.eType = TSBLANK;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::Blank;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "`";
|
|
}
|
|
break;
|
|
case '{':
|
|
{
|
|
m_aCurToken.eType = TLGROUP;
|
|
m_aCurToken.setChar(MS_LBRACE);
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "{";
|
|
}
|
|
break;
|
|
case '|':
|
|
{
|
|
m_aCurToken.eType = TOR;
|
|
m_aCurToken.setChar(MS_OR);
|
|
m_aCurToken.nGroup = TG::Sum;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "|";
|
|
}
|
|
break;
|
|
case '}':
|
|
{
|
|
m_aCurToken.eType = TRGROUP;
|
|
m_aCurToken.setChar(MS_RBRACE);
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "}";
|
|
}
|
|
break;
|
|
case '~':
|
|
{
|
|
m_aCurToken.eType = TBLANK;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::Blank;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "~";
|
|
}
|
|
break;
|
|
case '#':
|
|
{
|
|
if (m_aBufferString.match("##", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TDPOUND;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "##";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TPOUND;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "#";
|
|
}
|
|
}
|
|
break;
|
|
case '&':
|
|
{
|
|
m_aCurToken.eType = TAND;
|
|
m_aCurToken.setChar(MS_AND);
|
|
m_aCurToken.nGroup = TG::Product;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "&";
|
|
}
|
|
break;
|
|
case '(':
|
|
{
|
|
m_aCurToken.eType = TLPARENT;
|
|
m_aCurToken.setChar(MS_LPARENT);
|
|
m_aCurToken.nGroup = TG::LBrace;
|
|
m_aCurToken.nLevel = 5; //! 0 to continue expression
|
|
m_aCurToken.aText = "(";
|
|
}
|
|
break;
|
|
case ')':
|
|
{
|
|
m_aCurToken.eType = TRPARENT;
|
|
m_aCurToken.setChar(MS_RPARENT);
|
|
m_aCurToken.nGroup = TG::RBrace;
|
|
m_aCurToken.nLevel = 0; //! 0 to terminate expression
|
|
m_aCurToken.aText = ")";
|
|
}
|
|
break;
|
|
case '*':
|
|
{
|
|
m_aCurToken.eType = TMULTIPLY;
|
|
m_aCurToken.setChar(MS_MULTIPLY);
|
|
m_aCurToken.nGroup = TG::Product;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "*";
|
|
}
|
|
break;
|
|
case '+':
|
|
{
|
|
if (m_aBufferString.match("+-", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TPLUSMINUS;
|
|
m_aCurToken.setChar(MS_PLUSMINUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "+-";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TPLUS;
|
|
m_aCurToken.setChar(MS_PLUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "+";
|
|
}
|
|
}
|
|
break;
|
|
case '-':
|
|
{
|
|
if (m_aBufferString.match("-+", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TMINUSPLUS;
|
|
m_aCurToken.setChar(MS_MINUSPLUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "-+";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else if (m_aBufferString.match("->", nRealStart))
|
|
{
|
|
m_aCurToken.eType = TRIGHTARROW;
|
|
m_aCurToken.setChar(MS_RIGHTARROW);
|
|
m_aCurToken.nGroup = TG::Standalone;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "->";
|
|
|
|
rnEndPos = nRealStart + 2;
|
|
}
|
|
else
|
|
{
|
|
m_aCurToken.eType = TMINUS;
|
|
m_aCurToken.setChar(MS_MINUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "-";
|
|
}
|
|
}
|
|
break;
|
|
case '.':
|
|
{
|
|
// Only one character? Then it can't be a number.
|
|
if (m_nBufferIndex < m_aBufferString.getLength() - 1)
|
|
{
|
|
// for compatibility with SO5.2
|
|
// texts like .34 ...56 ... h ...78..90
|
|
// will be treated as numbers
|
|
m_aCurToken.eType = TNUMBER;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
|
|
sal_Int32 nTxtStart = m_nBufferIndex;
|
|
sal_Unicode cChar;
|
|
// if the equation ends with dot(.) then increment m_nBufferIndex till end of string only
|
|
do
|
|
{
|
|
cChar = m_aBufferString[++m_nBufferIndex];
|
|
} while ((cChar == '.' || rtl::isAsciiDigit(cChar))
|
|
&& (m_nBufferIndex < m_aBufferString.getLength() - 1));
|
|
|
|
m_aCurToken.aText
|
|
= m_aBufferString.copy(nTxtStart, m_nBufferIndex - nTxtStart);
|
|
aRes.EndPos = m_nBufferIndex;
|
|
}
|
|
else
|
|
bHandled = false;
|
|
}
|
|
break;
|
|
case '/':
|
|
{
|
|
m_aCurToken.eType = TDIVIDEBY;
|
|
m_aCurToken.setChar(MS_SLASH);
|
|
m_aCurToken.nGroup = TG::Product;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "/";
|
|
}
|
|
break;
|
|
case '=':
|
|
{
|
|
m_aCurToken.eType = TASSIGN;
|
|
m_aCurToken.setChar(MS_ASSIGN);
|
|
m_aCurToken.nGroup = TG::Relation;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "=";
|
|
}
|
|
break;
|
|
default:
|
|
bHandled = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
bHandled = false;
|
|
|
|
if (!bHandled)
|
|
{
|
|
m_aCurToken.eType = TCHARACTER;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
|
|
// tdf#129372: we may have to deal with surrogate pairs
|
|
// (see https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates)
|
|
// in this case, we must read 2 sal_Unicode instead of 1
|
|
int nOffset(rtl::isSurrogate(m_aBufferString[nRealStart]) ? 2 : 1);
|
|
m_aCurToken.aText = m_aBufferString.copy(nRealStart, nOffset);
|
|
|
|
aRes.EndPos = nRealStart + nOffset;
|
|
}
|
|
m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
|
|
|
|
if (TEND != m_aCurToken.eType)
|
|
m_nBufferIndex = aRes.EndPos;
|
|
}
|
|
|
|
void SmParser5::NextTokenColor(SmTokenType dvipload)
|
|
{
|
|
sal_Int32 nBufLen = m_aBufferString.getLength();
|
|
ParseResult aRes;
|
|
sal_Int32 nRealStart;
|
|
bool bCont;
|
|
|
|
do
|
|
{
|
|
// skip white spaces
|
|
while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
|
|
++m_nBufferIndex;
|
|
//parse, there are few options, so less strict.
|
|
aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
|
|
coContFlags, "");
|
|
nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
|
|
m_nBufferIndex = nRealStart;
|
|
bCont = false;
|
|
if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
|
|
{
|
|
// keep data needed for tokens row and col entry up to date
|
|
++m_nRow;
|
|
m_nBufferIndex = m_nColOff = nRealStart + 1;
|
|
bCont = true;
|
|
}
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
|
|
{
|
|
//SkipComment
|
|
m_nBufferIndex = nRealStart + 2;
|
|
while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
|
|
++m_nBufferIndex;
|
|
bCont = true;
|
|
}
|
|
}
|
|
} while (bCont);
|
|
|
|
// set index of current token
|
|
m_nTokenIndex = m_nBufferIndex;
|
|
sal_uInt32 nCol = nRealStart - m_nColOff;
|
|
|
|
if (nRealStart >= nBufLen)
|
|
m_aCurToken.eType = TEND;
|
|
else if (aRes.TokenType & KParseType::IDENTNAME)
|
|
{
|
|
sal_Int32 n = aRes.EndPos - nRealStart;
|
|
assert(n >= 0);
|
|
OUString aName(m_aBufferString.copy(nRealStart, n));
|
|
switch (dvipload)
|
|
{
|
|
case TCOLOR:
|
|
m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName);
|
|
break;
|
|
case TDVIPSNAMESCOL:
|
|
m_aCurToken = starmathdatabase::Identify_ColorName_DVIPSNAMES(aName);
|
|
break;
|
|
default:
|
|
m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName);
|
|
break;
|
|
}
|
|
}
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
if (m_aBufferString[nRealStart] == '#' && !m_aBufferString.match("##", nRealStart))
|
|
{
|
|
m_aCurToken.eType = THEX;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::Color;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "hex";
|
|
}
|
|
}
|
|
else
|
|
m_aCurToken.eType = TNONE;
|
|
|
|
m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
|
|
if (TEND != m_aCurToken.eType)
|
|
m_nBufferIndex = aRes.EndPos;
|
|
}
|
|
|
|
void SmParser5::NextTokenFontSize()
|
|
{
|
|
sal_Int32 nBufLen = m_aBufferString.getLength();
|
|
ParseResult aRes;
|
|
sal_Int32 nRealStart;
|
|
bool bCont;
|
|
bool hex = false;
|
|
|
|
do
|
|
{
|
|
// skip white spaces
|
|
while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
|
|
++m_nBufferIndex;
|
|
//hexadecimal parser
|
|
aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coNum16StartFlags, ".",
|
|
coNum16ContFlags, ".,");
|
|
if (aRes.TokenType == 0)
|
|
{
|
|
// Try again with the default token parsing.
|
|
aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
|
|
coContFlags, "");
|
|
}
|
|
else
|
|
hex = true;
|
|
nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
|
|
m_nBufferIndex = nRealStart;
|
|
bCont = false;
|
|
if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
|
|
{
|
|
// keep data needed for tokens row and col entry up to date
|
|
++m_nRow;
|
|
m_nBufferIndex = m_nColOff = nRealStart + 1;
|
|
bCont = true;
|
|
}
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
|
|
{
|
|
//SkipComment
|
|
m_nBufferIndex = nRealStart + 2;
|
|
while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
|
|
++m_nBufferIndex;
|
|
bCont = true;
|
|
}
|
|
}
|
|
} while (bCont);
|
|
|
|
// set index of current token
|
|
m_nTokenIndex = m_nBufferIndex;
|
|
sal_uInt32 nCol = nRealStart - m_nColOff;
|
|
|
|
if (nRealStart >= nBufLen)
|
|
m_aCurToken.eType = TEND;
|
|
else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
|
|
{
|
|
if (aRes.EndPos - nRealStart == 1)
|
|
{
|
|
switch (m_aBufferString[nRealStart])
|
|
{
|
|
case '*':
|
|
m_aCurToken.eType = TMULTIPLY;
|
|
m_aCurToken.setChar(MS_MULTIPLY);
|
|
m_aCurToken.nGroup = TG::Product;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "*";
|
|
break;
|
|
case '+':
|
|
m_aCurToken.eType = TPLUS;
|
|
m_aCurToken.setChar(MS_PLUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "+";
|
|
break;
|
|
case '-':
|
|
m_aCurToken.eType = TMINUS;
|
|
m_aCurToken.setChar(MS_MINUS);
|
|
m_aCurToken.nGroup = TG::UnOper | TG::Sum;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = "-";
|
|
break;
|
|
case '/':
|
|
m_aCurToken.eType = TDIVIDEBY;
|
|
m_aCurToken.setChar(MS_SLASH);
|
|
m_aCurToken.nGroup = TG::Product;
|
|
m_aCurToken.nLevel = 0;
|
|
m_aCurToken.aText = "/";
|
|
break;
|
|
default:
|
|
m_aCurToken.eType = TNONE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
m_aCurToken.eType = TNONE;
|
|
}
|
|
else if (hex)
|
|
{
|
|
assert(aRes.EndPos > 0);
|
|
sal_Int32 n = aRes.EndPos - nRealStart;
|
|
assert(n >= 0);
|
|
m_aCurToken.eType = THEX;
|
|
m_aCurToken.cMathChar = u"";
|
|
m_aCurToken.nGroup = TG::NONE;
|
|
m_aCurToken.nLevel = 5;
|
|
m_aCurToken.aText = m_aBufferString.copy(nRealStart, n);
|
|
}
|
|
else
|
|
m_aCurToken.eType = TNONE;
|
|
|
|
m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
|
|
if (TEND != m_aCurToken.eType)
|
|
m_nBufferIndex = aRes.EndPos;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
SmNodeArray buildNodeArray(std::vector<std::unique_ptr<SmNode>>& rSubNodes)
|
|
{
|
|
SmNodeArray aSubArray(rSubNodes.size());
|
|
for (size_t i = 0; i < rSubNodes.size(); ++i)
|
|
aSubArray[i] = rSubNodes[i].release();
|
|
return aSubArray;
|
|
}
|
|
} //end namespace
|
|
|
|
// grammar
|
|
/*************************************************************************************************/
|
|
|
|
std::unique_ptr<SmTableNode> SmParser5::DoTable()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::vector<std::unique_ptr<SmNode>> aLineArray;
|
|
aLineArray.push_back(DoLine());
|
|
while (m_aCurToken.eType == TNEWLINE)
|
|
{
|
|
NextToken();
|
|
aLineArray.push_back(DoLine());
|
|
}
|
|
assert(m_aCurToken.eType == TEND);
|
|
std::unique_ptr<SmTableNode> xSNode(new SmTableNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
xSNode->SetSubNodes(buildNodeArray(aLineArray));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoAlign(bool bUseExtraSpaces)
|
|
// parse alignment info (if any), then go on with rest of expression
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::unique_ptr<SmStructureNode> xSNode;
|
|
|
|
if (TokenInGroup(TG::Align))
|
|
{
|
|
xSNode.reset(new SmAlignNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
|
|
// allow for just one align statement in 5.0
|
|
if (TokenInGroup(TG::Align))
|
|
return DoError(SmParseError::DoubleAlign);
|
|
}
|
|
|
|
auto pNode = DoExpression(bUseExtraSpaces);
|
|
|
|
if (xSNode)
|
|
{
|
|
xSNode->SetSubNode(0, pNode.release());
|
|
return xSNode;
|
|
}
|
|
return pNode;
|
|
}
|
|
|
|
// Postcondition: m_aCurToken.eType == TEND || m_aCurToken.eType == TNEWLINE
|
|
std::unique_ptr<SmNode> SmParser5::DoLine()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::vector<std::unique_ptr<SmNode>> ExpressionArray;
|
|
|
|
// start with single expression that may have an alignment statement
|
|
// (and go on with expressions that must not have alignment
|
|
// statements in 'while' loop below. See also 'Expression()'.)
|
|
if (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
|
|
ExpressionArray.push_back(DoAlign());
|
|
|
|
while (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
|
|
ExpressionArray.push_back(DoExpression());
|
|
|
|
//If there's no expression, add an empty one.
|
|
//this is to avoid a formula tree without any caret
|
|
//positions, in visual formula editor.
|
|
if (ExpressionArray.empty())
|
|
{
|
|
SmToken aTok;
|
|
aTok.eType = TNEWLINE;
|
|
ExpressionArray.emplace_back(std::unique_ptr<SmNode>(new SmExpressionNode(aTok)));
|
|
}
|
|
|
|
auto xSNode = std::make_unique<SmLineNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
xSNode->SetSubNodes(buildNodeArray(ExpressionArray));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoExpression(bool bUseExtraSpaces)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::vector<std::unique_ptr<SmNode>> RelationArray;
|
|
RelationArray.push_back(DoRelation());
|
|
while (m_aCurToken.nLevel >= 4)
|
|
RelationArray.push_back(DoRelation());
|
|
|
|
if (RelationArray.size() > 1)
|
|
{
|
|
std::unique_ptr<SmExpressionNode> xSNode(new SmExpressionNode(m_aCurToken));
|
|
xSNode->SetSubNodes(buildNodeArray(RelationArray));
|
|
xSNode->SetUseExtraSpaces(bUseExtraSpaces);
|
|
return xSNode;
|
|
}
|
|
else
|
|
{
|
|
// This expression has only one node so just push this node.
|
|
return std::move(RelationArray[0]);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoRelation()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
int nDepthLimit = m_nParseDepth;
|
|
|
|
auto xFirst = DoSum();
|
|
while (TokenInGroup(TG::Relation))
|
|
{
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
auto xSecond = DoOpSubSup();
|
|
auto xThird = DoSum();
|
|
xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
|
|
xFirst = std::move(xSNode);
|
|
|
|
++m_nParseDepth;
|
|
DepthProtect bDepthGuard(m_nParseDepth);
|
|
}
|
|
|
|
m_nParseDepth = nDepthLimit;
|
|
|
|
return xFirst;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoSum()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
int nDepthLimit = m_nParseDepth;
|
|
|
|
auto xFirst = DoProduct();
|
|
while (TokenInGroup(TG::Sum))
|
|
{
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
auto xSecond = DoOpSubSup();
|
|
auto xThird = DoProduct();
|
|
xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
|
|
xFirst = std::move(xSNode);
|
|
|
|
++m_nParseDepth;
|
|
DepthProtect bDepthGuard(m_nParseDepth);
|
|
}
|
|
|
|
m_nParseDepth = nDepthLimit;
|
|
|
|
return xFirst;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoProduct()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
auto xFirst = DoPower();
|
|
|
|
int nDepthLimit = 0;
|
|
|
|
while (TokenInGroup(TG::Product))
|
|
{
|
|
//this linear loop builds a recursive structure, if it gets
|
|
//too deep then later processing, e.g. releasing the tree,
|
|
//can exhaust stack
|
|
if (m_nParseDepth + nDepthLimit > DEPTH_LIMIT)
|
|
throw std::range_error("parser depth limit");
|
|
|
|
std::unique_ptr<SmStructureNode> xSNode;
|
|
std::unique_ptr<SmNode> xOper;
|
|
|
|
SmTokenType eType = m_aCurToken.eType;
|
|
switch (eType)
|
|
{
|
|
case TOVER:
|
|
xSNode.reset(new SmBinVerNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
xOper.reset(new SmRectangleNode(m_aCurToken));
|
|
xOper->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
break;
|
|
|
|
case TBOPER:
|
|
xSNode.reset(new SmBinHorNode(m_aCurToken));
|
|
|
|
NextToken();
|
|
|
|
//Let the glyph node know it's a binary operation
|
|
m_aCurToken.eType = TBOPER;
|
|
m_aCurToken.nGroup = TG::Product;
|
|
xOper = DoGlyphSpecial();
|
|
break;
|
|
|
|
case TOVERBRACE:
|
|
case TUNDERBRACE:
|
|
xSNode.reset(new SmVerticalBraceNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
xOper.reset(new SmMathSymbolNode(m_aCurToken));
|
|
xOper->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
break;
|
|
|
|
case TWIDEBACKSLASH:
|
|
case TWIDESLASH:
|
|
{
|
|
SmBinDiagonalNode* pSTmp = new SmBinDiagonalNode(m_aCurToken);
|
|
pSTmp->SetAscending(eType == TWIDESLASH);
|
|
xSNode.reset(pSTmp);
|
|
|
|
xOper.reset(new SmPolyLineNode(m_aCurToken));
|
|
xOper->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
xSNode.reset(new SmBinHorNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
|
|
xOper = DoOpSubSup();
|
|
}
|
|
|
|
auto xArg = DoPower();
|
|
xSNode->SetSubNodesBinMo(std::move(xFirst), std::move(xOper), std::move(xArg));
|
|
xFirst = std::move(xSNode);
|
|
++nDepthLimit;
|
|
}
|
|
return xFirst;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoSubSup(TG nActiveGroup, std::unique_ptr<SmNode> xGivenNode)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(nActiveGroup == TG::Power || nActiveGroup == TG::Limit);
|
|
assert(m_aCurToken.nGroup == nActiveGroup);
|
|
|
|
std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken));
|
|
pNode->SetSelection(m_aCurESelection);
|
|
//! Of course 'm_aCurToken' is just the first sub-/supscript token.
|
|
//! It should be of no further interest. The positions of the
|
|
//! sub-/supscripts will be identified by the corresponding subnodes
|
|
//! index in the 'aSubNodes' array (enum value from 'SmSubSup').
|
|
|
|
pNode->SetUseLimits(nActiveGroup == TG::Limit);
|
|
|
|
// initialize subnodes array
|
|
std::vector<std::unique_ptr<SmNode>> aSubNodes(1 + SUBSUP_NUM_ENTRIES);
|
|
aSubNodes[0] = std::move(xGivenNode);
|
|
|
|
// process all sub-/supscripts
|
|
int nIndex = 0;
|
|
while (TokenInGroup(nActiveGroup))
|
|
{
|
|
SmTokenType eType(m_aCurToken.eType);
|
|
|
|
switch (eType)
|
|
{
|
|
case TRSUB:
|
|
nIndex = static_cast<int>(RSUB);
|
|
break;
|
|
case TRSUP:
|
|
nIndex = static_cast<int>(RSUP);
|
|
break;
|
|
case TFROM:
|
|
case TCSUB:
|
|
nIndex = static_cast<int>(CSUB);
|
|
break;
|
|
case TTO:
|
|
case TCSUP:
|
|
nIndex = static_cast<int>(CSUP);
|
|
break;
|
|
case TLSUB:
|
|
nIndex = static_cast<int>(LSUB);
|
|
break;
|
|
case TLSUP:
|
|
nIndex = static_cast<int>(LSUP);
|
|
break;
|
|
default:
|
|
SAL_WARN("starmath", "unknown case");
|
|
}
|
|
nIndex++;
|
|
assert(1 <= nIndex && nIndex <= SUBSUP_NUM_ENTRIES);
|
|
|
|
std::unique_ptr<SmNode> xENode;
|
|
if (aSubNodes[nIndex]) // if already occupied at earlier iteration
|
|
{
|
|
// forget the earlier one, remember an error instead
|
|
aSubNodes[nIndex].reset();
|
|
xENode = DoError(SmParseError::DoubleSubsupscript); // this also skips current token.
|
|
}
|
|
else
|
|
{
|
|
// skip sub-/supscript token
|
|
NextToken();
|
|
}
|
|
|
|
// get sub-/supscript node
|
|
// (even when we saw a double-sub/supscript error in the above
|
|
// in order to minimize mess and continue parsing.)
|
|
std::unique_ptr<SmNode> xSNode;
|
|
if (eType == TFROM || eType == TTO)
|
|
{
|
|
// parse limits in old 4.0 and 5.0 style
|
|
xSNode = DoRelation();
|
|
}
|
|
else
|
|
xSNode = DoTerm(true);
|
|
|
|
aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode);
|
|
}
|
|
|
|
pNode->SetSubNodes(buildNodeArray(aSubNodes));
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoSubSupEvaluate(std::unique_ptr<SmNode> xGivenNode)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken));
|
|
pNode->SetSelection(m_aCurESelection);
|
|
pNode->SetUseLimits(true);
|
|
|
|
// initialize subnodes array
|
|
std::vector<std::unique_ptr<SmNode>> aSubNodes(1 + SUBSUP_NUM_ENTRIES);
|
|
aSubNodes[0] = std::move(xGivenNode);
|
|
|
|
// process all sub-/supscripts
|
|
int nIndex = 0;
|
|
while (TokenInGroup(TG::Limit))
|
|
{
|
|
SmTokenType eType(m_aCurToken.eType);
|
|
|
|
switch (eType)
|
|
{
|
|
case TFROM:
|
|
nIndex = static_cast<int>(RSUB);
|
|
break;
|
|
case TTO:
|
|
nIndex = static_cast<int>(RSUP);
|
|
break;
|
|
default:
|
|
SAL_WARN("starmath", "unknown case");
|
|
}
|
|
nIndex++;
|
|
assert(1 <= nIndex && nIndex <= SUBSUP_NUM_ENTRIES);
|
|
|
|
std::unique_ptr<SmNode> xENode;
|
|
if (aSubNodes[nIndex]) // if already occupied at earlier iteration
|
|
{
|
|
// forget the earlier one, remember an error instead
|
|
aSubNodes[nIndex].reset();
|
|
xENode = DoError(SmParseError::DoubleSubsupscript); // this also skips current token.
|
|
}
|
|
else
|
|
NextToken(); // skip sub-/supscript token
|
|
|
|
// get sub-/supscript node
|
|
std::unique_ptr<SmNode> xSNode;
|
|
xSNode = DoTerm(true);
|
|
|
|
aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode);
|
|
}
|
|
|
|
pNode->SetSubNodes(buildNodeArray(aSubNodes));
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoOpSubSup()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
// get operator symbol
|
|
auto xNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
|
|
xNode->SetSelection(m_aCurESelection);
|
|
// skip operator token
|
|
NextToken();
|
|
// get sub- supscripts if any
|
|
if (m_aCurToken.nGroup == TG::Power)
|
|
return DoSubSup(TG::Power, std::move(xNode));
|
|
return xNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoPower()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
// get body for sub- supscripts on top of stack
|
|
std::unique_ptr<SmNode> xNode(DoTerm(false));
|
|
|
|
if (m_aCurToken.nGroup == TG::Power)
|
|
return DoSubSup(TG::Power, std::move(xNode));
|
|
return xNode;
|
|
}
|
|
|
|
std::unique_ptr<SmBlankNode> SmParser5::DoBlank()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(TokenInGroup(TG::Blank));
|
|
std::unique_ptr<SmBlankNode> pBlankNode(new SmBlankNode(m_aCurToken));
|
|
pBlankNode->SetSelection(m_aCurESelection);
|
|
|
|
do
|
|
{
|
|
pBlankNode->IncreaseBy(m_aCurToken);
|
|
NextToken();
|
|
} while (TokenInGroup(TG::Blank));
|
|
|
|
// Ignore trailing spaces, if corresponding option is set
|
|
if (m_aCurToken.eType == TNEWLINE
|
|
|| (m_aCurToken.eType == TEND && !utl::ConfigManager::IsFuzzing()
|
|
&& SM_MOD()->GetConfig()->IsIgnoreSpacesRight()))
|
|
{
|
|
pBlankNode->Clear();
|
|
}
|
|
return pBlankNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoTerm(bool bGroupNumberIdent)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
switch (m_aCurToken.eType)
|
|
{
|
|
case TESCAPE:
|
|
return DoEscape();
|
|
|
|
case TNOSPACE:
|
|
case TLGROUP:
|
|
{
|
|
bool bNoSpace = m_aCurToken.eType == TNOSPACE;
|
|
if (bNoSpace)
|
|
NextToken();
|
|
if (m_aCurToken.eType != TLGROUP)
|
|
return DoTerm(false); // nospace is no longer concerned
|
|
|
|
NextToken();
|
|
|
|
// allow for empty group
|
|
if (m_aCurToken.eType == TRGROUP)
|
|
{
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmExpressionNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
xSNode->SetSubNodes(nullptr, nullptr);
|
|
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(xSNode.release());
|
|
}
|
|
|
|
auto pNode = DoAlign(!bNoSpace);
|
|
if (m_aCurToken.eType == TRGROUP)
|
|
{
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
std::unique_ptr<SmNode> xError(DoError(SmParseError::RgroupExpected));
|
|
xSNode->SetSubNodes(std::move(pNode), std::move(xError));
|
|
return std::unique_ptr<SmNode>(xSNode.release());
|
|
}
|
|
|
|
case TLEFT:
|
|
return DoBrace();
|
|
case TEVALUATE:
|
|
return DoEvaluate();
|
|
|
|
case TBLANK:
|
|
case TSBLANK:
|
|
return DoBlank();
|
|
|
|
case TTEXT:
|
|
{
|
|
auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_TEXT);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
case TCHARACTER:
|
|
{
|
|
auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_VARIABLE);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
case TIDENT:
|
|
case TNUMBER:
|
|
{
|
|
auto pTextNode = std::make_unique<SmTextNode>(
|
|
m_aCurToken, m_aCurToken.eType == TNUMBER ? FNT_NUMBER : FNT_VARIABLE);
|
|
pTextNode->SetSelection(m_aCurESelection);
|
|
if (!bGroupNumberIdent)
|
|
{
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pTextNode.release());
|
|
}
|
|
std::vector<std::unique_ptr<SmNode>> aNodes;
|
|
// Some people want to be able to write "x_2n" for "x_{2n}"
|
|
// although e.g. LaTeX or AsciiMath interpret that as "x_2 n".
|
|
// The tokenizer skips whitespaces so we need some additional
|
|
// work to distinguish from "x_2 n".
|
|
// See https://bz.apache.org/ooo/show_bug.cgi?id=11752 and
|
|
// https://bugs.libreoffice.org/show_bug.cgi?id=55853
|
|
sal_Int32 nBufLen = m_aBufferString.getLength();
|
|
|
|
// We need to be careful to call NextToken() only after having
|
|
// tested for a whitespace separator (otherwise it will be
|
|
// skipped!)
|
|
bool moveToNextToken = true;
|
|
while (m_nBufferIndex < nBufLen
|
|
&& m_pSysCC->getType(m_aBufferString, m_nBufferIndex)
|
|
!= UnicodeType::SPACE_SEPARATOR)
|
|
{
|
|
NextToken();
|
|
if (m_aCurToken.eType != TNUMBER && m_aCurToken.eType != TIDENT)
|
|
{
|
|
// Neither a number nor an identifier. We just moved to
|
|
// the next token, so no need to do that again.
|
|
moveToNextToken = false;
|
|
break;
|
|
}
|
|
aNodes.emplace_back(std::unique_ptr<SmNode>(new SmTextNode(
|
|
m_aCurToken, m_aCurToken.eType == TNUMBER ? FNT_NUMBER : FNT_VARIABLE)));
|
|
}
|
|
if (moveToNextToken)
|
|
NextToken();
|
|
if (aNodes.empty())
|
|
return std::unique_ptr<SmNode>(pTextNode.release());
|
|
// We have several concatenated identifiers and numbers.
|
|
// Let's group them into one SmExpressionNode.
|
|
aNodes.insert(aNodes.begin(), std::move(pTextNode));
|
|
std::unique_ptr<SmExpressionNode> xNode(new SmExpressionNode(SmToken()));
|
|
xNode->SetSubNodes(buildNodeArray(aNodes));
|
|
return std::unique_ptr<SmNode>(xNode.release());
|
|
}
|
|
case TLEFTARROW:
|
|
case TRIGHTARROW:
|
|
case TUPARROW:
|
|
case TDOWNARROW:
|
|
case TCIRC:
|
|
case TDRARROW:
|
|
case TDLARROW:
|
|
case TDLRARROW:
|
|
case TEXISTS:
|
|
case TNOTEXISTS:
|
|
case TFORALL:
|
|
case TPARTIAL:
|
|
case TNABLA:
|
|
case TLAPLACE:
|
|
case TFOURIER:
|
|
case TTOWARD:
|
|
case TDOTSAXIS:
|
|
case TDOTSDIAG:
|
|
case TDOTSDOWN:
|
|
case TDOTSLOW:
|
|
case TDOTSUP:
|
|
case TDOTSVERT:
|
|
{
|
|
auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
|
|
case TSETN:
|
|
case TSETZ:
|
|
case TSETQ:
|
|
case TSETR:
|
|
case TSETC:
|
|
case THBAR:
|
|
case TLAMBDABAR:
|
|
case TBACKEPSILON:
|
|
case TALEPH:
|
|
case TIM:
|
|
case TRE:
|
|
case TWP:
|
|
case TEMPTYSET:
|
|
case TINFINITY:
|
|
{
|
|
auto pNode = std::make_unique<SmMathIdentifierNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
|
|
case TPLACE:
|
|
{
|
|
auto pNode = std::make_unique<SmPlaceNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
|
|
case TSPECIAL:
|
|
return DoSpecial();
|
|
|
|
case TBINOM:
|
|
return DoBinom();
|
|
|
|
case TFRAC:
|
|
return DoFrac();
|
|
|
|
case TSTACK:
|
|
return DoStack();
|
|
|
|
case TMATRIX:
|
|
return DoMatrix();
|
|
|
|
case THEX:
|
|
NextTokenFontSize();
|
|
if (m_aCurToken.eType == THEX)
|
|
{
|
|
auto pTextNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_NUMBER);
|
|
pTextNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return pTextNode;
|
|
}
|
|
else
|
|
return DoError(SmParseError::NumberExpected);
|
|
default:
|
|
if (TokenInGroup(TG::LBrace))
|
|
return DoBrace();
|
|
if (TokenInGroup(TG::Oper))
|
|
return DoOperator();
|
|
if (TokenInGroup(TG::UnOper))
|
|
return DoUnOper();
|
|
if (TokenInGroup(TG::Attribute) || TokenInGroup(TG::FontAttr))
|
|
{
|
|
std::stack<std::unique_ptr<SmStructureNode>,
|
|
std::vector<std::unique_ptr<SmStructureNode>>>
|
|
aStack;
|
|
bool bIsAttr;
|
|
for (;;)
|
|
{
|
|
bIsAttr = TokenInGroup(TG::Attribute);
|
|
if (!bIsAttr && !TokenInGroup(TG::FontAttr))
|
|
break;
|
|
aStack.push(bIsAttr ? DoAttribute() : DoFontAttribute());
|
|
}
|
|
|
|
auto xFirstNode = DoPower();
|
|
while (!aStack.empty())
|
|
{
|
|
std::unique_ptr<SmStructureNode> xNode = std::move(aStack.top());
|
|
aStack.pop();
|
|
xNode->SetSubNodes(nullptr, std::move(xFirstNode));
|
|
xFirstNode = std::move(xNode);
|
|
}
|
|
return xFirstNode;
|
|
}
|
|
if (TokenInGroup(TG::Function))
|
|
return DoFunction();
|
|
return DoError(SmParseError::UnexpectedChar);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoEscape()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
NextToken();
|
|
|
|
switch (m_aCurToken.eType)
|
|
{
|
|
case TLPARENT:
|
|
case TRPARENT:
|
|
case TLBRACKET:
|
|
case TRBRACKET:
|
|
case TLDBRACKET:
|
|
case TRDBRACKET:
|
|
case TLBRACE:
|
|
case TLGROUP:
|
|
case TRBRACE:
|
|
case TRGROUP:
|
|
case TLANGLE:
|
|
case TRANGLE:
|
|
case TLCEIL:
|
|
case TRCEIL:
|
|
case TLFLOOR:
|
|
case TRFLOOR:
|
|
case TLLINE:
|
|
case TRLINE:
|
|
case TLDLINE:
|
|
case TRDLINE:
|
|
{
|
|
auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return std::unique_ptr<SmNode>(pNode.release());
|
|
}
|
|
default:
|
|
return DoError(SmParseError::UnexpectedToken);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SmOperNode> SmParser5::DoOperator()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(TokenInGroup(TG::Oper));
|
|
|
|
auto xSNode = std::make_unique<SmOperNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
|
|
// get operator
|
|
auto xOperator = DoOper();
|
|
|
|
if (m_aCurToken.nGroup == TG::Limit || m_aCurToken.nGroup == TG::Power)
|
|
xOperator = DoSubSup(m_aCurToken.nGroup, std::move(xOperator));
|
|
|
|
// get argument
|
|
auto xArg = DoPower();
|
|
|
|
xSNode->SetSubNodes(std::move(xOperator), std::move(xArg));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoOper()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
SmTokenType eType(m_aCurToken.eType);
|
|
std::unique_ptr<SmNode> pNode;
|
|
|
|
switch (eType)
|
|
{
|
|
case TSUM:
|
|
case TPROD:
|
|
case TCOPROD:
|
|
case TINT:
|
|
case TINTD:
|
|
case TIINT:
|
|
case TIIINT:
|
|
case TLINT:
|
|
case TLLINT:
|
|
case TLLLINT:
|
|
pNode.reset(new SmMathSymbolNode(m_aCurToken));
|
|
pNode->SetSelection(m_aCurESelection);
|
|
break;
|
|
|
|
case TLIM:
|
|
case TLIMSUP:
|
|
case TLIMINF:
|
|
m_aCurToken.aText
|
|
= eType == TLIMSUP ? u"lim sup" : eType == TLIMINF ? u"lim inf" : u"lim";
|
|
pNode.reset(new SmTextNode(m_aCurToken, FNT_TEXT));
|
|
pNode->SetSelection(m_aCurESelection);
|
|
break;
|
|
|
|
case TOPER:
|
|
NextToken();
|
|
OSL_ENSURE(m_aCurToken.eType == TSPECIAL, "Sm: wrong token");
|
|
m_aCurToken.eType = TOPER;
|
|
pNode.reset(new SmGlyphSpecialNode(m_aCurToken));
|
|
pNode->SetSelection(m_aCurESelection);
|
|
break;
|
|
|
|
default:
|
|
assert(false && "unknown case");
|
|
}
|
|
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoUnOper()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(TokenInGroup(TG::UnOper));
|
|
|
|
SmToken aNodeToken = m_aCurToken;
|
|
ESelection aESelection = m_aCurESelection;
|
|
SmTokenType eType = m_aCurToken.eType;
|
|
bool bIsPostfix = eType == TFACT;
|
|
|
|
std::unique_ptr<SmStructureNode> xSNode;
|
|
std::unique_ptr<SmNode> xOper;
|
|
std::unique_ptr<SmNode> xExtra;
|
|
std::unique_ptr<SmNode> xArg;
|
|
|
|
switch (eType)
|
|
{
|
|
case TABS:
|
|
case TSQRT:
|
|
NextToken();
|
|
break;
|
|
|
|
case TNROOT:
|
|
NextToken();
|
|
xExtra = DoPower();
|
|
break;
|
|
|
|
case TUOPER:
|
|
NextToken();
|
|
//Let the glyph know what it is...
|
|
m_aCurToken.eType = TUOPER;
|
|
m_aCurToken.nGroup = TG::UnOper;
|
|
xOper = DoGlyphSpecial();
|
|
break;
|
|
|
|
case TPLUS:
|
|
case TMINUS:
|
|
case TPLUSMINUS:
|
|
case TMINUSPLUS:
|
|
case TNEG:
|
|
case TFACT:
|
|
xOper = DoOpSubSup();
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
// get argument
|
|
xArg = DoPower();
|
|
|
|
if (eType == TABS)
|
|
{
|
|
xSNode.reset(new SmBraceNode(aNodeToken));
|
|
xSNode->SetSelection(aESelection);
|
|
xSNode->SetScaleMode(SmScaleMode::Height);
|
|
|
|
// build nodes for left & right lines
|
|
// (text, group, level of the used token are of no interest here)
|
|
// we'll use row & column of the keyword for abs
|
|
aNodeToken.eType = TABS;
|
|
|
|
aNodeToken.setChar(MS_VERTLINE);
|
|
std::unique_ptr<SmNode> xLeft(new SmMathSymbolNode(aNodeToken));
|
|
xLeft->SetSelection(aESelection);
|
|
std::unique_ptr<SmNode> xRight(new SmMathSymbolNode(aNodeToken));
|
|
xRight->SetSelection(aESelection);
|
|
|
|
xSNode->SetSubNodes(std::move(xLeft), std::move(xArg), std::move(xRight));
|
|
}
|
|
else if (eType == TSQRT || eType == TNROOT)
|
|
{
|
|
xSNode.reset(new SmRootNode(aNodeToken));
|
|
xSNode->SetSelection(aESelection);
|
|
xOper.reset(new SmRootSymbolNode(aNodeToken));
|
|
xOper->SetSelection(aESelection);
|
|
xSNode->SetSubNodes(std::move(xExtra), std::move(xOper), std::move(xArg));
|
|
}
|
|
else
|
|
{
|
|
xSNode.reset(new SmUnHorNode(aNodeToken));
|
|
xSNode->SetSelection(aESelection);
|
|
if (bIsPostfix)
|
|
xSNode->SetSubNodes(std::move(xArg), std::move(xOper));
|
|
else
|
|
{
|
|
// prefix operator
|
|
xSNode->SetSubNodes(std::move(xOper), std::move(xArg));
|
|
}
|
|
}
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoAttribute()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(TokenInGroup(TG::Attribute));
|
|
|
|
auto xSNode = std::make_unique<SmAttributeNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
std::unique_ptr<SmNode> xAttr;
|
|
SmScaleMode eScaleMode = SmScaleMode::None;
|
|
|
|
// get appropriate node for the attribute itself
|
|
switch (m_aCurToken.eType)
|
|
{
|
|
case TUNDERLINE:
|
|
case TOVERLINE:
|
|
case TOVERSTRIKE:
|
|
xAttr.reset(new SmRectangleNode(m_aCurToken));
|
|
xAttr->SetSelection(m_aCurESelection);
|
|
eScaleMode = SmScaleMode::Width;
|
|
break;
|
|
|
|
case TWIDEVEC:
|
|
case TWIDEHARPOON:
|
|
case TWIDEHAT:
|
|
case TWIDETILDE:
|
|
xAttr.reset(new SmMathSymbolNode(m_aCurToken));
|
|
xAttr->SetSelection(m_aCurESelection);
|
|
eScaleMode = SmScaleMode::Width;
|
|
break;
|
|
|
|
default:
|
|
xAttr.reset(new SmMathSymbolNode(m_aCurToken));
|
|
xAttr->SetSelection(m_aCurESelection);
|
|
}
|
|
|
|
NextToken();
|
|
|
|
xSNode->SetSubNodes(std::move(xAttr), nullptr); // the body will be filled later
|
|
xSNode->SetScaleMode(eScaleMode);
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoFontAttribute()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(TokenInGroup(TG::FontAttr));
|
|
|
|
switch (m_aCurToken.eType)
|
|
{
|
|
case TITALIC:
|
|
case TNITALIC:
|
|
case TBOLD:
|
|
case TNBOLD:
|
|
case TPHANTOM:
|
|
{
|
|
auto pNode = std::make_unique<SmFontNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
|
|
case TSIZE:
|
|
return DoFontSize();
|
|
|
|
case TFONT:
|
|
return DoFont();
|
|
|
|
case TCOLOR:
|
|
return DoColor();
|
|
|
|
default:
|
|
assert(false);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoColor()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(m_aCurToken.eType == TCOLOR);
|
|
sal_Int32 nBufferIndex = m_nBufferIndex;
|
|
NextTokenColor(TCOLOR);
|
|
SmToken aToken;
|
|
ESelection aESelection;
|
|
|
|
if (m_aCurToken.eType == TDVIPSNAMESCOL)
|
|
NextTokenColor(TDVIPSNAMESCOL);
|
|
if (m_aCurToken.eType == TERROR)
|
|
return DoError(SmParseError::ColorExpected);
|
|
if (TokenInGroup(TG::Color))
|
|
{
|
|
aToken = m_aCurToken;
|
|
aESelection = m_aCurESelection;
|
|
if (m_aCurToken.eType == TRGB) //loads r, g and b
|
|
{
|
|
sal_uInt32 nr, ng, nb, nc;
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
nr = m_aCurToken.aText.toUInt32();
|
|
if (nr > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
ng = m_aCurToken.aText.toUInt32();
|
|
if (ng > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
nb = m_aCurToken.aText.toUInt32();
|
|
if (nb > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
nc = nb | ng << 8 | nr << 16 | sal_uInt32(0) << 24;
|
|
aToken.cMathChar = OUString::number(nc, 16);
|
|
}
|
|
else if (m_aCurToken.eType == TRGBA) //loads r, g and b
|
|
{
|
|
sal_uInt32 nr, na, ng, nb, nc;
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
nr = m_aCurToken.aText.toUInt32();
|
|
if (nr > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
ng = m_aCurToken.aText.toUInt32();
|
|
if (ng > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
nb = m_aCurToken.aText.toUInt32();
|
|
if (nb > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
na = m_aCurToken.aText.toUInt32();
|
|
if (na > 255)
|
|
return DoError(SmParseError::ColorExpected);
|
|
nc = nb | ng << 8 | nr << 16 | na << 24;
|
|
aToken.cMathChar = OUString::number(nc, 16);
|
|
}
|
|
else if (m_aCurToken.eType == THEX) //loads hex code
|
|
{
|
|
sal_uInt32 nc;
|
|
NextTokenFontSize();
|
|
if (lcl_IsNotWholeNumber16(m_aCurToken.aText))
|
|
return DoError(SmParseError::ColorExpected);
|
|
nc = m_aCurToken.aText.toUInt32(16);
|
|
aToken.cMathChar = OUString::number(nc, 16);
|
|
}
|
|
aToken.aText = m_aBufferString.subView(nBufferIndex, m_nBufferIndex - nBufferIndex);
|
|
NextToken();
|
|
}
|
|
else
|
|
return DoError(SmParseError::ColorExpected);
|
|
|
|
std::unique_ptr<SmStructureNode> xNode;
|
|
xNode.reset(new SmFontNode(aToken));
|
|
xNode->SetSelection(aESelection);
|
|
return xNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoFont()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(m_aCurToken.eType == TFONT);
|
|
|
|
std::unique_ptr<SmStructureNode> xNode;
|
|
// last font rules, get that one
|
|
SmToken aToken;
|
|
ESelection aESelection = m_aCurESelection;
|
|
do
|
|
{
|
|
NextToken();
|
|
|
|
if (TokenInGroup(TG::Font))
|
|
{
|
|
aToken = m_aCurToken;
|
|
NextToken();
|
|
}
|
|
else
|
|
{
|
|
return DoError(SmParseError::FontExpected);
|
|
}
|
|
} while (m_aCurToken.eType == TFONT);
|
|
|
|
xNode.reset(new SmFontNode(aToken));
|
|
xNode->SetSelection(aESelection);
|
|
return xNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoFontSize()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(m_aCurToken));
|
|
pFontNode->SetSelection(m_aCurESelection);
|
|
NextTokenFontSize();
|
|
FontSizeType Type;
|
|
|
|
switch (m_aCurToken.eType)
|
|
{
|
|
case THEX:
|
|
Type = FontSizeType::ABSOLUT;
|
|
break;
|
|
case TPLUS:
|
|
Type = FontSizeType::PLUS;
|
|
break;
|
|
case TMINUS:
|
|
Type = FontSizeType::MINUS;
|
|
break;
|
|
case TMULTIPLY:
|
|
Type = FontSizeType::MULTIPLY;
|
|
break;
|
|
case TDIVIDEBY:
|
|
Type = FontSizeType::DIVIDE;
|
|
break;
|
|
|
|
default:
|
|
return DoError(SmParseError::SizeExpected);
|
|
}
|
|
|
|
if (Type != FontSizeType::ABSOLUT)
|
|
{
|
|
NextTokenFontSize();
|
|
if (m_aCurToken.eType != THEX)
|
|
return DoError(SmParseError::SizeExpected);
|
|
}
|
|
|
|
// get number argument
|
|
Fraction aValue(1);
|
|
if (lcl_IsNumber(m_aCurToken.aText))
|
|
{
|
|
aValue = m_aCurToken.aText.toDouble();
|
|
//!! Reduce values in order to avoid numerical errors
|
|
if (aValue.GetDenominator() > 1000)
|
|
{
|
|
tools::Long nNum = aValue.GetNumerator();
|
|
tools::Long nDenom = aValue.GetDenominator();
|
|
while (nDenom > 1000) //remove big denominator
|
|
{
|
|
nNum /= 10;
|
|
nDenom /= 10;
|
|
}
|
|
aValue = Fraction(nNum, nDenom);
|
|
}
|
|
}
|
|
else
|
|
return DoError(SmParseError::SizeExpected);
|
|
|
|
pFontNode->SetSizeParameter(aValue, Type);
|
|
NextToken();
|
|
return pFontNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoBrace()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
assert(m_aCurToken.eType == TLEFT || TokenInGroup(TG::LBrace));
|
|
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
std::unique_ptr<SmNode> pBody, pLeft, pRight;
|
|
SmScaleMode eScaleMode = SmScaleMode::None;
|
|
SmParseError eError = SmParseError::None;
|
|
|
|
if (m_aCurToken.eType == TLEFT)
|
|
{
|
|
NextToken();
|
|
|
|
eScaleMode = SmScaleMode::Height;
|
|
|
|
// check for left bracket
|
|
if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
|
|
{
|
|
pLeft.reset(new SmMathSymbolNode(m_aCurToken));
|
|
pLeft->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
pBody = DoBracebody(true);
|
|
|
|
if (m_aCurToken.eType == TRIGHT)
|
|
{
|
|
NextToken();
|
|
|
|
// check for right bracket
|
|
if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
|
|
{
|
|
pRight.reset(new SmMathSymbolNode(m_aCurToken));
|
|
pRight->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
}
|
|
else
|
|
eError = SmParseError::RbraceExpected;
|
|
}
|
|
else
|
|
eError = SmParseError::RightExpected;
|
|
}
|
|
else
|
|
eError = SmParseError::LbraceExpected;
|
|
}
|
|
else
|
|
{
|
|
assert(TokenInGroup(TG::LBrace));
|
|
|
|
pLeft.reset(new SmMathSymbolNode(m_aCurToken));
|
|
pLeft->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
pBody = DoBracebody(false);
|
|
|
|
SmTokenType eExpectedType = TUNKNOWN;
|
|
switch (pLeft->GetToken().eType)
|
|
{
|
|
case TLPARENT:
|
|
eExpectedType = TRPARENT;
|
|
break;
|
|
case TLBRACKET:
|
|
eExpectedType = TRBRACKET;
|
|
break;
|
|
case TLBRACE:
|
|
eExpectedType = TRBRACE;
|
|
break;
|
|
case TLDBRACKET:
|
|
eExpectedType = TRDBRACKET;
|
|
break;
|
|
case TLLINE:
|
|
eExpectedType = TRLINE;
|
|
break;
|
|
case TLDLINE:
|
|
eExpectedType = TRDLINE;
|
|
break;
|
|
case TLANGLE:
|
|
eExpectedType = TRANGLE;
|
|
break;
|
|
case TLFLOOR:
|
|
eExpectedType = TRFLOOR;
|
|
break;
|
|
case TLCEIL:
|
|
eExpectedType = TRCEIL;
|
|
break;
|
|
case TLRLINE:
|
|
eExpectedType = TLRLINE;
|
|
break;
|
|
case TLRDLINE:
|
|
eExpectedType = TLRDLINE;
|
|
break;
|
|
default:
|
|
SAL_WARN("starmath", "unknown case");
|
|
}
|
|
|
|
if (m_aCurToken.eType == eExpectedType)
|
|
{
|
|
pRight.reset(new SmMathSymbolNode(m_aCurToken));
|
|
pRight->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
}
|
|
else
|
|
eError = SmParseError::ParentMismatch;
|
|
}
|
|
|
|
if (eError == SmParseError::None)
|
|
{
|
|
assert(pLeft);
|
|
assert(pRight);
|
|
xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
|
|
xSNode->SetScaleMode(eScaleMode);
|
|
return xSNode;
|
|
}
|
|
return DoError(eError);
|
|
}
|
|
|
|
std::unique_ptr<SmBracebodyNode> SmParser5::DoBracebody(bool bIsLeftRight)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
auto pBody = std::make_unique<SmBracebodyNode>(m_aCurToken);
|
|
pBody->SetSelection(m_aCurESelection);
|
|
|
|
std::vector<std::unique_ptr<SmNode>> aNodes;
|
|
// get body if any
|
|
if (bIsLeftRight)
|
|
{
|
|
do
|
|
{
|
|
if (m_aCurToken.eType == TMLINE)
|
|
{
|
|
SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken);
|
|
pTempNode->SetSelection(m_aCurESelection);
|
|
aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode));
|
|
NextToken();
|
|
}
|
|
else if (m_aCurToken.eType != TRIGHT)
|
|
{
|
|
aNodes.push_back(DoAlign());
|
|
if (m_aCurToken.eType != TMLINE && m_aCurToken.eType != TRIGHT)
|
|
aNodes.emplace_back(DoError(SmParseError::RightExpected));
|
|
}
|
|
} while (m_aCurToken.eType != TEND && m_aCurToken.eType != TRIGHT);
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
if (m_aCurToken.eType == TMLINE)
|
|
{
|
|
SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken);
|
|
pTempNode->SetSelection(m_aCurESelection);
|
|
aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode));
|
|
NextToken();
|
|
}
|
|
else if (!TokenInGroup(TG::RBrace))
|
|
{
|
|
aNodes.push_back(DoAlign());
|
|
if (m_aCurToken.eType != TMLINE && !TokenInGroup(TG::RBrace))
|
|
aNodes.emplace_back(DoError(SmParseError::RbraceExpected));
|
|
}
|
|
} while (m_aCurToken.eType != TEND && !TokenInGroup(TG::RBrace));
|
|
}
|
|
|
|
pBody->SetSubNodes(buildNodeArray(aNodes));
|
|
pBody->SetScaleMode(bIsLeftRight ? SmScaleMode::Height : SmScaleMode::None);
|
|
return pBody;
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::DoEvaluate()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
// Create node
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
SmToken aToken(TRLINE, MS_VERTLINE, "evaluate", TG::RBrace, 5);
|
|
|
|
// Parse body && left none
|
|
NextToken();
|
|
std::unique_ptr<SmNode> pBody = DoPower();
|
|
SmToken bToken(TNONE, '\0', "", TG::LBrace, 5);
|
|
std::unique_ptr<SmNode> pLeft;
|
|
pLeft.reset(new SmMathSymbolNode(bToken));
|
|
|
|
// Mount nodes
|
|
std::unique_ptr<SmNode> pRight;
|
|
pRight.reset(new SmMathSymbolNode(aToken));
|
|
xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
|
|
xSNode->SetScaleMode(SmScaleMode::Height); // scalable line
|
|
|
|
// Parse from to
|
|
if (m_aCurToken.nGroup == TG::Limit)
|
|
{
|
|
std::unique_ptr<SmNode> rSNode;
|
|
rSNode = DoSubSupEvaluate(std::move(xSNode));
|
|
rSNode->GetToken().eType = TEVALUATE;
|
|
return rSNode;
|
|
}
|
|
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmTextNode> SmParser5::DoFunction()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
if (m_aCurToken.eType == TFUNC)
|
|
{
|
|
NextToken(); // skip "FUNC"-statement
|
|
m_aCurToken.eType = TFUNC;
|
|
m_aCurToken.nGroup = TG::Function;
|
|
}
|
|
auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_FUNCTION);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmTableNode> SmParser5::DoBinom()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
auto xSNode = std::make_unique<SmTableNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
|
|
auto xFirst = DoSum();
|
|
auto xSecond = DoSum();
|
|
xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmBinVerNode> SmParser5::DoFrac()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::unique_ptr<SmBinVerNode> xSNode = std::make_unique<SmBinVerNode>(m_aCurToken);
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
std::unique_ptr<SmNode> xOper = std::make_unique<SmRectangleNode>(m_aCurToken);
|
|
xOper->SetSelection(m_aCurESelection);
|
|
|
|
NextToken();
|
|
|
|
auto xFirst = DoSum();
|
|
auto xSecond = DoSum();
|
|
xSNode->SetSubNodes(std::move(xFirst), std::move(xOper), std::move(xSecond));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoStack()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::unique_ptr<SmStructureNode> xSNode(new SmTableNode(m_aCurToken));
|
|
xSNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
if (m_aCurToken.eType != TLGROUP)
|
|
return DoError(SmParseError::LgroupExpected);
|
|
std::vector<std::unique_ptr<SmNode>> aExprArr;
|
|
do
|
|
{
|
|
NextToken();
|
|
aExprArr.push_back(DoAlign());
|
|
} while (m_aCurToken.eType == TPOUND);
|
|
|
|
if (m_aCurToken.eType == TRGROUP)
|
|
NextToken();
|
|
else
|
|
aExprArr.emplace_back(DoError(SmParseError::RgroupExpected));
|
|
|
|
xSNode->SetSubNodes(buildNodeArray(aExprArr));
|
|
return xSNode;
|
|
}
|
|
|
|
std::unique_ptr<SmStructureNode> SmParser5::DoMatrix()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
std::unique_ptr<SmMatrixNode> xMNode(new SmMatrixNode(m_aCurToken));
|
|
xMNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
if (m_aCurToken.eType != TLGROUP)
|
|
return DoError(SmParseError::LgroupExpected);
|
|
|
|
std::vector<std::unique_ptr<SmNode>> aExprArr;
|
|
do
|
|
{
|
|
NextToken();
|
|
aExprArr.push_back(DoAlign());
|
|
} while (m_aCurToken.eType == TPOUND);
|
|
|
|
size_t nCol = aExprArr.size();
|
|
size_t nRow = 1;
|
|
while (m_aCurToken.eType == TDPOUND)
|
|
{
|
|
NextToken();
|
|
for (size_t i = 0; i < nCol; i++)
|
|
{
|
|
auto xNode = DoAlign();
|
|
if (i < (nCol - 1))
|
|
{
|
|
if (m_aCurToken.eType == TPOUND)
|
|
NextToken();
|
|
else
|
|
xNode = DoError(SmParseError::PoundExpected);
|
|
}
|
|
aExprArr.emplace_back(std::move(xNode));
|
|
}
|
|
++nRow;
|
|
}
|
|
|
|
if (m_aCurToken.eType == TRGROUP)
|
|
NextToken();
|
|
else
|
|
{
|
|
std::unique_ptr<SmNode> xENode(DoError(SmParseError::RgroupExpected));
|
|
if (aExprArr.empty())
|
|
nRow = nCol = 1;
|
|
else
|
|
aExprArr.pop_back();
|
|
aExprArr.emplace_back(std::move(xENode));
|
|
}
|
|
|
|
xMNode->SetSubNodes(buildNodeArray(aExprArr));
|
|
xMNode->SetRowCol(static_cast<sal_uInt16>(nRow), static_cast<sal_uInt16>(nCol));
|
|
return std::unique_ptr<SmStructureNode>(xMNode.release());
|
|
}
|
|
|
|
std::unique_ptr<SmSpecialNode> SmParser5::DoSpecial()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
bool bReplace = false;
|
|
OUString& rName = m_aCurToken.aText;
|
|
OUString aNewName;
|
|
|
|
// conversion of symbol names for 6.0 (XML) file format
|
|
// (name change on import / export.
|
|
// UI uses localized names XML file format does not.)
|
|
if (rName.startsWith("%"))
|
|
{
|
|
if (IsImportSymbolNames())
|
|
{
|
|
aNewName = SmLocalizedSymbolData::GetUiSymbolName(rName.subView(1));
|
|
bReplace = true;
|
|
}
|
|
else if (IsExportSymbolNames())
|
|
{
|
|
aNewName = SmLocalizedSymbolData::GetExportSymbolName(rName.subView(1));
|
|
bReplace = true;
|
|
}
|
|
}
|
|
if (!aNewName.isEmpty())
|
|
aNewName = "%" + aNewName;
|
|
|
|
if (bReplace && !aNewName.isEmpty() && rName != aNewName)
|
|
{
|
|
Replace(GetTokenIndex(), rName.getLength(), aNewName);
|
|
rName = aNewName;
|
|
}
|
|
|
|
// add symbol name to list of used symbols
|
|
const OUString aSymbolName(m_aCurToken.aText.copy(1));
|
|
if (!aSymbolName.isEmpty())
|
|
m_aUsedSymbols.insert(aSymbolName);
|
|
|
|
auto pNode = std::make_unique<SmSpecialNode>(m_aCurToken);
|
|
pNode->SetSelection(m_aCurESelection);
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmGlyphSpecialNode> SmParser5::DoGlyphSpecial()
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
auto pNode = std::make_unique<SmGlyphSpecialNode>(m_aCurToken);
|
|
NextToken();
|
|
return pNode;
|
|
}
|
|
|
|
std::unique_ptr<SmExpressionNode> SmParser5::DoError(SmParseError eError)
|
|
{
|
|
DepthProtect aDepthGuard(m_nParseDepth);
|
|
|
|
// Identify error message
|
|
OUString sStrBuf(SmResId(RID_ERR_IDENT) + starmathdatabase::getParseErrorDesc(eError));
|
|
|
|
// Generate error node
|
|
m_aCurToken.eType = TERROR;
|
|
m_aCurToken.cMathChar = sStrBuf;
|
|
auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
|
|
SmErrorNode* pErr(new SmErrorNode(m_aCurToken));
|
|
pErr->SetSelection(m_aCurESelection);
|
|
xSNode->SetSubNode(0, pErr);
|
|
|
|
// Append error to the error list
|
|
SmErrorDesc aErrDesc(eError, xSNode.get(), m_aCurToken.cMathChar);
|
|
m_aErrDescList.push_back(aErrDesc);
|
|
|
|
NextToken();
|
|
|
|
return xSNode;
|
|
}
|
|
|
|
// end grammar
|
|
|
|
SmParser5::SmParser5()
|
|
: m_nCurError(0)
|
|
, m_nBufferIndex(0)
|
|
, m_nTokenIndex(0)
|
|
, m_nRow(0)
|
|
, m_nColOff(0)
|
|
, m_bImportSymNames(false)
|
|
, m_bExportSymNames(false)
|
|
, m_nParseDepth(0)
|
|
, m_aNumCC(LanguageTag(LANGUAGE_ENGLISH_US))
|
|
, m_pSysCC(&SM_MOD()->GetSysLocale().GetCharClass())
|
|
{
|
|
}
|
|
|
|
SmParser5::~SmParser5() {}
|
|
|
|
std::unique_ptr<SmTableNode> SmParser5::Parse(const OUString& rBuffer)
|
|
{
|
|
m_aUsedSymbols.clear();
|
|
|
|
m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
|
|
m_nBufferIndex = 0;
|
|
m_nTokenIndex = 0;
|
|
m_nRow = 0;
|
|
m_nColOff = 0;
|
|
m_nCurError = -1;
|
|
|
|
m_aErrDescList.clear();
|
|
|
|
NextToken();
|
|
return DoTable();
|
|
}
|
|
|
|
std::unique_ptr<SmNode> SmParser5::ParseExpression(const OUString& rBuffer)
|
|
{
|
|
m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
|
|
m_nBufferIndex = 0;
|
|
m_nTokenIndex = 0;
|
|
m_nRow = 0;
|
|
m_nColOff = 0;
|
|
m_nCurError = -1;
|
|
|
|
m_aErrDescList.clear();
|
|
|
|
NextToken();
|
|
return DoExpression();
|
|
}
|
|
|
|
const SmErrorDesc* SmParser5::NextError()
|
|
{
|
|
if (!m_aErrDescList.empty())
|
|
if (m_nCurError > 0)
|
|
return &m_aErrDescList[--m_nCurError];
|
|
else
|
|
{
|
|
m_nCurError = 0;
|
|
return &m_aErrDescList[m_nCurError];
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
const SmErrorDesc* SmParser5::PrevError()
|
|
{
|
|
if (!m_aErrDescList.empty())
|
|
if (m_nCurError < static_cast<int>(m_aErrDescList.size() - 1))
|
|
return &m_aErrDescList[++m_nCurError];
|
|
else
|
|
{
|
|
m_nCurError = static_cast<int>(m_aErrDescList.size() - 1);
|
|
return &m_aErrDescList[m_nCurError];
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
const SmErrorDesc* SmParser5::GetError() const
|
|
{
|
|
if (m_aErrDescList.empty())
|
|
return nullptr;
|
|
return &m_aErrDescList.front();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|