home *** CD-ROM | disk | FTP | other *** search
/ Amiga Times / AmigaTimes.iso / programme / GoldED / developer / examples / syntax / example_context / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-10-06  |  20.0 KB  |  782 lines

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  C comment parser ©1996 Dietmar Eilert
  4.  
  5.  GoldED syntax parser example library. This is a context scanner highlighting
  6.  ANSI C comments and C++ comments. This example doesn't use a memory consuming
  7.  syntax cache, ie. results of a prior parser pass are not stored. However, the
  8.  scanner will remember (cache) whether a line has been parsed and whether parts
  9.  of a comment have been detected within the line using flag bits stored in the
  10.  line's UserData slot.
  11.  
  12.  Dice:
  13.  
  14.  DMAKE
  15.  
  16.  -------------------------------------------------------------------------------
  17.  
  18. */
  19.  
  20. #include "defs.h"
  21.  
  22. /// "Header stuff"
  23.  
  24. // Buffer handles are allocated for each text buffer to keep track of ressources:
  25.  
  26. struct BufferHandle {
  27.  
  28.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  29.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  30.     struct SyntaxChunk    *bh_SyntaxStack;           // parser output
  31.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  32. };
  33.  
  34. // these flag bits (attached to text lines) describe the line's properties
  35.  
  36. #define COMMENT_NONE      (1L<<0)                    //  1 no comment in this line
  37. #define COMMENT_SIMPLE    (1L<<1)                    //  2 there is a comment in this line
  38. #define COMMENT_START     (1L<<2)                    //  4 line is start of multi-line comment
  39. #define COMMENT_BODY      (1L<<3)                    //  8 line is body  of multi-line comment
  40. #define COMMENT_END       (1L<<4)                    // 16 line is end   of multi-line comment
  41.  
  42. ///
  43. /// "Prototype"
  44.  
  45. // library functions
  46.  
  47. Prototype LibCall struct ParserData     *MountScanner(void);
  48. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  49. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  50. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  51. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  52. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  53. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  54. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  55. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  56.  
  57. // private functions
  58.  
  59. Prototype struct LineNode       *FindContextStart(ULONG, struct LineNode *);
  60. Prototype void                   ParseContext(struct LineNode *, ULONG);
  61. Prototype ULONG                  ParseContextLine(struct LineNode *, ULONG);
  62. Prototype struct SyntaxChunk    *ParseString(UBYTE *, UWORD, ULONG, ULONG);
  63. Prototype BOOL                   BadSuccessor(ULONG, ULONG);
  64. Prototype struct RefreshRequest *VerifyContext(ULONG, ULONG, ULONG);
  65.  
  66. ///
  67. /// "Library functions"
  68.  
  69. /* ------------------------------- MountScanner --------------------------------
  70.  
  71.  Called by the editor before first usage of a scanner. Return a description of
  72.  our abilities.
  73.  
  74. */
  75.  
  76. LibCall struct ParserData *
  77. MountScanner()
  78. {
  79.     static UBYTE version[] = "$VER: Comments 2.2 (" __COMMODORE_DATE__ ")";
  80.  
  81.     static struct ParserData parserData;
  82.  
  83.     // syntax elements understood by parser
  84.  
  85.     static UBYTE *levelNames[] = {
  86.  
  87.         "Standard text",
  88.         "ANSI Comment",
  89.         "C++  Comment",
  90.         NULL
  91.     };
  92.  
  93.     static UBYTE *example[] = {
  94.  
  95.         "/* Simple ANSI C example */    ",
  96.         "                               ",
  97.         "#include <defs.h>              ",
  98.         "                               ",
  99.         "int                            ",
  100.         "main(int argc, char **argv)    ",
  101.         "{                              ",
  102.         "   // anybody at home ?        ",
  103.         "                               ",
  104.         "   puts(\"Hello world !\");    ",
  105.         "}                              ",
  106.  
  107.         NULL
  108.     };
  109.  
  110.     // color suggestions
  111.  
  112.     static ULONG levelColors[] = {
  113.  
  114.         MAKE_RGB4(0,  0,   0),
  115.         MAKE_RGB4(15, 15, 15),
  116.         MAKE_RGB4(15, 15,  0)
  117.     };
  118.  
  119.     parserData.pd_Release  = SCANLIBVERSION;
  120.     parserData.pd_Version  = 1;
  121.     parserData.pd_Serial   = 0;
  122.     parserData.pd_Info     = "Comments 2.2";
  123.     parserData.pd_Levels   = 3;
  124.     parserData.pd_Names    = levelNames;
  125.     parserData.pd_Colors   = levelColors;
  126.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
  127.     parserData.pd_Example  = example;
  128.  
  129.     return(&parserData);
  130. }
  131.  
  132.  
  133. /* ------------------------------- StartScanner --------------------------------
  134.  
  135.  Called by the editor after a new text buffer has been created. We allocate a
  136.  buffer to hold text-specific data. The buffer address is returned as handle.
  137.  Additionally, we preparse the unfolded text.
  138.  
  139. */
  140.  
  141. LibCall ULONG
  142. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxstack)
  143. {
  144.     struct BufferHandle *handle;
  145.  
  146.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  147.  
  148.         handle->bh_GlobalConfig = globalConfigPtr;
  149.         handle->bh_EditConfig   = editConfigPtr;
  150.         handle->bh_SyntaxStack  = syntaxstack;
  151.     }
  152.  
  153.     return((ULONG)handle);
  154. }
  155.  
  156.  
  157. /* ------------------------------- CloseScanner --------------------------------
  158.  
  159.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  160.  specific 'global' data.
  161.  
  162. */
  163.  
  164. LibCall ULONG
  165. CloseScanner(__D0 ULONG scanID)
  166. {
  167.     if (scanID) {
  168.  
  169.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  170.  
  171.         FreeVec(handle);
  172.     }
  173.  
  174.     return(0);
  175. }
  176.  
  177.  
  178. /* ------------------------------- FlushScanner --------------------------------
  179.  
  180.  Called by the editor in low memory situations: we are supposed to free as much
  181.  memory as possible. This scanner doesn't use a memory consuming syntax cache,
  182.  there is nothing to be freed.
  183.  
  184. */
  185.  
  186. LibCall void
  187. FlushScanner(__D0 ULONG scanID)
  188. {
  189.     ;
  190. }
  191.  
  192.  
  193. /* ------------------------------- SetupScanner --------------------------------
  194.  
  195.  Called by the editor if the user wants to change the scanner's configuration.
  196.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  197.  unset).
  198.  
  199. */
  200.  
  201. LibCall void
  202. SetupScanner(__A0 globalConfigPtr)
  203. {
  204.     ;
  205. }
  206.  
  207.  
  208. /* ------------------------------- BriefScanner --------------------------------
  209.  
  210.  Called to notify a context scanner if lines have been added, deleted or
  211.  modified. We are supposed to return an additional display request or NULL
  212.  (the editor will refresh the damaged region only if we return NULL). Damaged
  213.  lines have already been unparsed by the editor. This is what we have to do:
  214.  
  215.  a) lines have been deleted or modified
  216.  
  217.     Check whether the syntax scheme of the remaining lines is still valid (it
  218.     will be invalid if say a comment start has been deleted but the comment
  219.     end has not been deleted). Reparse the lines and return a refresh request
  220.     if not.
  221.  
  222.  b) lines have been inserted
  223.  
  224.     Check whether the syntax sheme of the new lines affects the existing lines.
  225.     Reparse all lines starting at the insertion point and return a refresh
  226.     request if not.
  227.  
  228.  c) lines have been (un)folded
  229.  
  230.     This scanner assumes that folding doesn't affect syntax highlighting for the
  231.     sake of simplicity. This isn't necessarily a valid assumption: we will run
  232.     into touble if the user folds parts of a comment only (e.g. the first line).
  233.  
  234. */
  235.  
  236. LibCall struct RefreshRequest *
  237. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  238. {
  239.     struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  240.  
  241.     switch (notify->sn_Class) {
  242.  
  243.         case SCANNER_NOTIFY_MODIFIED:
  244.  
  245.             ULONG line = notify->sn_Line;
  246.  
  247.             if (notify->sn_Removed > notify->sn_Lines) {
  248.  
  249.                 // lines have been deleted
  250.  
  251.                 return(VerifyContext(scanID, line, 0));
  252.             }
  253.             else if (notify->sn_Lines)
  254.  
  255.                 // lines have been modified or inserted
  256.  
  257.                 return(VerifyContext(scanID, line, notify->sn_Lines));
  258.             else
  259.                 return(NULL);
  260.  
  261.             break;
  262.  
  263.         default:
  264.  
  265.             return(NULL);
  266.     }
  267. }
  268.  
  269.  
  270. /* --------------------------------- ParseLine ---------------------------------
  271.  
  272.  Parse a line, build a syntax description
  273.  
  274. */
  275.  
  276. LibCall struct SyntaxChunk *
  277. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  278. {
  279.     if (IS_FOLD(lineNode))
  280.  
  281.         return(NULL);
  282.  
  283.     else if (lineNode->Len) {
  284.  
  285.         // not yet preparsed ? preparse a couple of lines
  286.  
  287.         if (lineNode->UserData == NULL) {
  288.  
  289.             ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line;
  290.  
  291.             if (lines > 50)
  292.                 lines = 50;
  293.  
  294.             ParseSection(scanID, lineNode, lines);
  295.         }
  296.  
  297.         if ((ULONG)lineNode->UserData & (COMMENT_SIMPLE | COMMENT_START | COMMENT_BODY | COMMENT_END))
  298.  
  299.             return(ParseString(lineNode->Text, lineNode->Len, scanID, (ULONG)lineNode->UserData));
  300.         else
  301.             return(NULL);
  302.     }
  303.     else
  304.         return(NULL);
  305. }
  306.  
  307. /* -------------------------------- UnparseLines -------------------------------
  308.  
  309.  Called by the editor if lines are to be deleted. We are supposed to free
  310.  private data attached to the lines.
  311.  
  312. */
  313.  
  314. LibCall void
  315. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  316. {
  317.     while (lines--) {
  318.  
  319.         // free syntax cache
  320.  
  321.         lineNode->UserData = NULL;
  322.  
  323.         // free folded subblock
  324.  
  325.         if (IS_FOLD(lineNode)) {
  326.  
  327.             struct Fold *fold = (struct Fold *)lineNode->SpecialInfo;
  328.  
  329.             UnparseLines(fold->TextNodes, fold->Lines);
  330.         }
  331.  
  332.         ++lineNode;
  333.     }
  334. }
  335.  
  336. /* -------------------------------- ParseSection -------------------------------
  337.  
  338.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  339.  preparse the lines.
  340.  
  341. */
  342.  
  343. LibCall void
  344. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  345. {
  346.     struct LineNode *firstNode;
  347.  
  348.     if (firstNode = FindContextStart(scanID, lineNode))
  349.         ParseContext(firstNode, lines + (lineNode - firstNode));
  350. }
  351.  
  352.  
  353. ///
  354. /// "private"
  355.  
  356. /*  ----------------------------- FindContextStart ------------------------------
  357.  
  358.  Search backwards until a parsed line or a context free line (fold or start of
  359.  file) is found. Return line node. This node may be used as starting point for
  360.  parsing lines.
  361.  
  362. */
  363.  
  364. struct LineNode *
  365. FindContextStart(scanID, lineNode)
  366.  
  367. ULONG            scanID;
  368. struct LineNode *lineNode;
  369. {
  370.     ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
  371.  
  372.     while (line--) {
  373.  
  374.         // found a fold or a parsed line ?
  375.  
  376.         if (lineNode->UserData || IS_FOLD(lineNode))
  377.             break;
  378.  
  379.         --lineNode;
  380.     }
  381.  
  382.     return(lineNode);
  383. }
  384.  
  385.  
  386. /* ------------------------------- ParseContext --------------------------------
  387.  
  388.  Preparse lines. The first line is expected to be either a context free line
  389.  (start of file or a fold header) or to be a parsed line.
  390.  
  391. */
  392.  
  393. void
  394. ParseContext(lineNode, lines)
  395.  
  396. struct LineNode *lineNode;
  397. ULONG            lines;
  398. {
  399.     ULONG mode;
  400.  
  401.     for (mode = COMMENT_NONE; lines--; ++lineNode) {
  402.  
  403.         if (IS_FOLD(lineNode))
  404.  
  405.             mode = COMMENT_NONE;
  406.  
  407.         else {
  408.  
  409.             if (lineNode->UserData == NULL)
  410.                 lineNode->UserData = (APTR)ParseContextLine(lineNode, mode);
  411.  
  412.             mode = (ULONG)lineNode->UserData;
  413.         }
  414.     }
  415. }
  416.  
  417.  
  418. /* ----------------------------- ParseContextLine ------------------------------
  419.  
  420.  Preparse a line, ie. check whether there are comments. The status of the last
  421.  line is passed in by the caller, this function will return the status (flag
  422.  bits) of the new line.
  423.  
  424. */
  425.  
  426. ULONG
  427. ParseContextLine(lineNode, last)
  428.  
  429. struct LineNode *lineNode;
  430. ULONG            last;
  431. {
  432.     UWORD len;
  433.     ULONG status;
  434.  
  435.     if (last & (COMMENT_START | COMMENT_BODY))
  436.         status = COMMENT_BODY;
  437.     else
  438.         status = FALSE;
  439.  
  440.     if (len = lineNode->Len) {
  441.  
  442.         BOOL   inString, inComment, commentStart;
  443.         UBYTE *text;
  444.  
  445.         // preset status of this line if part of a multi-line comment
  446.  
  447.         inComment    = status & COMMENT_BODY;
  448.         inString     = FALSE;
  449.         commentStart = FALSE;
  450.  
  451.         for (text = lineNode->Text; len >= 2; ++text, --len) {
  452.  
  453.             if (inComment) {
  454.  
  455.                 // end of comment detected ?
  456.  
  457.                 if ((text[0] == '*') && (text[1] == '/')) {
  458.  
  459.                     // end of a muli-line comment or end of a simple comment ?
  460.  
  461.                     if (commentStart)
  462.  
  463.                         status |= COMMENT_SIMPLE;
  464.  
  465.                     else {
  466.  
  467.                         status |=  COMMENT_END;
  468.                         status &= ~COMMENT_BODY;
  469.                     }
  470.  
  471.                     inComment = FALSE;
  472.  
  473.                     ++text;
  474.                     --len;
  475.                 }
  476.             }
  477.             else if (*text == 34)
  478.  
  479.                 inString = !inString;
  480.  
  481.             else if (inString == FALSE) {
  482.  
  483.                 // C++ comment detected (ignore rest of line) ?
  484.  
  485.                 if ((text[0] == '/') && (text[1] == '/')) {
  486.  
  487.                     status |= COMMENT_SIMPLE;
  488.  
  489.                     break;
  490.                 }
  491.                 else if ((text[0] == '/') && (text[1] == '*')) {
  492.  
  493.                     // start of comment detected
  494.  
  495.                     inComment    = TRUE;
  496.                     commentStart = TRUE;
  497.  
  498.                     ++text;
  499.                     --len;
  500.                 }
  501.             }
  502.         }
  503.  
  504.         // comment not closed ?
  505.  
  506.         if (inComment) {
  507.  
  508.             // continuation of multi-line comment or new comment ?
  509.  
  510.             if (commentStart)
  511.                 status |= COMMENT_START;
  512.             else
  513.                 status |= COMMENT_BODY;
  514.         }
  515.     }
  516.  
  517.     if (status == FALSE)
  518.         status = COMMENT_NONE;
  519.  
  520.     return(status);
  521. }
  522.  
  523.  
  524. /* -------------------------------- ParseString --------------------------------
  525.  
  526.  Parse a string, build a syntax description. Return NULL if there is nothing to
  527.  highlight.
  528.  
  529. */
  530.  
  531. struct SyntaxChunk *
  532. ParseString(UBYTE *text, UWORD len, ULONG scanID, ULONG status)
  533. {
  534.     if (len) {
  535.  
  536.         struct SyntaxChunk *syntaxStack = ((struct BufferHandle *)scanID)->bh_SyntaxStack;
  537.  
  538.         UWORD  element, indent, commentStart;
  539.         BOOL   inComment;
  540.  
  541.         // leading and trailing spaces have to be ignored
  542.  
  543.         for (indent = 0; (len && (*text == 32)); --len, ++indent)
  544.             ++text;
  545.  
  546.         while (len && (text[len - 1] == 32))
  547.             --len;
  548.  
  549.         // no syntax elements found so far
  550.  
  551.         element = 0;
  552.  
  553.         // inner block line ?
  554.  
  555.         if (status & COMMENT_BODY) {
  556.  
  557.             inComment    = TRUE;
  558.             commentStart = indent;
  559.         }
  560.         else {
  561.  
  562.             BOOL inString;
  563.  
  564.             if (inComment = (status & COMMENT_END))
  565.                 commentStart = indent;
  566.  
  567.             for (inString = FALSE; len >= 2; ++text, ++indent, --len) {
  568.  
  569.                 if (inComment) {
  570.  
  571.                     // end of comment detected ?
  572.  
  573.                     if ((text[0] == '*') && (text[1] == '/')) {
  574.  
  575.                         syntaxStack[element].sc_Start = commentStart;
  576.                         syntaxStack[element].sc_End   = indent + 1;
  577.                         syntaxStack[element].sc_Level = 1;
  578.  
  579.                         inComment = FALSE;
  580.  
  581.                         ++element;
  582.                         ++text;
  583.                         ++indent;
  584.                         --len;
  585.                     }
  586.                 }
  587.                 else if (*text == 34)
  588.  
  589.                     inString = !inString;
  590.  
  591.                 else if (inString == FALSE) {
  592.  
  593.                     // C++ comment detected (ignore rest of line) ?
  594.  
  595.                     if ((text[0] == '/') && (text[1] == '/')) {
  596.  
  597.                         syntaxStack[element].sc_Start = indent;
  598.                         syntaxStack[element].sc_End   = indent + len - 1;
  599.                         syntaxStack[element].sc_Level = 2;
  600.  
  601.                         ++element;
  602.  
  603.                         break;
  604.                     }
  605.                     else if ((text[0] == '/') && (text[1] == '*')) {
  606.  
  607.                         // start of comment detected
  608.  
  609.                         inComment    = TRUE;
  610.                         commentStart = indent;
  611.  
  612.                         ++text;
  613.                         ++indent;
  614.                         --len;
  615.                     }
  616.                 }
  617.             }
  618.         }
  619.  
  620.         // unterminated comment ? highlight rest of line
  621.  
  622.         if (inComment) {
  623.  
  624.             syntaxStack[element].sc_Start = commentStart;
  625.             syntaxStack[element].sc_End   = indent + len - 1;
  626.             syntaxStack[element].sc_Level = 1;
  627.  
  628.             ++element;
  629.         }
  630.  
  631.         if (element) {
  632.  
  633.             // terminate syntax stack
  634.  
  635.             syntaxStack[element].sc_Start = FALSE;
  636.             syntaxStack[element].sc_End   = FALSE;
  637.             syntaxStack[element].sc_Level = FALSE;
  638.  
  639.             return(syntaxStack);
  640.         }
  641.         else
  642.             return(NULL);
  643.  
  644.     }
  645.     else
  646.         return(NULL);
  647. }
  648.  
  649.  
  650. /* ------------------------------- VerifyContext -------------------------------
  651.  
  652.  Ensure that syntax information of a specified range of lines ("damage region")
  653.  is valid and consistent with the existing text. Return a refresh request if
  654.  not. The damage area is expected to have been unparsed already. This is what we
  655.  have to do: we preparse existing lines before the damage area if belonging to
  656.  the syntax context of the damage area (ie. all lines affecting highlighting of
  657.  the first line in the damage area). The damage area is parsed, too. Parsed
  658.  lines after the damage area are reparsed if highlighting is found to be
  659.  inconsistent with the line(s) before. Reparsing continues until the end of the
  660.  file respectively until no more inconsistencies are found.
  661.  
  662. */
  663.  
  664. struct RefreshRequest *
  665. VerifyContext(scanID, line, lines)
  666.  
  667. ULONG scanID, line, lines;
  668. {
  669.     struct EditConfig *config;
  670.     struct LineNode   *lineNode, *lastNode;
  671.  
  672.     ULONG last, refreshStart, refresh = FALSE;
  673.  
  674.     config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  675.  
  676.     lineNode = config->TextNodes + line;
  677.     lastNode = lineNode + (lines - 1);
  678.  
  679.     // preparse from context start until end of damage area
  680.  
  681.     if (lines)
  682.         ParseSection(scanID, lineNode, lines);
  683.  
  684.     // get syntax flags of last line in damage area
  685.  
  686.     if ((lastNode < config->TextNodes) || IS_FOLD(lastNode))
  687.         last = COMMENT_NONE;
  688.     else
  689.         last = (ULONG)lastNode->UserData;
  690.  
  691.     // continue parsing until no more inconsistencies are found (until context end)
  692.  
  693.     refreshStart = (line += lines);
  694.  
  695.     for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
  696.  
  697.         if (IS_FOLD(lineNode))
  698.  
  699.             break;
  700.  
  701.         else if (lineNode->UserData == NULL)
  702.  
  703.             lineNode->UserData = (APTR)ParseContextLine(lineNode, last);
  704.  
  705.         else if (BadSuccessor(last, (ULONG)lineNode->UserData))
  706.  
  707.             lineNode->UserData = (APTR)ParseContextLine(lineNode, last);
  708.         else
  709.             break;
  710.  
  711.         last = (ULONG)lineNode->UserData;
  712.     }
  713.  
  714.     if (refresh) {
  715.  
  716.         struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
  717.  
  718.         refreshRequest->rr_Line  = refreshStart;
  719.         refreshRequest->rr_Lines = refresh;
  720.  
  721.         return(refreshRequest);
  722.     }
  723.     else
  724.         return(NULL);
  725. }
  726.  
  727.  
  728. /* ------------------------------- BadSuccessor --------------------------------
  729.  
  730.  Return TRUE if syntax information of two adjacent lines is inconsistent.
  731.  
  732. */
  733.  
  734. BOOL
  735. BadSuccessor(pred, succ)
  736.  
  737. ULONG pred, succ;
  738. {
  739.     if (succ & (COMMENT_BODY | COMMENT_END)) {
  740.  
  741.         // comment body/end without start ?
  742.  
  743.         if ((pred & (COMMENT_START | COMMENT_BODY)) == FALSE)
  744.  
  745.             return(TRUE);
  746.     }
  747.     else if (pred & COMMENT_START) {
  748.  
  749.         // comment start without body/end ?
  750.  
  751.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  752.  
  753.             return(TRUE);
  754.  
  755.         // comment inside comment ?
  756.  
  757.         if (succ & COMMENT_START)
  758.  
  759.             return(TRUE);
  760.     }
  761.     else if (pred & COMMENT_BODY) {
  762.  
  763.         // body line without end ?
  764.  
  765.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  766.  
  767.             return(TRUE);
  768.     }
  769.  
  770.     // C++ comment inside ANSI comment ?
  771.  
  772.     if (pred & (COMMENT_START | COMMENT_BODY))
  773.  
  774.         if (pred & COMMENT_SIMPLE)
  775.  
  776.             return(TRUE);
  777.  
  778.     return(FALSE);
  779. }
  780.  
  781. ///
  782.