home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / contrib / src / applet / prepifelse.cpp < prev    next >
C/C++ Source or Header  |  2002-02-07  |  15KB  |  442 lines

  1. /****************************************************************************
  2. *
  3. *                       wxWindows HTML Applet Package
  4. *
  5. *               Copyright (C) 1991-2001 SciTech Software, Inc.
  6. *                            All rights reserved.
  7. *
  8. *  ========================================================================
  9. *
  10. *    The contents of this file are subject to the wxWindows License
  11. *    Version 3.0 (the "License"); you may not use this file except in
  12. *    compliance with the License. You may obtain a copy of the License at
  13. *    http://www.wxwindows.org/licence3.txt
  14. *
  15. *    Software distributed under the License is distributed on an
  16. *    "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  17. *    implied. See the License for the specific language governing
  18. *    rights and limitations under the License.
  19. *
  20. *  ========================================================================
  21. *
  22. * Language:        ANSI C++
  23. * Environment:    Any
  24. *
  25. * Description:  This file is the implementation of the Preprocessor object
  26. *               for parsing the <!--#if directive
  27. *
  28. ****************************************************************************/
  29.  
  30. // Include private headers
  31. #include "wx/applet/prepifelse.h"
  32. #include "wx/applet/ifelsevar.h"
  33. #include "wx/applet/echovar.h"
  34. #include "wx/string.h"
  35.  
  36. // Force link macro
  37. #include "wx/html/forcelnk.h"
  38. // wxWindows
  39. #include "wx/msgdlg.h"
  40.  
  41. /*----------------------------- Implementation ----------------------------*/
  42.  
  43. /* {SECRET} */
  44. /****************************************************************************
  45. REMARKS:
  46. None of the Reverse Find functions in wxWindows appear to work in a way that
  47. can be used by our code. This includes the libstr rfind implementations which
  48. do not correctly pass the given return value.
  49. ****************************************************************************/
  50. int ReverseFind(
  51.     const wxString &tstr,
  52.     const wxString &str,
  53.     int start = -1)
  54. {
  55.     wxASSERT( str.GetStringData()->IsValid() );
  56.  
  57.     // TODO could be made much quicker than that
  58.     int p = tstr.Len()-str.Len()-1;
  59.     int p2 = start-str.Len();
  60.  
  61.     // if the user supplied a valid start point, use it
  62.     if (start != -1 && p > p2) p = p2;
  63.     while ( p >= 0 ) {
  64.         if ( wxStrncmp(tstr.c_str() + p, str.c_str(), str.Len()) == 0 )
  65.             return p;
  66.         p--;
  67.         }
  68.     return -1;
  69. }
  70.  
  71. /* {SECRET} */
  72. /****************************************************************************
  73. REMARKS:
  74. tells if a character is a letter.
  75. replace this when wxWindows gets regex library. (without strange licensing
  76. restrictions)
  77. ****************************************************************************/
  78. bool IsLetter(
  79.     char c, bool acceptspace = false)
  80. {
  81.     if (acceptspace && (c == ' ')) return true;
  82.     if (c >= '0' && c <= '9') return true;
  83.     return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '\"' || c == '\'' );
  84. }
  85.  
  86. #define IsQuote(c) (c == '\'' || c == '\"')
  87.  
  88. /* {SECRET} */
  89. /****************************************************************************
  90. REMARKS:
  91. tells if a character is a letter.
  92. replace this when wxWindows gets regex library. (without strange licensing
  93. restrictions)
  94. ****************************************************************************/
  95. wxString GetEquals(
  96.     wxString var,
  97.     wxString value)
  98. {
  99.     if (!wxEchoVariable::Exists(var)) {
  100.         // TODO: when we implement the set variable, check for a set variable as well
  101.         #ifdef CHECKED        
  102.             wxMessageBox(wxString("wxHTML #if\\else error: Variable ") + var + wxString(" not found."),"Error",wxICON_ERROR);
  103.         #endif
  104.         return wxString("0"); // false
  105.         }
  106.  
  107.     wxString tmp = wxEchoVariable::GetValue(var);
  108.  
  109.     if (IsQuote( value.GetChar(0) ))
  110.         value = value.Mid(1);
  111.     if (IsQuote(value.GetChar(value.Length()-1)))
  112.         value = value.Mid(0,value.Length()-1);
  113.  
  114.     if (tmp.CmpNoCase(value) == 0) return wxString("1");
  115.     return wxString("0");
  116. }
  117.  
  118. /****************************************************************************
  119. PARAMETERS:
  120. str        - text of #if statement
  121.  
  122. RETURNS:
  123. true or false depending on how it evaluated
  124.  
  125. REMARKS:
  126. TODO: rewrite this whole thing using regular expressions when they are done.
  127.  
  128. SEE ALSO:
  129. wxIfElseVariable
  130. ****************************************************************************/
  131. bool ParseIfStatementValue(
  132.     wxString &str)
  133. {
  134.     // Find out if the tag has parenthesis
  135.     // recursive to parse the text within the parenthesis,
  136.     // replacing the text with 1 or 0, (hardcoded true or false)
  137.     int b;
  138.     while ((b = str.Find('(')) != -1) {
  139.         int e;
  140.         // Find the matching parenthesis
  141.         int nextbeg, nextend;
  142.         int parencount = 1, min = b+1;
  143.         do {
  144.             nextbeg = str.find('(', min);
  145.             nextend = str.find(')', min);
  146.             if (nextbeg < nextend && nextbeg != wxString::npos) {
  147.                 parencount++;
  148.                 min = nextbeg+1;
  149.                 }
  150.             else {
  151.                 parencount--;
  152.                 min = nextend+1;
  153.                 }
  154.  
  155.             if (nextend == wxString::npos) {
  156. #ifdef CHECKED        
  157.                 wxMessageBox("wxHTML #if\\else error: Unmatched parenthesis in #if expression.","Error",wxICON_ERROR);
  158. #endif
  159.                 return true;
  160.                 }
  161.             // once parencount reaches 0 again we have found our matchin )
  162.             } while (parencount > 0);
  163.  
  164.         e = nextend;
  165.  
  166.         // Extract the expression from the parenthesis block and recurse
  167.         // to solve it.
  168.         wxString tag;
  169.         tag = str.Mid(b+1, e-b-1);
  170.         bool val = ParseIfStatementValue(tag);
  171.         // Add extra spaces just in case of NOT(VAL)
  172.         if (val) str = str.Mid(0, b) + " 1" + str.Mid(e+1);
  173.         else str = str.Mid(0, b) + " 0" + str.Mid(e+1);
  174.         }
  175.  
  176.     // Remove spaces from left and right
  177.     str.Trim(false);
  178.     str.Trim(true);
  179.  
  180.     // Convert text method of operators "AND" and "OR" to c style
  181.     // this makes only one special case necessary for each later on
  182.     str.Replace(" AND ", "&&");
  183.     str.Replace(" OR ", "||");
  184.     str.Replace(" EQUALS ", "==");
  185.  
  186.     // Check for equals statements
  187.     // == statements are special because they are evaluated as a single block
  188.     int equ;
  189.     equ = str.find("==");
  190.     while (equ != wxString::npos) {
  191.         int begin, end;
  192.         int begin2, end2; // ends of words
  193.         begin = equ-1;
  194.         end = equ+2;
  195.  
  196.         // remove spaces, find extents
  197.         while (end < str.Length() && str.GetChar(end) == ' ')
  198.             end++;
  199.         while (begin >= 0 && str.GetChar(begin) == ' ')
  200.             begin--;
  201.         end2 = end;
  202.         begin2 = begin;
  203.         if (str.GetChar(end2) == '\'' || str.GetChar(end2) == '\"') {
  204.             end2++;
  205.             while (end2 < str.Length() && str.GetChar(end2) != '\'' && str.GetChar(end2) != '\"' )
  206.                 end2++;
  207.             end2++;
  208.             }
  209.         else {
  210.             while (end2 < str.Length() && IsLetter(str.GetChar(end2)))
  211.                 end2++;
  212.             }
  213.         while (begin >= 0 && IsLetter(str.GetChar(begin)))
  214.             begin--;
  215.  
  216.         if (begin < 0) begin = 0;
  217.         else begin++;
  218.         if (end2 >= str.Length()) end2 = str.Length();
  219.  
  220.         wxString tmpeq = GetEquals(str.Mid(begin, begin2-begin+1), str.Mid(end, end2-end));
  221.         str = str.Mid(0, begin) + wxString(" ") + tmpeq + wxString(" ") +
  222.             str.Mid(end2);
  223.         equ = str.find("==");
  224.  
  225.         // Remove spaces from left and right
  226.         str.Trim(false);
  227.         str.Trim(true);
  228.         }
  229.  
  230.     // We use ReverseFind so that the whole left expression gets evaluated agains
  231.     // the right single item, creating a left -> right evaluation
  232.     // Search for || operators, recurse to solve (so we don't have to handle special cases here)
  233.     int and, or;
  234.     and = ReverseFind(str, "&&");
  235.     or = ReverseFind(str, "||");
  236.     if ( (and != -1) || (or != -1) ) {
  237.         wxString tag1, tag2;
  238.         // handle the rightmost first to force left->right evaluation
  239.         if ( (and > or) ) {
  240.             return (
  241.                 ParseIfStatementValue(tag2 = str.Mid(and+2)) &&
  242.                 ParseIfStatementValue(tag1 = str.Mid(0, and)) );
  243.             }
  244.         else {
  245.             return (
  246.                 ParseIfStatementValue(tag2 = str.Mid(or+2)) ||
  247.                 ParseIfStatementValue(tag1 = str.Mid(0, or)) );
  248.             }
  249.         }
  250.  
  251.     // By the time we get to this place in the function we are guarenteed to have a single
  252.     // variable operation, perhaps with a NOT or ! operator
  253.     bool notval = false;
  254.  
  255.     // search for a NOT or ! operator
  256.     if (str.Mid(0, 1) == "!") {
  257.         str.Remove(0, 1);
  258.         str.Trim(false); // trim spaces from left
  259.         notval = true;
  260.         }
  261.     else if (str.Mid(0,4).CmpNoCase("NOT ") == 0) {
  262.         str.Remove(0, 4);
  263.         str.Trim(false); // trim any extra spaces from left
  264.         notval = true;
  265.         }
  266.  
  267.     // now all we have left is the name of the class or a hardcoded 0 or 1
  268.     if (str == "") {
  269. #ifdef CHECKED        
  270.         wxMessageBox("wxHTML #if\\else error: Empty expression in #if\\#elif statement.","Error",wxICON_ERROR);
  271. #endif
  272.         return true;
  273.         }
  274.  
  275.     // check for hardcoded 0 and 1 cases, (these are used by parenthesis catcher)
  276.     // this just decomplicates the recursion algorithm
  277.     if (str == "0") return notval;
  278.     if (str == "1") return !notval;
  279.  
  280.     // Grab the value from the variable class identified by cname
  281.     bool value = wxIfElseVariable::GetValue(str);
  282.     if (notval) value = !value;
  283.     return value;
  284.  
  285. }
  286. /****************************************************************************
  287. PARAMETERS:
  288. text        - HTML to process for if/else blocks
  289.  
  290. RETURNS:
  291. The string containing the processed HTML
  292.  
  293. REMARKS:
  294. This function replaces #if, #else, #elif, and #endif directives with the text
  295. contained within the blocks, dependant on the value of the given boolean
  296. variable. The variable is created by making a sub class of wxIfElseVariable.
  297. Dynamic class construction is used at run time internally to create an instance
  298. of this class and access the value of the variable.
  299.  
  300. SEE ALSO:
  301. wxIfElseVariable
  302. ****************************************************************************/
  303. wxString wxIfElsePrep::Process(
  304.     const wxString& text) const
  305. {
  306.     int b;
  307.     char ft[] = "<!--#if ";
  308.     char ftend[] = "<!--#endif-->";
  309.     char ftelse[] = "<!--#else-->";
  310.     char ftnot[] = "<!--#if not ";
  311.     char ftnot2[] = "<!--#if !";
  312.     char ftelif[] = "<!--#elif ";
  313.     
  314.     // make a copy so we can replace text as we go without affecting the original
  315.     wxString output = text;
  316.  
  317.     // Avoid duplication of our parsing code by turning any #elif blocks into appropriate
  318.     // else/if blocks
  319.     while ((b = ReverseFind(output.Lower(), ftelif)) != -1) {
  320.         int e;
  321.         // Replace beginning of block
  322.         e = output.find("-->", b + strlen(ftelif));
  323.  
  324.         if (e == wxString::npos) {
  325. #ifdef CHECKED        
  326.             wxMessageBox("wxHTML #elif error: Premature end of file while parsing #elif.","Error",wxICON_ERROR);
  327. #endif
  328.             break;
  329.             }
  330.  
  331.         // Convert to lower case so find is easy, grab everything after #elif tag
  332.         wxString remains = (output.Mid(e+strlen("-->"))).Lower();
  333.  
  334.         // find matching else or endif
  335.         int nextif, nextendif;
  336.         int ifcount = 1, min = 0;
  337.         do {
  338.             nextif = remains.find(ft, min);
  339.             nextendif = remains.find(ftend, min);
  340.             if (nextif < nextendif && nextif != wxString::npos) {
  341.                 ifcount++;
  342.                 min = nextif+1;
  343.                 }
  344.             else {
  345.                 ifcount--;
  346.                 min = nextendif+1;
  347.                 }
  348.  
  349.             if (nextendif == wxString::npos) {
  350. #ifdef CHECKED        
  351.                 wxMessageBox("wxHTML #elif error: Premature end of file before finding #endif.","Error",wxICON_ERROR);
  352. #endif
  353.                 break;
  354.                 }
  355.             // once ifcount reaches 0 again we have found our matchin #endif
  356.             } while (ifcount > 0);
  357.  
  358.         // If it couldn't be found die gracefully
  359.         if (nextendif == wxString::npos) {
  360.             // We already displayed a message, just break all the way out
  361.             break;
  362.             }
  363.  
  364.         int elifsize = e - (b + strlen(ftelif)) + strlen("-->");
  365.         // Create the #if/else block, removing the #elif code
  366.         output = output.Mid(0, b) +
  367.             wxString(wxString(ftelse)+wxString(ft)) +
  368.             output.Mid(b+strlen(ftelif), elifsize+nextendif) +
  369.             wxString(ftend) +
  370.             output.Mid(b+strlen(ftelif)+elifsize+nextendif);
  371.         }
  372.     
  373.     // Parse out the if else blocks themselves
  374.     while ((b = ReverseFind(output.Lower(), ft)) != -1) {
  375.         // Loop until every #if directive is found
  376.         // We search from the end of the string so that #if statements will properly recurse
  377.         // and we avoid the hassle of matching statements with the correct <!--#endif-->
  378.         bool notval = false;
  379.         int off = 0;
  380.         int end;
  381.         wxString usecode, code;
  382.         wxString cname;
  383.         wxString tag;
  384.         bool value;
  385.  
  386.         code = wxString("");
  387.  
  388.         // grab the tag and get the name of the variable
  389.         end = (output.Mid(b)).Find("-->");
  390.         if (end == -1) {
  391. #ifdef CHECKED        
  392.             wxMessageBox("wxHTML #if error: Premature end of file while parsing #if.","Error",wxICON_ERROR);
  393. #endif
  394.             break;
  395.             }
  396.  
  397.         end += 3;
  398.         // remove the <!--#if and --> sections from the tag before passing it on to be parsed
  399.         tag = output.Mid(b+strlen(ft), end-strlen(ft)-3);
  400.         output.Remove(b, end);
  401.  
  402.         value = ParseIfStatementValue(tag);
  403.  
  404.         // Find the end of the tag (<!--#endif-->) and copy it all into the variable code
  405.         end = ((output.Mid(b)).Lower()).Find(ftend);
  406.         if (end == -1) {
  407. #ifdef CHECKED        
  408.             wxMessageBox("wxHTML #if error: Premature end of file while searching for matching #endif.","Error",wxICON_ERROR);
  409. #endif
  410.             break;
  411.             }
  412.  
  413.         code = output.Mid(b, end);
  414.         output.Remove(b, end+strlen(ftend)); // remove the entire #if block from original document
  415.  
  416.         // Find out if there is an else statement
  417.         end = (code.Lower()).Find(ftelse);
  418.         if (end != -1) {
  419.             if (!value) {
  420.                 // Use the else statement
  421.                 usecode = code.Mid(end+strlen(ftelse));
  422.                 }
  423.             else {
  424.                 // Use statement before #else
  425.                 usecode = code.Mid(0, end);
  426.                 }
  427.             }
  428.         else if (value) {
  429.             // There is no #else statement
  430.             usecode = code;
  431.             }
  432.  
  433.         if (usecode != wxString(""))
  434.             output = (output.Mid(0,b) + usecode + output.Mid(b));
  435.         }
  436.  
  437.     return output;
  438. }
  439.  
  440. FORCE_LINK(ifelsevar)
  441.                 
  442.