tdf#83779: convert TRUE/FALSE constants to functions TRUE()/FALSE()

This avoids problems with round-tripping Excel spreadsheets, where
previously a formula like =IF(ISNA(A1)=FALSE;"a";"b") was imported
as =IF(ISNA(A1)=0;"a";"b"), and when exported back, it didn't work
in Excel, because boolean values had a distinct type in it.

Change-Id: I672a631bfa1a4811349794f714293404c6b24381
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/86238
Tested-by: Jenkins
Reviewed-by: Eike Rathke <erack@redhat.com>
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index eca794a..e604551 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -31,6 +31,7 @@
#include <rtl/ustrbuf.hxx>
#include <com/sun/star/sheet/ExternalLinkInfo.hpp>
#include <com/sun/star/i18n/ParseResult.hpp>
#include <queue>
#include <vector>
#include <memory>
#include <set>
@@ -282,6 +283,8 @@
    sal_Int32   nSrcPos;                            // tokenizer position (source code)
    mutable ScRawToken maRawToken;

    std::queue<OpCode> maPendingOpCodes; // additional opcodes generated from a single symbol

    const CharClass*    pCharClass;         // which character classification is used for parseAnyToken
    sal_uInt16      mnPredetectedReference;     // reference when reading ODF, 0 (none), 1 (single) or 2 (double)
    sal_Int32   mnRangeOpPosInSymbol;       // if and where a range operator is in symbol
diff --git a/sc/qa/unit/data/xlsx/tdf83779.xlsx b/sc/qa/unit/data/xlsx/tdf83779.xlsx
new file mode 100644
index 0000000..0a5d645
--- /dev/null
+++ b/sc/qa/unit/data/xlsx/tdf83779.xlsx
Binary files differ
diff --git a/sc/qa/unit/subsequent_export-test.cxx b/sc/qa/unit/subsequent_export-test.cxx
index 1fe48fb4..5dad9a3 100644
--- a/sc/qa/unit/subsequent_export-test.cxx
+++ b/sc/qa/unit/subsequent_export-test.cxx
@@ -232,6 +232,7 @@
    void testRotatedImageODS();
    void testTdf128976();
    void testTdf120502();
    void testTdf83779();

    CPPUNIT_TEST_SUITE(ScExportTest);
    CPPUNIT_TEST(test);
@@ -364,6 +365,7 @@
    CPPUNIT_TEST(testRotatedImageODS);
    CPPUNIT_TEST(testTdf128976);
    CPPUNIT_TEST(testTdf120502);
    CPPUNIT_TEST(testTdf83779);

    CPPUNIT_TEST_SUITE_END();

@@ -4706,6 +4708,24 @@
    assertXPath(pSheet1, "/x:worksheet/x:cols/x:col", "max", OUString::number(nMaxCol + 1));
}

void ScExportTest::testTdf83779()
{
    // Roundtripping TRUE/FALSE constants (not functions) must convert them to functions
    ScDocShellRef xShell = loadDoc("tdf83779.", FORMAT_XLSX);
    CPPUNIT_ASSERT(xShell);

    auto pXPathFile = ScBootstrapFixture::exportTo(&(*xShell), FORMAT_XLSX);

    const xmlDocPtr pVmlDrawing
        = XPathHelper::parseExport(pXPathFile, m_xSFactory, "xl/worksheets/sheet1.xml");
    CPPUNIT_ASSERT(pVmlDrawing);

    assertXPathContent(pVmlDrawing, "/x:worksheet/x:sheetData/x:row[1]/x:c/x:f", "FALSE()");
    assertXPathContent(pVmlDrawing, "/x:worksheet/x:sheetData/x:row[2]/x:c/x:f", "TRUE()");

    xShell->DoClose();
}

CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest);

CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 769790d..66ce3d5 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -3013,14 +3013,17 @@
                return false;   // some function name, not a constant

            // Could be TRUE or FALSE constant.
            OpCode eOpFunc = ocNone;
            if (rSym.equalsIgnoreAsciiCase("TRUE"))
                eOpFunc = ocTrue;
            else if (rSym.equalsIgnoreAsciiCase("FALSE"))
                eOpFunc = ocFalse;
            if (eOpFunc != ocNone)
            {
                maRawToken.SetDouble( 1.0 );
                return true;
            }
            if (rSym.equalsIgnoreAsciiCase("FALSE"))
            {
                maRawToken.SetDouble( 0.0 );
                maRawToken.SetOpCode(eOpFunc);
                // add missing trailing parentheses
                maPendingOpCodes.push(ocOpen);
                maPendingOpCodes.push(ocClose);
                return true;
            }
            return false;
@@ -4168,6 +4171,13 @@

bool ScCompiler::NextNewToken( bool bInArray )
{
    if (!maPendingOpCodes.empty())
    {
        maRawToken.SetOpCode(maPendingOpCodes.front());
        maPendingOpCodes.pop();
        return true;
    }

    bool bAllowBooleans = bInArray;
    sal_Int32 nSpaces = NextSymbol(bInArray);