home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 4 / AACD04.ISO / AACD / Programming / envsof20 / source / syntax / funcs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-01  |  18.1 KB  |  662 lines

  1. /*
  2.  * funcs.c -- Cached Eiffel syntax parser for GoldEd Studio 6.
  3.  *
  4.  * Copyright 1999 Thomas Aglassinger and others, see file "forum.txt"
  5.  */
  6. #define DEBUG 0
  7.  
  8. #define PARSER_NAME     "Eiffel"
  9. #define PARSER_VERSION  "2.3"
  10. #define PARSER_VERSION_ID 1
  11.  
  12. #include "defs.h"
  13. #include "debug.h"
  14. #include "language.h"
  15. #include "keyword.h"
  16.  
  17. /// "Header stuff"
  18.  
  19. // we extend the ParserHandle structure for our private data
  20.  
  21. struct MyParserHandle {
  22.  
  23.    struct ParserHandle ParserHandle;   // embedded parser handle
  24.  
  25.    struct SyntaxChunk *SyntaxStack;    // parser output
  26.  
  27.    struct EditConfig *EditConfig;   // buffer data
  28.  
  29.    struct keyword_info *keyword_info;  // keywords to be recognised
  30.  
  31. };
  32.  
  33. #define EMPTY_STACK ((struct SyntaxChunk *)~0)  // empty stack flag
  34.  
  35. extern char is_normal[256];
  36. extern char is_alpha[256];
  37. extern char is_numeric[256];
  38. extern char is_upper[256];
  39. extern char is_lower[256];
  40.  
  41. // names of built-in syntax levels
  42.  
  43. const static UBYTE *levelNames[SYNTAX_MAXIMUM + 1] =
  44. {
  45.    "Standard text",
  46.    "Comment",
  47.    "Manifest string",
  48.    "Keyword",
  49.    "Reserved word",
  50.    "Type name",
  51.    "Constant name",
  52.    // "Routine declaration",
  53.    "Error or flaw",
  54.    NULL
  55. };
  56.  
  57. ///
  58.  
  59. /// "Prototype"
  60.  
  61. // library functions
  62.  
  63. Prototype LibCall struct ParserData     *MountScanner(void);
  64. Prototype LibCall struct ParserHandle   *StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *, __D1 struct SyntaxSetup *);
  65. Prototype LibCall ULONG                  CloseScanner(__A0 struct ParserHandle *);
  66. Prototype LibCall void                   FlushScanner(__A0 struct ParserHandle *);
  67. Prototype LibCall void                   SetupScanner(__A0 struct GlobalConfig *);
  68. Prototype LibCall struct RefreshRequest *BriefScanner(__A0 struct ParserHandle *, __A1 struct ScannerNotify *);
  69. Prototype LibCall struct SyntaxChunk    *ParseLine   (__A0 struct ParserHandle *, __A1 struct LineNode *, __D0 ULONG);
  70. Prototype LibCall void                   UnparseLines(__A0 struct LineNode *, __D0 ULONG);
  71. Prototype LibCall void                   ParseSection(__A0 struct ParserHandle *, __A1 struct LineNode *, __D0 ULONG);
  72.  
  73. // private functions
  74.  
  75. Prototype struct SyntaxChunk            *ParseString (UBYTE *, UWORD, struct ParserHandle *);
  76. Prototype struct SyntaxChunk            *DupStack    (struct SyntaxChunk *);
  77.  
  78. ///
  79.  
  80. /// "Library functions"
  81.  
  82. /* ------------------------------- MountScanner --------------------------------
  83.  * 
  84.  * Called by the editor before first usage of a scanner. Return a description of
  85.  * our abilities.
  86.  * 
  87.  */
  88.  
  89. LibCall struct ParserData *
  90. MountScanner()
  91. {
  92.    static UBYTE version[] = "$VER: " PARSER_NAME " " PARSER_VERSION " (" __COMMODORE_DATE__ ")";
  93.  
  94.    static struct ParserData parserData;
  95.  
  96.    // syntax elements understood by parser
  97.  
  98.    const static UBYTE *example[] =
  99.    {
  100.       "indexing                       ",
  101.       "  description: \"Say hello.\"    ",
  102.       "                               ",
  103.       "class                          ",
  104.       "   HELLO                       ",
  105.       "                               ",
  106.       "creation {ANY}                 ",
  107.       "   make                        ",
  108.       "                               ",
  109.       "feature {ANY}                  ",
  110.       "                               ",
  111.       "   make is                     ",
  112.       "      do                       ",
  113.       "         print(\"hello%         ",
  114.       "               % world\")       ",
  115.       "         print('%N')           ",
  116.       "      end -- make              ",
  117.       "                               ",
  118.       "   Almost_pi: REAL is 3.1415   ",
  119.       "                               ",
  120.       "   Is_nice: BOOLEAN is True;   ",
  121.       "                               ",
  122.       "   DontUseSuchNames: LOOSER    ",
  123.       "      -- only Java cunts do it ",
  124.       "                               ",
  125.       "end -- class HELLO             ",
  126.       NULL
  127.    };
  128.  
  129.    setup_char_array();
  130.    create_keyword_list();
  131.  
  132.    parserData.pd_Release = SCANLIBVERSION;
  133.    parserData.pd_Version = 1;
  134.    parserData.pd_Serial = 0;
  135.    parserData.pd_Info = PARSER_NAME " " PARSER_VERSION;
  136.    parserData.pd_Example = example;
  137.    parserData.pd_Properties = 0;
  138.  
  139.    // signal editor that we cache syntax information
  140.  
  141.    parserData.pd_Flags = SCPRF_SYNTAXCACHE;
  142.  
  143.    return (&parserData);
  144. }
  145.  
  146. /* ------------------------------- StartScanner --------------------------------
  147.  * 
  148.  * Called by the editor after a new text buffer has been created. We allocate a
  149.  * buffer to hold text-specific data. The buffer address is returned as handle.
  150.  * 
  151.  */
  152.  
  153. /* ------------------------------- StartScanner --------------------------------
  154.  * 
  155.  * Called by the editor after a new text buffer has been created. We return a
  156.  * parser handle (extended with our private data).
  157.  * 
  158.  */
  159.  
  160. LibCall struct ParserHandle *
  161. StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack, __D1 struct SyntaxSetup *syntaxSetup)
  162. {
  163. #define TEMPLATE "FILE"
  164.    struct MyParserHandle *handle = NULL;
  165.    UBYTE *settings_argument = syntaxSetup->sp_UserData;  // parser argument string
  166.  
  167.    ULONG length = 0;            // length of parser argument string
  168.  
  169.    STRPTR scan_buffer;          // contains copy of parser argument string with "\n"
  170.  
  171.    static enum {
  172.       ARG_FILE, ARG_MAX
  173.    };
  174.    LONG argument[ARG_MAX] =
  175.    {NULL};
  176.    argument[ARG_FILE] = (LONG) "golded:add-ons/eiffel/syntax/eiffel.keyword";
  177.  
  178.    // Copy parser arguments to buffer and append "\n"
  179.    if (settings_argument != NULL) {
  180.       length = strlen(settings_argument);
  181.    }
  182.    scan_buffer = AllocVec(length + 2, MEMF_ANY);
  183.    if (scan_buffer != NULL) {
  184.       if (length > 0) {
  185.          strcpy(scan_buffer, settings_argument);
  186.       }
  187.       scan_buffer[length] = '\n';
  188.       scan_buffer[length + 1] = '\0';
  189.    }
  190.    if (scan_buffer != NULL) {
  191.       struct RDArgs *scanner = (struct RDArgs *) AllocDosObject(DOS_RDARGS, NULL);
  192.  
  193.       if (scanner != NULL) {
  194.          scanner->RDA_Source.CS_Buffer = scan_buffer;
  195.          scanner->RDA_Source.CS_Length = (LONG) strlen(scan_buffer);
  196.  
  197.          if (ReadArgs(TEMPLATE, argument, scanner)) {
  198.  
  199.             if (handle = AllocVec(sizeof(struct MyParserHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
  200.  
  201.                handle->keyword_info = keywords_of((STRPTR) argument[ARG_FILE]);
  202.  
  203.                if (handle->keyword_info != NULL) {
  204.                   handle->ParserHandle.ph_Levels = SYNTAX_MAXIMUM;
  205.                   handle->ParserHandle.ph_Names = levelNames;
  206.                   handle->ParserHandle.ph_ColorsFG = NULL;
  207.                   handle->ParserHandle.ph_ColorsBG = NULL;
  208.                   handle->SyntaxStack = syntaxStack;
  209.                   handle->EditConfig = editConfigPtr;
  210.                } else {
  211.                   FreeVec(handle);
  212.                   handle = NULL;
  213.                }
  214.             }
  215.             FreeArgs(scanner);
  216.          }
  217.          FreeDosObject(DOS_RDARGS, scanner);
  218.       }
  219.       FreeVec(scan_buffer);
  220.    }
  221.    return ((ULONG) handle);
  222. }
  223.  
  224. /* ------------------------------- CloseScanner --------------------------------
  225.  * 
  226.  * Called by the editor if a text buffer is about to be closed. Deallocate buffer
  227.  * specific 'global' data.
  228.  * 
  229.  */
  230.  
  231. LibCall ULONG
  232. CloseScanner(__A0 struct ParserHandle * handle)
  233. {
  234.    D(bug("CloseScanner()\n"));
  235.    if (handle) {
  236.  
  237.       struct MyParserHandle *my_handle = (struct *MyParserHandle) *handle;
  238.  
  239.       dispose_keyword_info(my_handle->keyword_info);
  240.  
  241.       FreeVec(handle);
  242.    }
  243.    return (0);
  244. }
  245.  
  246. /* ------------------------------- FlushScanner --------------------------------
  247.  * 
  248.  * Called by the editor in low memory situations: we are supposed to free as much
  249.  * memory as possible.
  250.  * 
  251.  */
  252.  
  253. LibCall void 
  254. FlushScanner(__A0 struct ParserHandle *handle)
  255. {
  256.    D(bug("FlushScanner()\n"));
  257.    struct EditConfig *config = ((struct MyParserHandle *) handle)->EditConfig;
  258.  
  259.    if (config->TextNodes && config->Lines) {
  260.       UnparseLines(config->TextNodes, config->Lines);
  261.    }
  262. }
  263.  
  264. /* ------------------------------- SetupScanner --------------------------------
  265.  * 
  266.  * Called by the editor if the user wants to change the scanner's configuration.
  267.  * We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
  268.  * unset).
  269.  * 
  270.  */
  271.  
  272. LibCall void
  273. SetupScanner(__A0 globalConfigPtr)
  274. {
  275.    ;
  276. }
  277.  
  278. /* ------------------------------- BriefScanner --------------------------------
  279.  * 
  280.  * Called to notify a context scanner if lines have been added, deleted or
  281.  * modified. We aren't a context scanner (parserData.pd_Flags: SCPRF_CONTEXT
  282.  * flag unset), so we won't ever have to request additional display requests:
  283.  * the editor's built-in refresh of damage regions is sufficient.
  284.  * 
  285.  */
  286.  
  287. LibCall struct RefreshRequest *
  288. BriefScanner(__A0 struct ParserHandle *handle, __A1 struct ScannerNotify *notify)
  289. {
  290.    return (NULL);
  291. }
  292.  
  293. /* --------------------------------- ParseLine ---------------------------------
  294.  * 
  295.  * Parse a line, build a syntax description
  296.  * 
  297.  */
  298.  
  299. LibCall struct SyntaxChunk *
  300. ParseLine(__A0 struct ParserHandle *handle, __A1 struct LineNode *lineNode, __D0 ULONG line)
  301. {
  302.    if (IS_FOLD(lineNode))
  303.       return (NULL);
  304.  
  305.    else if (lineNode->Len) {
  306.  
  307.       // line not yet parsed ?
  308.  
  309.       if (lineNode->UserData == NULL) {
  310.  
  311.          struct SyntaxChunk *syntaxStack = ParseString(lineNode->Text, lineNode->Len, handle);
  312.  
  313.          if (syntaxStack == EMPTY_STACK)
  314.             lineNode->UserData = EMPTY_STACK;
  315.          else
  316.             lineNode->UserData = DupStack(syntaxStack);
  317.       }
  318.       if (lineNode->UserData == EMPTY_STACK)
  319.          return ((struct SyntaxChunk *) NULL);
  320.       else
  321.          return ((struct SyntaxChunk *) lineNode->UserData);
  322.    } else
  323.       return (NULL);
  324. }
  325.  
  326. /* -------------------------------- UnparseLines -------------------------------
  327.  * 
  328.  * Called by the editor if lines are to be deleted. We are supposed to free
  329.  * private data attached to the lines.
  330.  * 
  331.  */
  332.  
  333. LibCall void
  334. UnparseLines(__A0 struct LineNode *lineNode, __D0 ULONG lines)
  335. {
  336.    while (lines--) {
  337.  
  338.       // free syntax cache
  339.  
  340.       if (lineNode->UserData) {
  341.  
  342.          if (lineNode->UserData != (APTR) EMPTY_STACK)
  343.             FreeVec((APTR) lineNode->UserData);
  344.  
  345.          lineNode->UserData = NULL;
  346.       }
  347.       // free folded subblock
  348.  
  349.       if (IS_FOLD(lineNode)) {
  350.  
  351.          struct Fold *fold = (struct Fold *) lineNode->SpecialInfo;
  352.  
  353.          UnparseLines(fold->TextNodes, fold->Lines);
  354.       }
  355.       ++lineNode;
  356.    }
  357. }
  358.  
  359. /* -------------------------------- ParseSection -------------------------------
  360.  * 
  361.  * Called by the editor if lines are to be displayed. The scanner is encouraged to
  362.  * preparse the lines.
  363.  * 
  364.  */
  365.  
  366. LibCall void 
  367. ParseSection(__A0 struct ParserHandle *handle, __A1 struct LineNode *lineNode, __D0 ULONG lines)
  368. {
  369. #if 0
  370.    while (lines--) {
  371.  
  372.       // fold headers have to be ignored
  373.  
  374.       if (IS_FOLD(lineNode) == FALSE) {
  375.  
  376.          // line not yet parsed ?
  377.  
  378.          if (lineNode->Len)
  379.             if (lineNode->UserData == NULL)
  380.                lineNode->UserData = DupStack(ParseString(lineNode->Text, lineNode->Len, handle));
  381.       }
  382.       ++lineNode;
  383.    }
  384. #endif
  385. }
  386.  
  387. ///
  388.  
  389. /// "private"
  390.  
  391. /* -------------------------------- getNameType ---------------------------- */
  392. UBYTE
  393. getNameType(UBYTE * text, ULONG begin, ULONG end, struct keyword_info *info)
  394. {
  395. #if 1
  396.    UBYTE word[500];
  397.    LONG word_index = begin;
  398.  
  399.    while (word_index <= end) {
  400.       word[word_index - begin] = text[word_index];
  401.       word_index += 1;
  402.    }
  403.    word[word_index - begin] = 0;
  404.  
  405.    D(bug("  check word: \""));
  406.    D(bug(word));
  407.    D(bug("\"\n"));
  408. #endif
  409.  
  410. #if 1
  411.    return get_word_type(text, begin, end + 1, info);
  412. #else
  413.    return SYNTAX_KEYWORD;
  414. #endif
  415. }
  416.  
  417. /* -------------------------------- ParseString --------------------------------
  418.  * 
  419.  * Parse a string, build a syntax description. Return EMPTY_STACK in case there
  420.  * is nothing to highlight.
  421.  * 
  422.  */
  423.  
  424. struct SyntaxChunk *
  425. ParseString(UBYTE * text, UWORD len, struct ParserHandle *handle)
  426. {
  427. #define ADD_ELEMENT(start,end,level)           \
  428. {                                              \
  429.     syntaxStack[element].sc_Start = (start);   \
  430.     syntaxStack[element].sc_End   = (end);     \
  431.     syntaxStack[element].sc_Level = (level);   \
  432.     element += 1;                              \
  433.     D(bug("    elem: "));                      \
  434.     D(kint(start));                            \
  435.     D(bug(" "));                               \
  436.     D(kint(end));                              \
  437.     D(bug(" "));                               \
  438.     D(kint(level));                            \
  439.     D(bug("\n"));                              \
  440. }
  441.  
  442.    UBYTE *lineStart = text;
  443.    UWORD lenStart = len;
  444.  
  445.    if (len) {
  446.       struct MyParserHandle *my_handle = (struct MyParserHandle *) handle;
  447.       struct SyntaxChunk *syntaxStack = my_handle->SyntaxStack;
  448.       struct keyword_info *info = my_handle->keyword_info;
  449.  
  450.       UWORD element, indent;
  451.       UWORD stringStart, stringEnd;
  452.       UWORD nameStart, nameEnd;
  453.       // if in string/character, this is either (") or ('), otherwise 0
  454.       UBYTE inString = 0;
  455.       // if in name, this is the first character of the name, otherwise 0
  456.       UBYTE inName = 0;
  457.       BOOL inComment = FALSE;
  458.       BOOL afterPercent = FALSE;
  459.  
  460.       // leading and trailing spaces have to be ignored
  461.       for (indent = 0; (len && (*text <= 32)); --len, ++indent)
  462.          ++text;
  463.       while (len && (text[len - 1] <= 32))
  464.          --len;
  465.  
  466.       // no syntax elements found so far
  467.       element = 0;
  468.  
  469.       if (*text == '%') {
  470.  
  471.          inString = '"';
  472.          afterPercent = TRUE;
  473.          stringStart = indent;
  474.       }
  475.       for (inComment = FALSE; len >= 1; ++text, ++indent, --len) {
  476.  
  477.          if (inName) {
  478.  
  479.             if (!is_normal[*text]) {
  480.                int nameType;
  481.                UBYTE *ends_with = " ";
  482.                ends_with[0] = *text;
  483.  
  484.                // end of new name
  485.                D(bug("  Name ends with \""));
  486.                D(bug(ends_with));
  487.                D(bug("\"\n"));
  488.  
  489.                // go back one char so it is parsed again
  490.                text -= 1;
  491.                indent -= 1;
  492.                len += 1;
  493.                ends_with[0] = *text;
  494.                D(bug("  Now at \""));
  495.                D(bug(ends_with));
  496.                D(bug("\"\n"));
  497.  
  498.                nameEnd = indent;
  499.                nameType = getNameType(lineStart, nameStart, nameEnd, info);
  500.  
  501.                if (nameType != SYNTAX_TEXT) {
  502.                   ADD_ELEMENT(nameStart, nameEnd, nameType);
  503.                }
  504.                inName = 0;
  505.             }
  506.          } else if (inString) {
  507.  
  508.             if (afterPercent) {
  509.  
  510.                // skip escaped character
  511.                afterPercent = FALSE;
  512.  
  513.             } else if (*text == inString) {
  514.  
  515.                // end of string detected
  516.  
  517.                D(bug("  String end: "));
  518.  
  519.                stringEnd = indent - 1;
  520.                ADD_ELEMENT(stringStart, stringEnd, SYNTAX_STRING);
  521.                inString = 0;
  522.                afterPercent = FALSE;
  523.  
  524.                D(kint(stringStart));
  525.                D(bug(" - "));
  526.                D(kint(stringEnd));
  527.                D(bug("\n"));
  528.  
  529.             } else if (*text == '%') {
  530.  
  531.                afterPercent = TRUE;
  532.             }
  533.          } else if ((*text == 34) || (*text == 39)) {
  534.  
  535.             D(bug("  String start\n"));
  536.  
  537.             // start of string detected
  538.             inString = *text;
  539.             afterPercent = FALSE;
  540.             stringStart = indent + 1;
  541.  
  542.          } else if ((len >= 2) && (text[0] == '-') && (text[1] == '-')) {
  543.  
  544.             // comment until end of line
  545.             ADD_ELEMENT(indent, indent + len - 1, SYNTAX_COMMENT);
  546.             break;
  547.          } else if (is_alpha[*text]) {
  548.  
  549.             // Start of a new name */
  550.             D(bug("  Name start\n"));
  551.  
  552.             // start of name detected
  553.             inName = *text;
  554.             afterPercent = FALSE;
  555.             nameStart = indent;
  556.          } else {
  557.             UBYTE *ends_with = " ";
  558.             ends_with[0] = *text;
  559.  
  560.             // end of new name
  561.             D(bug("ignore \""));
  562.             D(bug(ends_with));
  563.             D(bug("\"\n"));
  564.          }
  565.       }
  566.  
  567.       // go back one char so the last char of the line is addressed
  568.       text -= 1;
  569.       indent -= 1;
  570.       len += 1;
  571.  
  572.       // unterminated string or name? highlight rest of line
  573.  
  574.       if (inString) {
  575.  
  576.          UBYTE level = SYNTAX_STRING;
  577.          stringEnd = indent + len - 1;
  578.  
  579.          if (*text != '%') {
  580.             stringEnd += 0;
  581.             level = SYNTAX_BAD;
  582.          }
  583.          ADD_ELEMENT(stringStart, stringEnd, level);
  584.       } else if (inName) {
  585.          UBYTE *buf = "  ";
  586.  
  587.          buf[0] = text[-1];
  588.          buf[1] = text[0];
  589.          D(bug("last word ends: \""));
  590.          D(bug(buf));
  591.          D(bug("\"\n"));
  592.  
  593.          if (!is_normal[*text]) {
  594.             D(bug("  (decreased)\n"));
  595.             len -= 1;
  596.          }
  597.          nameEnd = indent + len - 1;
  598.          ADD_ELEMENT(nameStart, nameEnd,
  599.                      getNameType(lineStart, nameStart, nameEnd, info));
  600.       }
  601.       if (element) {
  602.  
  603.          // check, if last element is "is". If so, first element is
  604.          // a feature declaration
  605. #if 0
  606.          struct SyntaxChunk *firstElement = &(syntaxStack[0]);
  607.          struct SyntaxChunk *lastElement = &(syntaxStack[element - 1]);
  608.  
  609.          if ((lastElement->sc_Level == SYNTAX_KEYWORD)
  610.              && ((lastElement->sc_End - lastElement->sc_Start) == 2)
  611.             ) {
  612.             firstElement->sc_Level = SYNTAX_DECLARATION;
  613.          }
  614. #endif
  615.  
  616.          // terminate syntax stack
  617.          ADD_ELEMENT(FALSE, FALSE, FALSE);
  618.          return (syntaxStack);
  619.       } else
  620.          return (EMPTY_STACK);
  621.  
  622.    } else
  623.       return (EMPTY_STACK);
  624. }
  625.  
  626. /* --------------------------------- DupStack ----------------------------------
  627.  * 
  628.  * Duplicate syntax stack (to be FreeVec'ed). Return NULL in case of failure.
  629.  * 
  630.  */
  631.  
  632. struct SyntaxChunk *
  633. DupStack(struct SyntaxChunk *syntaxStack)
  634. {
  635.    if (syntaxStack && (syntaxStack != EMPTY_STACK)) {
  636.  
  637.       struct SyntaxChunk *chunk;
  638.       UWORD elements;
  639.  
  640.       // determine stack size
  641.  
  642.       for (elements = 0, chunk = syntaxStack; chunk->sc_Level; ++chunk)
  643.          ++elements;
  644.  
  645.       // create copy of syntax stack (to be attached to a text line by the caller)
  646.  
  647.       if (elements) {
  648.  
  649.          ULONG size = (++elements) * sizeof(struct SyntaxChunk);
  650.  
  651.          chunk = syntaxStack;
  652.  
  653.          if (syntaxStack = AllocVec(size, MEMF_PUBLIC))
  654.             movmem(chunk, syntaxStack, size);
  655.       } else
  656.          syntaxStack = EMPTY_STACK;
  657.    }
  658.    return (syntaxStack);
  659. }
  660.  
  661. ///
  662.