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

  1. /* -----------------------------------------------------------------------------
  2.  
  3.  C++ scanner ©1996 Dietmar Eilert, modified by Olaf Barthel
  4.  
  5.  GoldED syntax parser (uses a syntax cache). Dice:
  6.  
  7.  DMAKE
  8.  
  9.  -------------------------------------------------------------------------------
  10.  
  11. */
  12.  
  13. #include "defs.h"
  14.  
  15. /// "Header stuff"
  16.  
  17. // Buffer handles are allocated for each text buffer to keep track of ressources:
  18.  
  19. struct BufferHandle {
  20.  
  21.     struct EditConfig     *bh_EditConfig;            // pointer to text data
  22.     struct GlobalConfig   *bh_GlobalConfig;          // editor configuration
  23.     struct SyntaxInfo     *bh_SyntaxInfo;            // parser output
  24.     struct RefreshRequest  bh_RefreshRequest;        // display refresh request
  25. };
  26.  
  27. // known ANSI C keywords are described by syntax structures:
  28.  
  29. struct Keyword {
  30.  
  31.     UBYTE *Pattern;                                  // known pattern (e.g. "UWORD")
  32.     UBYTE *Next;                                     // valid range for next non-space char  (or NULL)
  33.     UBYTE *Required;                                 // this char has to be on the same line (or NULL)
  34.     UWORD  Level;                                    // syntax level
  35. };
  36.  
  37. #define EMPTY_STACK ((struct SyntaxInfo *)~0)        // empty stack flag
  38.  
  39. #define UPPER(a) ((a) & 95)                          // simple uppercase conversion
  40.  
  41. // syntax infos (or EMPTY_STACK) are attached to parsed lines
  42.  
  43. struct SyntaxInfo {
  44.  
  45.     UWORD              Flags;                        // comment flags
  46.     struct SyntaxChunk SyntaxChunk;                  // syntax chunks
  47. };
  48.  
  49. // these flag bits describe the line's comment properties
  50.  
  51. #define COMMENT_NONE      (0)                        // no comment in this line
  52. #define COMMENT_SIMPLE    (1L<<0)                    // there is a comment in this line
  53. #define COMMENT_START     (1L<<1)                    // line is start of multi-line comment
  54. #define COMMENT_BODY      (1L<<2)                    // line is body  of multi-line comment
  55. #define COMMENT_END       (1L<<3)                    // line is end   of multi-line comment
  56.  
  57. // macros to access SyntaxInfo members
  58.  
  59. #define GET_FLAGS(node)    (((struct SyntaxInfo *)(node)->UserData)->Flags)
  60. #define GET_CHUNK(node)   (&((struct SyntaxInfo *)(node)->UserData)->SyntaxChunk)
  61.  
  62. // recognized syntax levels
  63.  
  64. enum SyntaxLevels {
  65.  
  66.     SYNTAX_STANDARD,
  67.     SYNTAX_COMMENTANSI,
  68.     SYNTAX_COMMENTCPP,
  69.     SYNTAX_RESERVED,
  70.     SYNTAX_PREPROCESSOR,
  71.     SYNTAX_NUMBER,
  72.     SYNTAX_QUOTE,
  73.     SYNTAX_STRING,
  74.     SYNTAX_TYPE,
  75.     SYNTAX_OPERATOR,
  76.     SYNTAX_BRACKET,
  77.     SYNTAX_POINTER,
  78.     SYNTAX_MAXIMUM
  79. };
  80.  
  81. ///
  82. /// "Globals"
  83.  
  84. struct Keyword *Hash[256];
  85.  
  86. BOOL IsOperator[256];
  87. BOOL IsLetter  [256];
  88. BOOL IsSpace   [256];
  89. BOOL IsDigit   [256];
  90. BOOL IsKnown   [256];
  91.  
  92. struct Keyword Reserved[] = {
  93.  
  94.     //  known_word next_char  on_same_line      level
  95.  
  96.     // uppercase
  97.  
  98.     { "APTR",       NULL,        NULL,      SYNTAX_TYPE     },
  99.     { "BOOL",       NULL,        NULL,      SYNTAX_TYPE     },
  100.     { "BPTR",       NULL,        NULL,      SYNTAX_TYPE     },
  101.     { "BPTR",       NULL,        NULL,      SYNTAX_TYPE     },
  102.     { "BYTE",       NULL,        NULL,      SYNTAX_TYPE     },
  103.     { "FLOAT",      NULL,        NULL,      SYNTAX_TYPE     },
  104.     { "LONG",       NULL,        NULL,      SYNTAX_TYPE     },
  105.     { "REGISTER",   NULL,        NULL,      SYNTAX_TYPE     },
  106.     { "STATIC",     NULL,        NULL,      SYNTAX_TYPE     },
  107.     { "STRPTR",     NULL,        NULL,      SYNTAX_TYPE     },
  108.     { "TEXT",       NULL,        NULL,      SYNTAX_TYPE     },
  109.     { "UBYTE",      NULL,        NULL,      SYNTAX_TYPE     },
  110.     { "ULONG",      NULL,        NULL,      SYNTAX_TYPE     },
  111.     { "UWORD",      NULL,        NULL,      SYNTAX_TYPE     },
  112.     { "VOID",       NULL,        NULL,      SYNTAX_TYPE     },
  113.     { "WORD",       NULL,        NULL,      SYNTAX_TYPE     },
  114.  
  115.     // lowercase
  116.  
  117.     { "asm",        "{",         NULL,      SYNTAX_RESERVED },
  118.     { "auto",       NULL,        NULL,      SYNTAX_RESERVED },
  119.     { "break",      ";",         NULL,      SYNTAX_RESERVED },
  120.     { "case",       NULL,        ":",       SYNTAX_RESERVED },
  121.     { "catch",      "(",         NULL,      SYNTAX_RESERVED },
  122.     { "char",       NULL,        NULL,      SYNTAX_TYPE     },
  123.     { "class",      NULL,        NULL,      SYNTAX_RESERVED },
  124.     { "const",      NULL,        NULL,      SYNTAX_RESERVED },
  125.     { "continue",   ";",         NULL,      SYNTAX_RESERVED },
  126.     { "default",    ":",         NULL,      SYNTAX_RESERVED },
  127.     { "delete",     NULL,        NULL,      SYNTAX_RESERVED },
  128.     { "do",         NULL,        NULL,      SYNTAX_RESERVED },
  129.     { "double",     NULL,        NULL,      SYNTAX_TYPE     },
  130.     { "else",       NULL,        NULL,      SYNTAX_RESERVED },
  131.     { "enum",       NULL,        NULL,      SYNTAX_RESERVED },
  132.     { "extern",     NULL,        ";",       SYNTAX_RESERVED },
  133.     { "float",      NULL,        NULL,      SYNTAX_TYPE     },
  134.     { "for",        "(",         "(",       SYNTAX_RESERVED },
  135.     { "friend",     NULL,        NULL,      SYNTAX_RESERVED },
  136.     { "goto",       NULL,        NULL,      SYNTAX_RESERVED },
  137.     { "if",         "(",         "(",       SYNTAX_RESERVED },
  138.     { "inline",     NULL,        NULL,      SYNTAX_RESERVED },
  139.     { "int",        NULL,        NULL,      SYNTAX_TYPE     },
  140.     { "long",       NULL,        NULL,      SYNTAX_TYPE     },
  141.     { "new",        NULL,        NULL,      SYNTAX_RESERVED },
  142.     { "operator",   NULL,        NULL,      SYNTAX_RESERVED },
  143.     { "private",    NULL,        NULL,      SYNTAX_RESERVED },
  144.     { "protected",  NULL,        NULL,      SYNTAX_RESERVED },
  145.     { "public",     NULL,        NULL,      SYNTAX_RESERVED },
  146.     { "register",   NULL,        NULL,      SYNTAX_RESERVED },
  147.     { "return",     "(;",        NULL,      SYNTAX_RESERVED },
  148.     { "short",      NULL,        NULL,      SYNTAX_TYPE     },
  149.     { "signed",     NULL,        NULL,      SYNTAX_TYPE     },
  150.     { "sizeof",     NULL,        NULL,      SYNTAX_RESERVED },
  151.     { "static",     NULL,        NULL,      SYNTAX_RESERVED },
  152.     { "struct",     NULL,        NULL,      SYNTAX_TYPE     },
  153.     { "switch",     "(",         NULL,      SYNTAX_RESERVED },
  154.     { "template",   "<",         NULL,      SYNTAX_RESERVED },
  155.     { "this",       NULL,        NULL,      SYNTAX_RESERVED },
  156.     { "throw",      NULL,        NULL,      SYNTAX_RESERVED },
  157.     { "try",        "{",         NULL,      SYNTAX_RESERVED },
  158.     { "typedef",    NULL,        NULL,      SYNTAX_RESERVED },
  159.     { "union",      NULL,        NULL,      SYNTAX_TYPE     },
  160.     { "unsigned",   NULL,        NULL,      SYNTAX_TYPE     },
  161.     { "virtual",    NULL,        NULL,      SYNTAX_RESERVED },
  162.     { "void",       NULL,        NULL,      SYNTAX_TYPE     },
  163.     { "volatile",   NULL,        NULL,      SYNTAX_RESERVED },
  164.     { "while",      "(",         NULL,      SYNTAX_RESERVED },
  165.  
  166.     { NULL, NULL, NULL, 0}
  167. };
  168.  
  169. ///
  170. /// "Prototype"
  171.  
  172. // library functions
  173.  
  174. Prototype LibCall struct ParserData     *MountScanner(void);
  175. Prototype LibCall ULONG                  StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
  176. Prototype LibCall ULONG                  CloseScanner(__D0 ULONG);
  177. Prototype LibCall void                   FlushScanner(__D0 ULONG);
  178. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig  *);
  179. Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
  180. Prototype LibCall struct SyntaxChunk    *ParseLine   (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  181. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  182. Prototype LibCall void                   ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
  183.  
  184. // private functions
  185.  
  186. Prototype void                           PrepareHash(void);
  187. Prototype struct SyntaxChunk            *ParseString(UBYTE *, UWORD, ULONG);
  188. Prototype struct SyntaxInfo             *DupInfo(struct SyntaxInfo *);
  189. Prototype struct Keyword                *IsAReservedWord(UBYTE *, UWORD);
  190. Prototype struct Keyword                *IsAReservedOperator(UBYTE *, UWORD);
  191. Prototype BOOL                           strnchr(UBYTE *, UWORD, UWORD);
  192. Prototype BOOL                           CheckEnvironment(UBYTE *, UWORD, UWORD, struct Keyword *);
  193. Prototype struct LineNode               *FindContextStart(ULONG, struct LineNode *);
  194. Prototype void                           ParseContext(struct LineNode *, ULONG, ULONG);
  195. Prototype struct SyntaxInfo             *ParseContextLine(UBYTE *, UWORD, ULONG, ULONG);
  196. Prototype BOOL                           BadSuccessor(ULONG, ULONG);
  197. Prototype struct RefreshRequest         *VerifyContext(ULONG, ULONG, ULONG);
  198. Prototype BOOL                           ReadKeywords(void);
  199.  
  200. #define AllocVec AllocVecPooled
  201. #define FreeVec  FreeVecPooled
  202.  
  203. ///
  204. /// "Library functions"
  205.  
  206. /* ------------------------------- MountScanner --------------------------------
  207.  
  208.  Called by the editor before first usage of a scanner. Return a description of
  209.  our abilities.
  210.  
  211. */
  212.  
  213. LibCall struct ParserData *
  214. MountScanner()
  215. {
  216.     static UBYTE version[] = "$VER: WarpC++ 2.5 (" __COMMODORE_DATE__ ")";
  217.  
  218.     static struct ParserData parserData;
  219.  
  220.     // syntax elements understood by parser
  221.  
  222.     static UBYTE *levelNames[] = {
  223.  
  224.         "Standard text",
  225.         "Comment ANSI",
  226.         "Comment C++",
  227.         "Reserved words",
  228.         "Preprocessor",
  229.         "Numbers",
  230.         "Quotes",
  231.         "Strings",
  232.         "Types",
  233.         "Double operators",
  234.         "{}",
  235.         "->",
  236.         NULL
  237.     };
  238.  
  239.     static UBYTE *example[] = {
  240.  
  241.         "/* Simple ANSI C example */       ",
  242.         "                                  ",
  243.         "#include <defs.h>                 ",
  244.         "                                  ",
  245.         "int                               ",
  246.         "main(int argc, char **argv)       ",
  247.         "{                                 ",
  248.         "   if (ExecBase->SoftVer >= 39) { ",
  249.         "                                  ",
  250.         "      // any arguments available ?",
  251.         "                                  ",
  252.         "      if (argc-- > 1) {           ",
  253.         "                                  ",
  254.         "         puts(\"hello world\");   ",
  255.         "         putchar('\\a');          ",
  256.         "      }                           ",
  257.         "   }                              ",
  258.         "}                                 ",
  259.  
  260.         NULL
  261.     };
  262.  
  263.     // color suggestions
  264.  
  265.     static ULONG levelColors[] = {
  266.  
  267.         MAKE_RGB4( 0,  0,   0),                      // black
  268.         MAKE_RGB4( 5,  7,   9),                      // blue
  269.         MAKE_RGB4( 8,  8,   8),                      // grey
  270.         MAKE_RGB4(15, 15,  15),                      // white
  271.         MAKE_RGB4(15, 15,   9),                      // yellow
  272.         MAKE_RGB4( 0,  0,   0),                      // black
  273.         MAKE_RGB4( 0,  0,   0),                      // black
  274.         MAKE_RGB4( 0,  0,   0),                      // black
  275.         MAKE_RGB4( 0,  0,   0),                      // black
  276.         MAKE_RGB4( 0,  0,   0),                      // black
  277.         MAKE_RGB4(15, 15,  15),                      // white
  278.         MAKE_RGB4( 0,  0,   0),                      // black
  279.     };
  280.  
  281.     parserData.pd_Release  = SCANLIBVERSION;
  282.     parserData.pd_Version  = 1;
  283.     parserData.pd_Serial   = 0;
  284.     parserData.pd_Info     = "WarpC++ 2.5";
  285.     parserData.pd_Levels   = SYNTAX_MAXIMUM;
  286.     parserData.pd_Names    = levelNames;
  287.     parserData.pd_Colors   = levelColors;
  288.     parserData.pd_Flags    = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
  289.     parserData.pd_Example  = example;
  290.  
  291.     PrepareHash();
  292.  
  293.     return(&parserData);
  294. }
  295.  
  296.  
  297. /* ------------------------------- StartScanner --------------------------------
  298.  
  299.  Called by the editor after a new text buffer has been created. We allocate a
  300.  buffer to hold text-specific data. The buffer address is returned as handle.
  301.  
  302. */
  303.  
  304. LibCall ULONG
  305. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
  306. {
  307.     struct BufferHandle *handle;
  308.  
  309.     if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  310.  
  311.         handle->bh_GlobalConfig = globalConfigPtr;
  312.         handle->bh_EditConfig   = editConfigPtr;
  313.         handle->bh_SyntaxInfo   = (struct SyntaxInfo *)syntaxStack;
  314.     }
  315.  
  316.     return((ULONG)handle);
  317. }
  318.  
  319.  
  320. /* ------------------------------- CloseScanner --------------------------------
  321.  
  322.  Called by the editor if a text buffer is about to be closed. Deallocate buffer
  323.  specific 'global' data.
  324.  
  325. */
  326.  
  327. LibCall ULONG
  328. CloseScanner(__D0 ULONG scanID)
  329. {
  330.     if (scanID) {
  331.  
  332.         struct BufferHandle *handle = (struct BufferHandle *)scanID;
  333.  
  334.         FreeVec(handle);
  335.     }
  336.  
  337.     return(0);
  338. }
  339.  
  340.  
  341. /* ------------------------------- FlushScanner --------------------------------
  342.  
  343.  Called by the editor in low memory situations: we are supposed to free as much
  344.  memory as possible.
  345.  
  346. */
  347.  
  348. LibCall void
  349. FlushScanner(__D0 ULONG scanID)
  350. {
  351.     struct BufferHandle *handle;
  352.     struct EditConfig   *config;
  353.     struct LineNode     *lineNode;
  354.  
  355.     handle = (struct BufferHandle *)scanID;
  356.     config = handle->bh_EditConfig;
  357.  
  358.     if (lineNode = config->TextNodes)
  359.         UnparseLines(lineNode, config->Lines);
  360. }
  361.  
  362.  
  363. /* ------------------------------- SetupScanner --------------------------------
  364.  
  365.  Called by the editor if the user wants to change the scanner's configuration.
  366.  We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  367.  unset).
  368.  
  369. */
  370.  
  371. LibCall void
  372. SetupScanner(__A0 globalConfigPtr)
  373. {
  374.     return();
  375. }
  376.  
  377.  
  378. /* ------------------------------- BriefScanner --------------------------------
  379.  
  380.  Called to notify a context scanner if lines have been added, deleted or
  381.  modified. We are supposed to return an additional display request or NULL
  382.  (the editor will refresh the damaged region only if we return NULL). Damaged
  383.  lines have already been unparsed by the editor. This is what we have to do:
  384.  
  385.  a) lines have been deleted or modified
  386.  
  387.     Check whether the syntax scheme of the remaining lines is still valid (it
  388.     will be invalid if say a comment start has been deleted but the comment
  389.     end has not been deleted). Reparse the lines and return a refresh request
  390.     if not.
  391.  
  392.  b) lines have been inserted
  393.  
  394.     Check whether the syntax sheme of the new lines affects the existing lines.
  395.     Reparse all lines starting at the insertion point and return a refresh
  396.     request if not.
  397.  
  398.  c) lines have been (un)folded
  399.  
  400.     This scanner assumes that folding doesn't affect syntax highlighting for the
  401.     sake of simplicity. This isn't necessarily a valid assumption: we will run
  402.     into touble if the user folds parts of a comment only (e.g. the first line).
  403.  
  404. */
  405.  
  406. LibCall struct RefreshRequest *
  407. BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
  408. {
  409.     struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  410.  
  411.     switch (notify->sn_Class) {
  412.  
  413.         case SCANNER_NOTIFY_MODIFIED:
  414.  
  415.             ULONG line = notify->sn_Line;
  416.  
  417.             if (notify->sn_Removed > notify->sn_Lines) {
  418.  
  419.                 // lines have been deleted
  420.  
  421.                 return(VerifyContext(scanID, line, 0));
  422.             }
  423.             else if (notify->sn_Lines)
  424.  
  425.                 // lines have been modified or inserted
  426.  
  427.                 return(VerifyContext(scanID, line, notify->sn_Lines));
  428.             else
  429.                 return(NULL);
  430.  
  431.             break;
  432.  
  433.         default:
  434.  
  435.             return(NULL);
  436.     }
  437. }
  438.  
  439.  
  440. /* --------------------------------- ParseLine ---------------------------------
  441.  
  442.  Parse a line, build a syntax description
  443.  
  444. */
  445.  
  446. LibCall struct SyntaxChunk *
  447. ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
  448. {
  449.     if (IS_FOLD(lineNode))
  450.  
  451.         return(NULL);
  452.  
  453.     else if (lineNode->Len) {
  454.  
  455.         // line parsed already ?
  456.  
  457.         if (lineNode->UserData == EMPTY_STACK)
  458.  
  459.             return(NULL);
  460.  
  461.         else if (lineNode->UserData)
  462.  
  463.             return(GET_CHUNK(lineNode));
  464.  
  465.         else {
  466.  
  467.             // not yet parsed ? preparse a couple of lines
  468.  
  469.             ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line;
  470.  
  471.             if (lines > 50)
  472.                 lines = 50;
  473.  
  474.             ParseSection(scanID, lineNode, lines);
  475.  
  476.             if (lineNode->UserData == EMPTY_STACK)
  477.                 return(NULL);
  478.             else
  479.                 return(GET_CHUNK(lineNode));
  480.         }
  481.     }
  482.     else
  483.         return(NULL);
  484. }
  485.  
  486.  
  487. /* -------------------------------- UnparseLines -------------------------------
  488.  
  489.  Called by the editor if lines are to be deleted. We are supposed to free
  490.  private data attached to the lines.
  491.  
  492. */
  493.  
  494. LibCall void
  495. UnparseLines(__A0  struct LineNode *lineNode, __D0 ULONG lines)
  496. {
  497.     while (lines--) {
  498.  
  499.         // free syntax cache
  500.  
  501.         if (lineNode->UserData) {
  502.  
  503.             if (lineNode->UserData != (APTR)EMPTY_STACK)
  504.                 FreeVec(lineNode->UserData);
  505.  
  506.             lineNode->UserData = NULL;
  507.         }
  508.  
  509.         // free folded subblock
  510.  
  511.         if (IS_FOLD(lineNode)) {
  512.  
  513.             struct Fold *fold = (struct Fold *)lineNode->SpecialInfo;
  514.  
  515.             UnparseLines(fold->TextNodes, fold->Lines);
  516.         }
  517.  
  518.         ++lineNode;
  519.     }
  520. }
  521.  
  522. /* -------------------------------- ParseSection -------------------------------
  523.  
  524.  Called by the editor if lines are to be displayed. The scanner is encouraged to
  525.  preparse the lines.
  526.  
  527. */
  528.  
  529. LibCall void
  530. ParseSection(__D0 ULONG scanID, __A0  struct LineNode *lineNode, __D1 ULONG lines)
  531. {
  532.     struct LineNode *firstNode;
  533.  
  534.     if (firstNode = FindContextStart(scanID, lineNode))
  535.         ParseContext(firstNode, lines + (lineNode - firstNode), scanID);
  536. }
  537.  
  538. ///
  539. /// "Private"
  540.  
  541. /* -------------------------------- PrepareHash --------------------------------
  542.  
  543.  Prepare reserved keyword hashtable (used to find keyword description if ascii
  544.  value of first letter is known) and prepare IsOperator array.
  545.  
  546. */
  547.  
  548. void
  549. PrepareHash()
  550. {
  551.     struct Keyword *keyword;
  552.     UWORD           ascii;
  553.  
  554.     memset(Hash, 0, sizeof(Hash));
  555.  
  556.     keyword = Reserved;
  557.  
  558.     while (keyword->Pattern) {
  559.  
  560.         UWORD ascii = *keyword->Pattern;
  561.  
  562.         Hash[ascii] = keyword;
  563.  
  564.         while (keyword->Pattern && (*keyword->Pattern == ascii))
  565.             ++keyword;
  566.     }
  567.  
  568.     memset(IsOperator, FALSE, sizeof(IsOperator));
  569.  
  570.     IsOperator['*'] = TRUE;
  571.     IsOperator['='] = TRUE;
  572.     IsOperator['<'] = TRUE;
  573.     IsOperator['>'] = TRUE;
  574.     IsOperator['!'] = TRUE;
  575.     IsOperator['+'] = TRUE;
  576.     IsOperator['-'] = TRUE;
  577.     IsOperator['&'] = TRUE;
  578.     IsOperator['/'] = TRUE;
  579.     IsOperator['|'] = TRUE;
  580.     IsOperator[':'] = TRUE;
  581.  
  582.     memset(IsLetter, FALSE, sizeof(IsLetter));
  583.     memset(IsSpace,  FALSE, sizeof(IsLetter));
  584.     memset(IsKnown,  FALSE, sizeof(IsLetter));
  585.  
  586.     for (ascii = 0; ascii <= 255; ++ascii) {
  587.  
  588.         IsLetter[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z'));
  589.         IsSpace [ascii] = ((ascii >= ' ') && (ascii <= '/')) || ((ascii >= ':') && (ascii <= '?')) || ((ascii >= '[') && (ascii <= '^')) || ((ascii >= '{') && (ascii <= '~'));
  590.         IsDigit [ascii] = ((ascii >= '0') && (ascii <= '9'));
  591.     }
  592.  
  593.     // only expressions starting with a 'known' character are parsed at all
  594.  
  595.     for (ascii = 0; ascii <= 255; ++ascii)
  596.         IsKnown[ascii] = IsOperator[ascii] || IsDigit[ascii] || Hash[ascii] || (ascii == 39) || (ascii == 34) || (ascii == '/') || (ascii == '{') || (ascii == '}') || (ascii == '#');
  597. }
  598.  
  599.  
  600. /* ------------------------------ IsAReservedWord ------------------------------
  601.  
  602.  Decide whether word of length <len> is a reserved ANSI C keyword
  603.  
  604. */
  605.  
  606. struct Keyword *
  607. IsAReservedWord(text, len)
  608.  
  609. UBYTE *text;
  610. UWORD  len;
  611. {
  612.     struct Keyword *keyword;
  613.  
  614.     if (keyword = Hash[*text]) {
  615.  
  616.         while (keyword->Pattern) {
  617.  
  618.             if (*keyword->Pattern > *text)
  619.  
  620.                 break;
  621.  
  622.             else if (keyword->Pattern[len] || (keyword->Pattern[1] != text[1]) || memcmp(text, keyword->Pattern, len))
  623.  
  624.                 ++keyword;
  625.             else
  626.                 return(keyword);
  627.         }
  628.  
  629.         return(NULL);
  630.     }
  631.     else
  632.         return(NULL);
  633. }
  634.  
  635.  
  636. /* ------------------------------ IsAReservedOperator --------------------------
  637.  
  638.  Decide whether word of length <len> is a reserved C 'double' operator
  639.  
  640. */
  641.  
  642. struct Keyword *
  643. IsAReservedOperator(text, len)
  644.  
  645. UBYTE *text;
  646. UWORD  len;
  647. {
  648.     static struct Keyword reserved[] = {
  649.  
  650.     //  known word    next char   on same line
  651.  
  652.         { "!=",         NULL,        NULL,      SYNTAX_OPERATOR },
  653.         { "&&",         NULL,        NULL,      SYNTAX_OPERATOR },
  654.         { "&=",         NULL,        NULL,      SYNTAX_OPERATOR },
  655.         { "*=",         NULL,        NULL,      SYNTAX_OPERATOR },
  656.         { "++",         NULL,        NULL,      SYNTAX_OPERATOR },
  657.         { "+=",         NULL,        NULL,      SYNTAX_OPERATOR },
  658.         { "--",         NULL,        NULL,      SYNTAX_OPERATOR },
  659.         { "-=",         NULL,        NULL,      SYNTAX_OPERATOR },
  660.         { "->",         NULL,        NULL,      SYNTAX_POINTER  },
  661.         { "/=",         NULL,        NULL,      SYNTAX_OPERATOR },
  662.         { "::",         NULL,        NULL,      SYNTAX_OPERATOR },
  663.         { "<<",         NULL,        NULL,      SYNTAX_OPERATOR },
  664.         { "<=",         NULL,        NULL,      SYNTAX_OPERATOR },
  665.         { "==",         NULL,        NULL,      SYNTAX_OPERATOR },
  666.         { ">=",         NULL,        NULL,      SYNTAX_OPERATOR },
  667.         { ">>",         NULL,        NULL,      SYNTAX_OPERATOR },
  668.         { "|=",         NULL,        NULL,      SYNTAX_OPERATOR },
  669.         { "||",         NULL,        NULL,      SYNTAX_OPERATOR },
  670.         {  NULL,        NULL,        NULL,      0}
  671.     };
  672.  
  673.     struct Keyword *keyword;
  674.  
  675.     for (keyword = reserved; keyword->Pattern; ++keyword) {
  676.  
  677.         if (*keyword->Pattern > *text)
  678.  
  679.             break;
  680.  
  681.         else if ((*keyword->Pattern == *text) && (keyword->Pattern[1] == text[1]))
  682.  
  683.             return(keyword);
  684.     }
  685.  
  686.     return(NULL);
  687. }
  688.  
  689.  
  690.  
  691. /* ----------------------------- CheckEnvironment ------------------------------
  692.  
  693.  Check whether keyword environment complies with keyword.
  694.  
  695. */
  696.  
  697. BOOL
  698. CheckEnvironment(text, len, wordLen, keyword)
  699.  
  700. struct Keyword *keyword;
  701. UBYTE          *text;
  702. UWORD           len, wordLen;
  703. {
  704.     // move to first non-space character after recognized keyword
  705.  
  706.     for (text += wordLen, len -= wordLen; len && (*text == ' '); ++wordLen, --len)
  707.         ++text;
  708.  
  709.     // check whether first non-space character is valid
  710.  
  711.     if ((keyword->Next == NULL) || strchr(keyword->Next, *text)) {
  712.  
  713.         // check whether required character is used on the same line
  714.  
  715.         if ((keyword->Required == NULL) || strnchr(text, len, *keyword->Required)) {
  716.  
  717.             return(TRUE);
  718.         }
  719.         else
  720.             return(FALSE);
  721.     }
  722.     else
  723.         return(FALSE);
  724. }
  725.  
  726.  
  727. /* ---------------------------------- strnchr ----------------------------------
  728.  
  729.  strchr replacement (doesn't require 0-terminated string). Return TRUE if the
  730.  character is found within <text>.
  731.  
  732. */
  733.  
  734. BOOL
  735. strnchr(text, len, character)
  736.  
  737. UBYTE *text;
  738. UWORD  character, len;
  739. {
  740.     while (len--)
  741.         if (*text++ == character)
  742.             return(TRUE);
  743.  
  744.     return(FALSE);
  745. }
  746.  
  747.  
  748. /* --------------------------------- DupInfo -----------------------------------
  749.  
  750.  Duplicate syntax info (to be FreeVec'ed)
  751.  
  752. */
  753.  
  754. struct SyntaxInfo *
  755. DupInfo(syntaxInfo)
  756.  
  757. struct SyntaxInfo *syntaxInfo;
  758. {
  759.     struct SyntaxChunk *chunk;
  760.     struct SyntaxInfo  *info;
  761.     ULONG               size;
  762.     UWORD               elements;
  763.  
  764.     // determine stack size
  765.  
  766.     for (elements = 0, chunk = &syntaxInfo->SyntaxChunk; chunk->sc_Level; ++chunk)
  767.         ++elements;
  768.  
  769.     // create copy of syntax stack (to be attached to a text line by the caller)
  770.  
  771.     size = (++elements) * sizeof(struct SyntaxChunk) + sizeof(UWORD);
  772.  
  773.     if (info = AllocVec(size + sizeof(UWORD), MEMF_PUBLIC)) {
  774.  
  775.         movmem(syntaxInfo, info, size);
  776.  
  777.         return(info);
  778.     }
  779.     else
  780.         return(NULL);
  781. }
  782.  
  783.  
  784. /*  ----------------------------- FindContextStart ------------------------------
  785.  
  786.  Search backwards until a parsed line or a context free line (fold or start of
  787.  file) is found. Return line node. This node may be used as starting point for
  788.  parsing lines.
  789.  
  790. */
  791.  
  792. struct LineNode *
  793. FindContextStart(scanID, lineNode)
  794.  
  795. ULONG            scanID;
  796. struct LineNode *lineNode;
  797. {
  798.     ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
  799.  
  800.     while (line--) {
  801.  
  802.         // found a fold or a parsed line ?
  803.  
  804.         if (lineNode->UserData || IS_FOLD(lineNode))
  805.             break;
  806.  
  807.         --lineNode;
  808.     }
  809.  
  810.     return(lineNode);
  811. }
  812.  
  813.  
  814. /* ------------------------------- ParseContext --------------------------------
  815.  
  816.  Preparse lines. The first line is expected to be either a context free line
  817.  (start of file or a fold header) or to be a parsed line.
  818.  
  819. */
  820.  
  821. void
  822. ParseContext(lineNode, lines, scanID)
  823.  
  824. struct LineNode *lineNode;
  825. ULONG            lines, scanID;
  826. {
  827.     ULONG mode;
  828.  
  829.     for (mode = COMMENT_NONE; lines--; ++lineNode) {
  830.  
  831.         if (IS_FOLD(lineNode))
  832.  
  833.             mode = COMMENT_NONE;
  834.  
  835.         else {
  836.  
  837.             if (lineNode->UserData == EMPTY_STACK)
  838.  
  839.                 mode = COMMENT_NONE;
  840.  
  841.             else if (lineNode->UserData)
  842.  
  843.                 mode = GET_FLAGS(lineNode);
  844.  
  845.             else {
  846.  
  847.                 struct SyntaxInfo *syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, mode, scanID);
  848.  
  849.                 if (syntaxInfo == EMPTY_STACK) {
  850.  
  851.                     lineNode->UserData = EMPTY_STACK;
  852.  
  853.                     mode = COMMENT_NONE;
  854.                 }
  855.                 else if (syntaxInfo) {
  856.  
  857.                     lineNode->UserData = DupInfo(syntaxInfo);
  858.  
  859.                     mode = syntaxInfo->Flags;
  860.                 }
  861.                 else
  862.                     mode = COMMENT_NONE;
  863.             }
  864.         }
  865.     }
  866. }
  867.  
  868.  
  869. /* ----------------------------- ParseContextLine ------------------------------
  870.  
  871.  Parse a string, build a syntax description. Return EMPTY_STACK in case there is
  872.  nothing to highlight. Check whether there are comments. The status of the last
  873.  line is passed in by the caller, this function will return the status (flag
  874.  bits) of the new line.
  875.  
  876. */
  877.  
  878. struct SyntaxInfo *
  879. ParseContextLine(text, len, last, scanID)
  880.  
  881. UBYTE *text;
  882. UWORD  len;
  883. ULONG  last, scanID;
  884. {
  885.     if (len) {
  886.  
  887.         struct SyntaxInfo  *syntaxInfo;
  888.         struct SyntaxChunk *syntaxStack;
  889.         struct Keyword     *keyword;
  890.  
  891.         BOOL   inString, inComment, anyText, innerComment, inQuote;
  892.         UBYTE *next;
  893.         UWORD  indent, startString, startComment, element, lastChar, wordLen, startQuote;
  894.         ULONG  status;
  895.  
  896.         syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  897.  
  898.         // leading spaces have to be ignored
  899.  
  900.         for (indent = 0; len && (*text == ' '); ++indent, --len)
  901.             ++text;
  902.  
  903.         // trailing spaces have to be ignored
  904.  
  905.          while (len && (text[len - 1] == ' '))
  906.             --len;
  907.  
  908.         // preset comment status based on status of previous line
  909.  
  910.         if (last & (COMMENT_START | COMMENT_BODY)) {
  911.  
  912.             status    = COMMENT_BODY;
  913.             inComment = TRUE;
  914.         }
  915.         else {
  916.  
  917.             status    = COMMENT_NONE;
  918.             inComment = FALSE;
  919.         }
  920.  
  921.         startComment = 0;
  922.         innerComment = FALSE;
  923.  
  924.         inString = FALSE;
  925.         inQuote  = FALSE;
  926.         anyText  = FALSE;
  927.  
  928.         lastChar = ' ';
  929.  
  930.         syntaxStack = &syntaxInfo->SyntaxChunk;
  931.  
  932.         for (element = 0; len; ++text, ++indent, --len) {
  933.  
  934.             if (IsKnown[*text]) {
  935.  
  936.                 if (inComment) {
  937.  
  938.                     // end of comment detected ?
  939.  
  940.                     if ((*text == '*') && (text[1] == '/') && (len > 1)) {
  941.  
  942.                         // end of a muli-line comment or end of inner line comment ?
  943.  
  944.                         if (innerComment)
  945.  
  946.                             status |= COMMENT_SIMPLE;
  947.  
  948.                         else {
  949.  
  950.                             status |=  COMMENT_END;
  951.                             status &= ~COMMENT_BODY;
  952.                         }
  953.  
  954.                         syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
  955.                         syntaxStack[element].sc_Start = startComment;
  956.                         syntaxStack[element].sc_End   = indent + 1;
  957.  
  958.                         ++element;
  959.  
  960.                         inComment = FALSE;
  961.  
  962.                         ++text;
  963.                         ++indent;
  964.                         --len;
  965.                     }
  966.                 }
  967.                 else if ((*text == '\'') && (inString == FALSE) && (*(text - 1) != '\\')) {
  968.  
  969.                     if (inQuote = !inQuote)
  970.  
  971.                         startQuote = indent + 1;
  972.  
  973.                     else if (indent > startQuote) {
  974.  
  975.                         // end of string detected
  976.  
  977.                         syntaxStack[element].sc_Level = SYNTAX_QUOTE;
  978.                         syntaxStack[element].sc_Start = startQuote;
  979.                         syntaxStack[element].sc_End   = indent - 1;
  980.  
  981.                         ++element;
  982.                     }
  983.                 }
  984.                 else if ((*text == '\"') && (inQuote == FALSE) && (*(text - 1) != '\\')) {
  985.  
  986.                     if (inString = !inString)
  987.  
  988.                         startString = indent + 1;
  989.  
  990.                     else if (indent > startString) {
  991.  
  992.                         // end of string detected
  993.  
  994.                         syntaxStack[element].sc_Level = SYNTAX_STRING;
  995.                         syntaxStack[element].sc_Start = startString;
  996.                         syntaxStack[element].sc_End   = indent - 1;
  997.  
  998.                         ++element;
  999.                     }
  1000.                 }
  1001.                 else if ((inString == FALSE) && (inQuote == FALSE)) {
  1002.  
  1003.                     if (IsLetter[*text]) {
  1004.  
  1005.                         wordLen = 1;
  1006.  
  1007.                         while (IsLetter[text[wordLen]] && (wordLen < len))
  1008.                             ++wordLen;
  1009.  
  1010.                         // reserved word detected ?
  1011.  
  1012.                         if (Hash[*text]) {
  1013.  
  1014.                             if (IsSpace[lastChar]) {
  1015.  
  1016.                                 if (keyword = IsAReservedWord(text, wordLen)) {
  1017.  
  1018.                                     // environment to be checked ?
  1019.  
  1020.                                     if (keyword->Next || keyword->Required) {
  1021.  
  1022.                                         if (CheckEnvironment(text, len, wordLen, keyword)) {
  1023.  
  1024.                                             syntaxStack[element].sc_Level = keyword->Level;
  1025.                                             syntaxStack[element].sc_Start = indent;
  1026.                                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1027.  
  1028.                                             ++element;
  1029.                                         }
  1030.                                     }
  1031.                                     else {
  1032.  
  1033.                                         syntaxStack[element].sc_Level = keyword->Level;
  1034.                                         syntaxStack[element].sc_Start = indent;
  1035.                                         syntaxStack[element].sc_End   = indent + wordLen - 1;
  1036.  
  1037.                                         ++element;
  1038.                                     }
  1039.                                 }
  1040.                             }
  1041.                         }
  1042.  
  1043.                         // move to next section (consider end-of-loop action)
  1044.  
  1045.                         --wordLen;
  1046.  
  1047.                         text   += wordLen;
  1048.                         indent += wordLen;
  1049.                         len    -= wordLen;
  1050.                     }
  1051.                     else if ((*text >= '0') && (*text <= '9')) {
  1052.  
  1053.                         // number detected
  1054.  
  1055.                         BOOL isHex = (*text == '0') && (text[1] == 'x');
  1056.  
  1057.                         // allows "0x12123UL"
  1058.  
  1059.                         for (next = text + (wordLen = 1); ((UPPER(*next) == 'U' || UPPER(*next) == 'L') || ((*next <= '9') && (*next >= '0')) || (isHex && ((*next == 'x') || ((UPPER(*next) >= 'A') && (UPPER(*next) <= 'F'))))) && (wordLen < len); ++next)
  1060.                             ++wordLen;
  1061.  
  1062.                         // ignore numbers attached to letters (e.g. __A0)
  1063.  
  1064.                         if (IsLetter[lastChar] == FALSE) {
  1065.  
  1066.                             syntaxStack[element].sc_Level = SYNTAX_NUMBER;
  1067.                             syntaxStack[element].sc_Start = indent;
  1068.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1069.  
  1070.                             ++element;
  1071.                         }
  1072.  
  1073.                         // move to next section (consider end-of-loop action)
  1074.  
  1075.                         --wordLen;
  1076.  
  1077.                         text   += wordLen;
  1078.                         indent += wordLen;
  1079.                         len    -= wordLen;
  1080.                     }
  1081.                     else if ((*text == '/') && (text[1] == '/') && (len > 1)) {
  1082.  
  1083.                         // c++ comment detected
  1084.  
  1085.                         syntaxStack[element].sc_Level = SYNTAX_COMMENTCPP;
  1086.                         syntaxStack[element].sc_Start = indent;
  1087.                         syntaxStack[element].sc_End   = indent + len - 1;
  1088.  
  1089.                         ++element;
  1090.  
  1091.                         // terminate parsing (rest of line is a comment)
  1092.  
  1093.                         break;
  1094.                     }
  1095.                     else if ((*text == '/') && (text[1] == '*') && (len > 1)) {
  1096.  
  1097.                         // start of comment detected
  1098.  
  1099.                         inComment    = TRUE;
  1100.                         innerComment = TRUE;
  1101.                         startComment = indent;
  1102.  
  1103.                         ++text;
  1104.                         ++indent;
  1105.                         --len;
  1106.                     }
  1107.                     else if (IsOperator[*text]) {
  1108.  
  1109.                         wordLen = 1;
  1110.  
  1111.                         while (IsOperator[text[wordLen]] && (wordLen < len))
  1112.                             ++wordLen;
  1113.  
  1114.                         // trim sequence (handles special case ++=, *++ and *--)
  1115.  
  1116.                         if (wordLen > 2) {
  1117.  
  1118.                             wordLen = 2;
  1119.  
  1120.                             if (*text == '*') {
  1121.  
  1122.                                 ++text;
  1123.                                 ++indent;
  1124.                                 --len;
  1125.                             }
  1126.                         }
  1127.  
  1128.                         // reserved operator detected ?
  1129.  
  1130.                         if (keyword = IsAReservedOperator(text, wordLen)) {
  1131.  
  1132.                             syntaxStack[element].sc_Level = keyword->Level;
  1133.                             syntaxStack[element].sc_Start = indent;
  1134.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1135.  
  1136.                             ++element;
  1137.                         }
  1138.  
  1139.                         // move to next section (consider end-of-loop action)
  1140.  
  1141.                         --wordLen;
  1142.  
  1143.                         text   += wordLen;
  1144.                         indent += wordLen;
  1145.                         len    -= wordLen;
  1146.                     }
  1147.                     else if ((*text == '{') || (*text == '}')) {
  1148.  
  1149.                         // bracket detected
  1150.  
  1151.                         syntaxStack[element].sc_Level = SYNTAX_BRACKET;
  1152.                         syntaxStack[element].sc_Start = indent;
  1153.                         syntaxStack[element].sc_End   = indent;
  1154.  
  1155.                         ++element;
  1156.                     }
  1157.                     else if (*text == '#') {
  1158.  
  1159.                         // only spaces detected so far ?
  1160.  
  1161.                         if (anyText == FALSE) {
  1162.  
  1163.                             // preprocessor line detected
  1164.  
  1165.                             wordLen = 1;
  1166.  
  1167.                             while ((wordLen < len) && (text[wordLen] == ' '))
  1168.                                 ++wordLen;
  1169.  
  1170.                             // get preprocessor command only
  1171.  
  1172.                             while ((wordLen < len) && (text[wordLen] != ' ')) {
  1173.  
  1174.                                 if ((text[wordLen] == '/') && (wordLen + 1 < len) && (text[wordLen + 1] == '/'))
  1175.                                     break;
  1176.                                 else
  1177.                                     ++wordLen;
  1178.                             }
  1179.  
  1180.                             syntaxStack[element].sc_Level = SYNTAX_PREPROCESSOR;
  1181.                             syntaxStack[element].sc_Start = indent;
  1182.                             syntaxStack[element].sc_End   = indent + wordLen - 1;
  1183.  
  1184.                             // move to end of reserved word (consider end-of-loop action)
  1185.  
  1186.                             --wordLen;
  1187.  
  1188.                             text   += wordLen;
  1189.                             indent += wordLen;
  1190.                             len    -= wordLen;
  1191.  
  1192.                             ++element;
  1193.                         }
  1194.                     }
  1195.                 }
  1196.  
  1197.                 anyText = TRUE;
  1198.             }
  1199.  
  1200.             lastChar = *text;
  1201.         }
  1202.  
  1203.         // comment not closed ?
  1204.  
  1205.         if (inComment) {
  1206.  
  1207.             // continuation of multi-line comment or new (inner line) comment ?
  1208.  
  1209.             if (innerComment)
  1210.                 status |= COMMENT_START;
  1211.             else
  1212.                 status |= COMMENT_BODY;
  1213.  
  1214.             syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
  1215.             syntaxStack[element].sc_Start = startComment;
  1216.             syntaxStack[element].sc_End   = indent - 1;
  1217.  
  1218.             ++element;
  1219.         }
  1220.  
  1221.         if (element) {
  1222.  
  1223.             // terminate syntax stack
  1224.  
  1225.             syntaxStack[element].sc_Start = FALSE;
  1226.             syntaxStack[element].sc_End   = FALSE;
  1227.             syntaxStack[element].sc_Level = FALSE;
  1228.  
  1229.             syntaxInfo->Flags = status;
  1230.  
  1231.             return(syntaxInfo);
  1232.         }
  1233.         else
  1234.             return(EMPTY_STACK);
  1235.     }
  1236.     else if (last) {
  1237.  
  1238.         if (last & (COMMENT_START | COMMENT_BODY)) {
  1239.  
  1240.             struct SyntaxInfo *syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
  1241.  
  1242.             syntaxInfo->SyntaxChunk.sc_Start = FALSE;
  1243.             syntaxInfo->SyntaxChunk.sc_End   = FALSE;
  1244.             syntaxInfo->SyntaxChunk.sc_Level = FALSE;
  1245.  
  1246.             syntaxInfo->Flags = COMMENT_BODY;
  1247.  
  1248.             return(syntaxInfo);
  1249.         }
  1250.         else
  1251.             return(EMPTY_STACK);
  1252.     }
  1253.     else
  1254.         return(EMPTY_STACK);
  1255. }
  1256.  
  1257.  
  1258. /* ------------------------------- VerifyContext -------------------------------
  1259.  
  1260.  Ensure that syntax information of a specified range of lines ("damage region")
  1261.  is valid and consistent with the existing text. Return a refresh request if
  1262.  not. The damage area is expected to have been unparsed already. This is what we
  1263.  have to do: we preparse existing lines before the damage area if belonging to
  1264.  the syntax context of the damage area (ie. all lines affecting highlighting of
  1265.  the first line in the damage area). The damage area is parsed, too. Parsed
  1266.  lines after the damage area are reparsed if highlighting is found to be
  1267.  inconsistent with the line(s) before. Reparsing continues until the end of the
  1268.  file respectively until no more inconsistencies are found.
  1269.  
  1270. */
  1271.  
  1272. struct RefreshRequest *
  1273. VerifyContext(scanID, line, lines)
  1274.  
  1275. ULONG scanID, line, lines;
  1276. {
  1277.     struct EditConfig *config;
  1278.     struct LineNode   *lineNode, *lastNode;
  1279.     struct SyntaxInfo *syntaxInfo;
  1280.  
  1281.     ULONG last, new, refreshStart, refresh = FALSE;
  1282.  
  1283.     config = ((struct BufferHandle *)scanID)->bh_EditConfig;
  1284.  
  1285.     lineNode = config->TextNodes + line;
  1286.     lastNode = config->TextNodes + line + (lines - 1);
  1287.  
  1288.     // preparse from context start until end of damage area
  1289.  
  1290.     ParseSection(scanID, lineNode, lines);
  1291.  
  1292.     // get syntax flags of last line in damage area
  1293.  
  1294.     if ((lastNode < config->TextNodes) || IS_FOLD(lastNode) || (lastNode->UserData == EMPTY_STACK) || (lastNode->UserData == NULL))
  1295.         last = COMMENT_NONE;
  1296.     else
  1297.         last = GET_FLAGS(lastNode);
  1298.  
  1299.     // continue parsing until no more inconsistencies are found (until context end)
  1300.  
  1301.     refreshStart = (line += lines);
  1302.  
  1303.     for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
  1304.  
  1305.         if (IS_FOLD(lineNode))
  1306.  
  1307.             // folds terminate parsing (context end)
  1308.  
  1309.             break;
  1310.  
  1311.         else if (lineNode->UserData == NULL) {
  1312.  
  1313.             // line not yet parsed
  1314.  
  1315.             syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1316.  
  1317.             if (syntaxInfo == EMPTY_STACK)
  1318.  
  1319.                 lineNode->UserData = EMPTY_STACK;
  1320.  
  1321.             else if (syntaxInfo)
  1322.  
  1323.                 lineNode->UserData = DupInfo(syntaxInfo);
  1324.         }
  1325.         else {
  1326.  
  1327.             // check whether highlighting of this line is consistent with previous line
  1328.  
  1329.             new = (lineNode->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(lineNode);
  1330.  
  1331.             if (BadSuccessor(last, new)) {
  1332.  
  1333.                 if (lineNode->UserData != EMPTY_STACK)
  1334.                     FreeVec(lineNode->UserData);
  1335.  
  1336.                 syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
  1337.  
  1338.                 if (syntaxInfo == EMPTY_STACK)
  1339.  
  1340.                     lineNode->UserData = EMPTY_STACK;
  1341.  
  1342.                 else if (syntaxInfo)
  1343.  
  1344.                     lineNode->UserData = DupInfo(syntaxInfo);
  1345.             }
  1346.             else
  1347.                 break;
  1348.         }
  1349.  
  1350.         if ((lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL) || IS_FOLD(lineNode))
  1351.             last = COMMENT_NONE;
  1352.         else
  1353.             last = GET_FLAGS(lineNode);
  1354.     }
  1355.  
  1356.     if (refresh) {
  1357.  
  1358.         struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
  1359.  
  1360.         refreshRequest->rr_Line  = refreshStart;
  1361.         refreshRequest->rr_Lines = refresh;
  1362.  
  1363.         return(refreshRequest);
  1364.     }
  1365.     else
  1366.         return(NULL);
  1367. }
  1368.  
  1369.  
  1370. /* ------------------------------- BadSuccessor --------------------------------
  1371.  
  1372.  Return TRUE if syntax information of two adjacent lines is inconsistent.
  1373.  
  1374. */
  1375.  
  1376. BOOL
  1377. BadSuccessor(pred, succ)
  1378.  
  1379. ULONG pred, succ;
  1380. {
  1381.     if (succ & (COMMENT_BODY | COMMENT_END)) {
  1382.  
  1383.         // comment body/end without start ?
  1384.  
  1385.         if ((pred & (COMMENT_START | COMMENT_BODY)) == FALSE)
  1386.  
  1387.             return(TRUE);
  1388.     }
  1389.     else if (pred & COMMENT_START) {
  1390.  
  1391.         // comment start without body/end ?
  1392.  
  1393.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1394.  
  1395.             return(TRUE);
  1396.  
  1397.         // comment inside comment ?
  1398.  
  1399.         if (succ & COMMENT_START)
  1400.  
  1401.             return(TRUE);
  1402.     }
  1403.     else if (pred & COMMENT_BODY) {
  1404.  
  1405.         // body line without end ?
  1406.  
  1407.         if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
  1408.  
  1409.             return(TRUE);
  1410.     }
  1411.  
  1412.     return(FALSE);
  1413. }
  1414.  
  1415. ///
  1416.