static void findSchemeTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = line; if (cp [0] == '(' && (cp [1] == 'D' || cp [1] == 'd') && (cp [2] == 'E' || cp [2] == 'e') && (cp [3] == 'F' || cp [3] == 'f')) { while (*cp != '\0' && !isspace (*cp)) cp++; /* Skip over open parens and white space */ do { while (*cp != '\0' && (isspace (*cp) || *cp == '(')) cp++; if (*cp == '\0') cp = line = readLineFromInputFile (); else break; } while (line); if (line == NULL) break; readIdentifier (name, cp); makeSimpleTag (name, SchemeKinds, K_FUNCTION); } if (cp [0] == '(' && (cp [1] == 'S' || cp [1] == 's') && (cp [2] == 'E' || cp [2] == 'e') && (cp [3] == 'T' || cp [3] == 't') && (cp [4] == '!') && (isspace (cp [5]) || cp[5] == '\0')) { cp += 5; /* Skip over white space */ do { while (*cp != '\0' && isspace (*cp)) cp++; if (*cp == '\0') cp = line = readLineFromInputFile (); else break; } while (line); if (line == NULL) break; readIdentifier (name, cp); makeSimpleTag (name, SchemeKinds, K_SET); } } vStringDelete (name); }
static void findClojureTags (void) { vString *name = vStringNew (); const char *p; int scope_index = CORK_NIL; while ((p = (char *)readLineFromInputFile ()) != NULL) { vStringClear (name); while (isspace (*p)) p++; if (*p == '(') { if (isNamespace (p)) { skipToSymbol (&p); scope_index = makeNamespaceTag (name, p); } else if (isFunction (p)) { skipToSymbol (&p); makeFunctionTag (name, p, scope_index); } } } vStringDelete (name); }
static void findMarkdownTags (void) { vString *name = vStringNew(); const unsigned char *line; while ((line = readLineFromInputFile()) != NULL) { int name_len = vStringLength(name); /* underlines must be the same length or more */ if (name_len > 0 && (line[0] == '=' || line[0] == '-') && issame((const char*) line)) { makeMarkdownTag(name, true); } else if (line[0] == '#') { vStringClear(name); vStringCatS(name, (const char *) line); makeMarkdownTag(name, false); } else { vStringClear (name); if (! isspace(*line)) vStringCatS(name, (const char*) line); } } vStringDelete (name); }
static void findBasicTags (void) { const char *line; KeyWord *keywords; keywords = freebasic_keywords; while ((line = (const char *) readLineFromInputFile ()) != NULL) { const char *p = line; KeyWord const *kw; while (isspace (*p)) p++; /* Empty line or comment? */ if (!*p || *p == '\'') continue; /* In Basic, keywords always are at the start of the line. */ for (kw = keywords; kw->token; kw++) if (match_keyword (p, kw)) break; /* Is it a label? */ match_colon_label (p); } }
static void eatComment (lexingState * st) { bool unfinished = true; bool lastIsStar = false; const unsigned char *c = st->cp + 2; while (unfinished) { /* we've reached the end of the line.. * so we have to reload a line... */ if (c == NULL || *c == '\0') { st->cp = readLineFromInputFile (); /* WOOPS... no more input... * we return, next lexing read * will be null and ok */ if (st->cp == NULL) return; c = st->cp; } /* we've reached the end of the comment */ else if (*c == '/' && lastIsStar) unfinished = false; else { lastIsStar = '*' == *c; c++; } } st->cp = c; }
static void eatComment (lexingState * st) { boolean unfinished = TRUE; boolean lastIsStar = FALSE; const unsigned char *c = st->cp + 2; while (unfinished) { /* we've reached the end of the line.. * so we have to reload a line... */ if (c == NULL || *c == '\0') { st->cp = readLineFromInputFile (); /* WOOPS... no more input... * we return, next lexing read * will be null and ok */ if (st->cp == NULL) return; c = st->cp; } /* we've reached the end of the comment */ else if (*c == ')' && lastIsStar) unfinished = FALSE; /* here we deal with imbricated comment, which * are allowed in OCaml */ else if (c[0] == '(' && c[1] == '*') { st->cp = c; eatComment (st); c = st->cp; if (c == NULL) return; lastIsStar = FALSE; c++; } /* OCaml has a rule which says : * * "Comments do not occur inside string or character literals. * Nested comments are handled correctly." * * So if we encounter a string beginning, we must parse it to * get a good comment nesting (bug ID: 3117537) */ else if (*c == '"') { st->cp = c; eatString (st); c = st->cp; } else { lastIsStar = '*' == *c; c++; } } st->cp = c; }
static void findOcamlTags (void) { vString *name = vStringNew (); lexingState st; ocaToken tok; initStack (); computeModuleName (); tempIdent = vStringNew (); lastModule = vStringNew (); lastClass = vStringNew (); voidName = vStringNew (); vStringCopyS (voidName, "_"); st.name = vStringNew (); st.cp = readLineFromInputFile (); toDoNext = &globalScope; tok = lex (&st); while (tok != Tok_EOF) { (*toDoNext) (st.name, tok); tok = lex (&st); } vStringDelete (st.name); vStringDelete (name); vStringDelete (voidName); vStringDelete (tempIdent); vStringDelete (lastModule); vStringDelete (lastClass); clearStack (); }
static void findAwkTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { if (strncmp ((const char*) line, "function", (size_t) 8) == 0 && isspace ((int) line [8])) { const unsigned char *cp = line + 8; while (isspace ((int) *cp)) ++cp; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } vStringTerminate (name); while (isspace ((int) *cp)) ++cp; if (*cp == '(') makeSimpleTag (name, AwkKinds, K_FUNCTION); vStringClear (name); if (*cp != '\0') ++cp; } } vStringDelete (name); }
static void findDocBookTags(void) { const char *line; while ((line = (const char*)readLineFromInputFile()) != NULL) { const char *cp = line; for (; *cp != '\0'; cp++) { if (*cp == '<') { cp++; /* <section id="..."> */ if (getWord("section", &cp)) { createTag(K_SECTION, cp); continue; } /* <sect1 id="..."> */ if (getWord("sect1", &cp)) { createTag(K_SECT1, cp); continue; } /* <sect2 id="..."> */ if (getWord("sect2", &cp)) { createTag(K_SECT2, cp); continue; } /* <sect3 id="..."> */ if (getWord("sect3", &cp) || getWord("sect4", &cp) || getWord("sect5", &cp)) { createTag(K_SECT3, cp); continue; } /* <chapter id="..."> */ if (getWord("chapter", &cp)) { createTag(K_CHAPTER, cp); continue; } /* <appendix id="..."> */ if (getWord("appendix", &cp)) { createTag(K_APPENDIX, cp); continue; } } } } }
/* * Perl subroutine declaration may look like one of the following: * * sub abc; * sub abc :attr; * sub abc (proto); * sub abc (proto) :attr; * * Note that there may be more than one attribute. Attributes may * have things in parentheses (they look like arguments). Anything * inside of those parentheses goes. Prototypes may contain semi-colons. * The matching end when we encounter (outside of any parentheses) either * a semi-colon (that'd be a declaration) or an left curly brace * (definition). * * This is pretty complicated parsing (plus we all know that only perl can * parse Perl), so we are only promising best effort here. * * If we can't determine what this is (due to a file ending, for example), * we will return false. */ static bool isSubroutineDeclaration (const unsigned char *cp) { bool attr = false; int nparens = 0; do { for ( ; *cp; ++cp) { SUB_DECL_SWITCH: switch (*cp) { case ':': if (nparens) break; else if (true == attr) return false; /* Invalid attribute name */ else attr = true; break; case '(': ++nparens; break; case ')': --nparens; break; case ' ': case '\t': break; case ';': if (!nparens) return true; case '{': if (!nparens) return false; default: if (attr) { if (isIdentifier1(*cp)) { cp++; while (isIdentifier (*cp)) cp++; attr = false; goto SUB_DECL_SWITCH; /* Instead of --cp; */ } else { return false; } } else if (nparens) { break; } else { return false; } } } } while (NULL != (cp = readLineFromInputFile ())); return false; }
static const unsigned char *readVimballLine (void) { const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { break; } return line; }
static void findResTags(void) { const unsigned char *line; ResParserState state = P_STATE_NONE; _blockDepth = 0; while ((line = readLineFromInputFile()) != NULL) { state = parseResLine(line, state); if (state == P_STATE_AT_END) return; } }
static void findSmlTags (void) { vString *const identifier = vStringNew (); const unsigned char *line; smlKind lastTag = K_NONE; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = skipSpace (line); do { smlKind foundTag; if (CommentLevel != 0) { cp = (const unsigned char *) strstr ((const char *) cp, "*)"); if (cp == NULL) continue; else { --CommentLevel; cp += 2; } } foundTag = findNextIdentifier (&cp); if (foundTag != K_NONE) { cp = skipSpace (cp); cp = parseIdentifier (cp, identifier); if (foundTag == K_AND) { if (lastTag != K_NONE) makeSmlTag (lastTag, identifier); } else { makeSmlTag (foundTag, identifier); lastTag = foundTag; } } if (strstr ((const char *) cp, "(*") != NULL) { cp += 2; cp = (const unsigned char *) strstr ((const char *) cp, "*)"); if (cp == NULL) ++CommentLevel; } } while (cp != NULL && strcmp ((const char *) cp, "") != 0); } vStringDelete (identifier); }
static void findShTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char* cp = line; bool functionFound = false; if (line [0] == '#') continue; while (isspace (*cp)) cp++; if (strncmp ((const char*) cp, "function", (size_t) 8) == 0 && isspace ((int) cp [8])) { functionFound = true; cp += 8; if (! isspace ((int) *cp)) continue; while (isspace ((int) *cp)) ++cp; } if (! (isalnum ((int) *cp) || *cp == '_')) continue; while (isalnum ((int) *cp) || *cp == '_') { vStringPut (name, (int) *cp); ++cp; } while (isspace ((int) *cp)) ++cp; if (*cp++ == '(') { while (isspace ((int) *cp)) ++cp; if (*cp == ')' && ! hackReject (name)) functionFound = true; } if (functionFound) makeSimpleTag (name, ShKinds, K_FUNCTION); vStringClear (name); } vStringDelete (name); }
static const unsigned char *readVimLine (void) { const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { while (isspace ((int) *line)) ++line; if ((int) *line == '"') continue; /* skip comment */ break; } return line; }
/* Algorithm adapted from from GNU etags. */ static void findLispTags (void) { vString *name = vStringNew (); const unsigned char* p; while ((p = readLineFromInputFile ()) != NULL) { if (*p == '(') { if (L_isdef (p)) { while (*p != '\0' && !isspace ((int) *p)) p++; while (isspace ((int) *p)) p++; L_getit (name, p); } else { /* Check for (foo::defmumble name-defined ... */ do p++; while (*p != '\0' && !isspace ((int) *p) && *p != ':' && *p != '(' && *p != ')'); if (*p == ':') { do p++; while (*p == ':'); if (L_isdef (p - 1)) { while (*p != '\0' && !isspace ((int) *p)) p++; while (isspace (*p)) p++; L_getit (name, p); } } } } } vStringDelete (name); }
/* Parse constants declared via hash reference, like this: * use constant { * A => 1, * B => 2, * }; * The approach we take is simplistic, but it covers the vast majority of * cases well. There can be some false positives. * Returns 0 if found the end of the hashref, -1 if we hit EOF */ static int parseConstantsFromHashRef (const unsigned char *cp, vString *name, vString *package) { while (1) { enum const_state state = parseConstantsFromLine((const char *) cp, name, package); switch (state) { case CONST_STATE_NEXT_LINE: cp = readLineFromInputFile(); if (cp) break; else return -1; case CONST_STATE_HIT_END: return 0; } } }
/* TODO: parse overlining & underlining as distinct sections. */ static void findRstTags (void) { vString *name = vStringNew (); fpos_t filepos; const unsigned char *line; memset(&filepos, 0, sizeof(fpos_t)); memset(kindchars, 0, sizeof kindchars); nestingLevels = nestingLevelsNew(); while ((line = readLineFromInputFile ()) != NULL) { int line_len = strlen((const char*) line); int name_len_bytes = vStringLength(name); int name_len = utf8_strlen(vStringValue(name), name_len_bytes); /* if the name doesn't look like UTF-8, assume one-byte charset */ if (name_len < 0) name_len = name_len_bytes; /* underlines must be the same length or more */ if (line_len >= name_len && name_len > 0 && ispunct(line[0]) && issame((const char*) line)) { char c = line[0]; int kind = get_kind(c); if (kind >= 0) { makeRstTag(name, kind, filepos); continue; } } vStringClear (name); if (!isspace(*line)) { vStringCatS(name, (const char*)line); filepos = getInputFilePosition(); } vStringTerminate(name); } vStringDelete (name); nestingLevelsFree(nestingLevels); }
static void findTclTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp; while (isspace (line [0])) ++line; if (line [0] == '\0' || line [0] == '#') continue; /* read first word */ for (cp = line ; *cp != '\0' && ! isspace ((int) *cp) ; ++cp) ; if (! isspace ((int) *cp)) continue; while (isspace ((int) *cp)) ++cp; /* Now `line' points at first word and `cp' points at next word */ if (match (line, "proc")) cp = makeTclTag (cp, name, K_PROCEDURE); else if (match (line, "class") || match (line, "itcl::class")) cp = makeTclTag (cp, name, K_CLASS); else if (match (line, "public") || match (line, "protected") || match (line, "private")) { if (match (cp, "method")) { cp += 6; while (isspace ((int) *cp)) ++cp; cp = makeTclTag (cp, name, K_METHOD); } } } vStringDelete (name); }
static void found_macro_cb (const char *line, const regexMatch *matches, unsigned int count, void *uesrData) { struct macro_cb_data *data = uesrData; if (count > 0) { vString *signature = ((count > 1) && (matches[2].length > 0))? vStringNew(): NULL; vString *name = vStringNew (); tagEntryInfo tag; if (signature) vStringNCopyS (signature, line + matches[2].start, matches[2].length); vStringNCopyS (name, line + matches[1].start, matches[1].length); if (data->rindex == ROLE_INDEX_DEFINITION) initTagEntry (&tag, vStringValue (name), &(RpmSpecKinds[data->kindex])); else initRefTagEntry (&tag, vStringValue (name), &(RpmSpecKinds[data->kindex]), data->rindex); if (signature) tag.extensionFields.signature = vStringValue (signature); /* Skip the definition */ while (line && is_line_continued (line)) { rejecting = TRUE; line = (const char *)readLineFromInputFile (); } rejecting = FALSE; tag.extensionFields.endLine = getInputLineNumber(); makeTagEntry (&tag); vStringDelete (name); if (signature) vStringDelete (signature); } }
extern void makeFileTag (const char *const fileName) { boolean via_line_directive = (strcmp (fileName, getInputFileName()) != 0); xtagType xtag = XTAG_UNKNOWN; if (isXtagEnabled(XTAG_FILE_NAMES)) xtag = XTAG_FILE_NAMES; if (isXtagEnabled(XTAG_FILE_NAMES_WITH_TOTAL_LINES)) xtag = XTAG_FILE_NAMES_WITH_TOTAL_LINES; if (xtag != XTAG_UNKNOWN) { tagEntryInfo tag; kindOption *kind; kind = getInputLanguageFileKind(); Assert (kind); kind->enabled = isXtagEnabled(XTAG_FILE_NAMES); /* TODO: you can return here if enabled == FALSE. */ initTagEntry (&tag, baseFilename (fileName), kind); tag.isFileEntry = TRUE; tag.lineNumberEntry = TRUE; markTagExtraBit (&tag, xtag); if (via_line_directive || (!isXtagEnabled(XTAG_FILE_NAMES_WITH_TOTAL_LINES))) { tag.lineNumber = 1; } else { while (readLineFromInputFile () != NULL) ; /* Do nothing */ tag.lineNumber = getInputLineNumber (); } makeTagEntry (&tag); } }
/* Read next contiguous sequence of non-whitespace characters, store * the address in `ptok', and return its length. Return value of zero * means EOF. * * TODO: Currently, POD and multi-line comments are not handled. */ static int getNonSpaceStr (struct p6Ctx *ctx, const char **ptok) { const char *s = ctx->line; if (!s) { next_line: s = (const char *) readLineFromInputFile(); if (!s) return 0; /* EOF */ } while (*s && isspace(*s)) /* Skip whitespace */ ++s; if ('#' == *s) goto next_line; int non_white_len = strcspn(s, ",; \t"); if (non_white_len) { ctx->line = s + non_white_len; /* Save state */ *ptok = s; return non_white_len; } else goto next_line; }
static void findAbaqusTags(void) { const char *line; while ((line = (const char*)readLineFromInputFile()) != NULL) { const char *cp = line; for (; *cp != '\0'; cp++) { if (*cp == '*') { cp++; /* Parts*/ if (getWord("part", &cp)) { createTag(K_PART, cp); continue; } /* Assembly */ if (getWord("assembly", &cp)) { createTag(K_ASSEMBLY, cp); continue; } /* Steps */ if (getWord("step", &cp)) { createTag(K_STEP, cp); continue; } } } } }
static void findBasicTags (void) { const char *line; const char *extension = fileExtension (getInputFileName ()); KeyWord *keywords; if (strcmp (extension, "bb") == 0) keywords = blitzbasic_keywords; else if (strcmp (extension, "pb") == 0) keywords = purebasic_keywords; else keywords = freebasic_keywords; while ((line = (const char *) readLineFromInputFile ()) != NULL) { const char *p = line; KeyWord const *kw; while (isspace (*p)) p++; /* Empty line? */ if (!*p) continue; /* In Basic, keywords always are at the start of the line. */ for (kw = keywords; kw->token; kw++) if (match_keyword (p, kw)) break; /* Is it a label? */ if (strcmp (extension, "bb") == 0) match_dot_label (p); else match_colon_label (p); } }
static void findErlangTags (void) { vString *const module = vStringNew (); const unsigned char *line; while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = line; if (*cp == '%') /* skip initial comment */ continue; if (*cp == '"') /* strings sometimes start in column one */ continue; if ( *cp == '-') { ++cp; /* Move off of the '-' */ parseDirective(cp, module); } else if (isIdentifierFirstCharacter ((int) *cp)) parseFunctionTag (cp, module); } vStringDelete (module); }
static void findJavaPropertiesTags (void) { const unsigned char *line; bool in_value = false; bool value_continues; static vString *key; if (key == NULL) key = vStringNew (); else vStringClear (key); while ((line = readLineFromInputFile ()) != NULL) { if (in_value) { value_continues = doesValueContinue (line); if (!value_continues) in_value = false; continue; } line = skipWhiteSpace (line); if (*line == '\0' || *line == '!' || *line == '#') continue; line = extractKey (line, key); makeSimpleTag (key, K_KEY); vStringClear (key); value_continues = doesValueContinue (line); if (value_continues) in_value = true; } }
static void findAbcTags (void) { vString *name = vStringNew(); const unsigned char *line; while ((line = readLineFromInputFile()) != NULL) { /*int name_len = vStringLength(name);*/ /* underlines must be the same length or more */ /*if (name_len > 0 && (line[0] == '=' || line[0] == '-') && issame((const char*) line)) { makeAbcTag(name, TRUE); }*/ /* if (line[1] == '%') { vStringClear(name); vStringCatS(name, (const char *) line); vStringTerminate(name); makeAbcTag(name, FALSE); }*/ if (line[0] == 'T') { /*vStringClear(name);*/ vStringCatS(name, " / "); vStringCatS(name, (const char *) line); vStringTerminate(name); makeAbcTag(name, FALSE); } else { vStringClear (name); if (! isspace(*line)) vStringCatS(name, (const char*) line); vStringTerminate(name); } } vStringDelete (name); }
/* The lexer is in charge of reading the file. * Some of sub-lexer (like eatComment) also read file. * lexing is finished when the lexer return Tok_EOF */ static objcKeyword lex (lexingState * st) { int retType; /* handling data input here */ while (st->cp == NULL || st->cp[0] == '\0') { st->cp = readLineFromInputFile (); if (st->cp == NULL) return Tok_EOF; return Tok_EOL; } if (isAlpha (*st->cp) || (*st->cp == '_')) { readIdentifier (st); retType = lookupKeyword (vStringValue (st->name), Lang_ObjectiveC); if (retType == -1) /* If it's not a keyword */ { return ObjcIDENTIFIER; } else { return retType; } } else if (*st->cp == '@') { readIdentifierObjcDirective (st); retType = lookupKeyword (vStringValue (st->name), Lang_ObjectiveC); if (retType == -1) /* If it's not a keyword */ { return Tok_any; } else { return retType; } } else if (isSpace (*st->cp)) { eatWhiteSpace (st); return lex (st); } else switch (*st->cp) { case '(': st->cp++; return Tok_PARL; case '\\': st->cp++; return Tok_Backslash; case '#': st->cp++; return Tok_Sharp; case '/': if (st->cp[1] == '*') /* ergl, a comment */ { eatComment (st); return lex (st); } else if (st->cp[1] == '/') { st->cp = NULL; return lex (st); } else { st->cp++; return Tok_any; } break; case ')': st->cp++; return Tok_PARR; case '{': st->cp++; return Tok_CurlL; case '}': st->cp++; return Tok_CurlR; case '[': st->cp++; return Tok_SQUAREL; case ']': st->cp++; return Tok_SQUARER; case ',': st->cp++; return Tok_COMA; case ';': st->cp++; return Tok_semi; case ':': st->cp++; return Tok_dpoint; case '"': eatString (st); return Tok_any; case '+': st->cp++; return Tok_PLUS; case '-': st->cp++; return Tok_MINUS; case '*': st->cp++; return Tok_Asterisk; case '<': st->cp++; return Tok_ANGLEL; case '>': st->cp++; return Tok_ANGLER; default: st->cp++; break; } /* default return if nothing is recognized, * shouldn't happen, but at least, it will * be handled without destroying the parsing. */ return Tok_any; }
static void findRubyTags (void) { const unsigned char *line; boolean inMultiLineComment = FALSE; nesting = nestingLevelsNew (0); /* FIXME: this whole scheme is wrong, because Ruby isn't line-based. * You could perfectly well write: * * def * method * puts("hello") * end * * if you wished, and this function would fail to recognize anything. */ while ((line = readLineFromInputFile ()) != NULL) { const unsigned char *cp = line; /* if we expect a separator after a while, for, or until statement * separators are "do", ";" or newline */ boolean expect_separator = FALSE; if (canMatch (&cp, "=begin", isWhitespace)) { inMultiLineComment = TRUE; continue; } if (canMatch (&cp, "=end", isWhitespace)) { inMultiLineComment = FALSE; continue; } if (inMultiLineComment) continue; skipWhitespace (&cp); /* Avoid mistakenly starting a scope for modifiers such as * * return if <exp> * * FIXME: this is fooled by code such as * * result = if <exp> * <a> * else * <b> * end * * FIXME: we're also fooled if someone does something heinous such as * * puts("hello") \ * unless <exp> */ if (canMatchKeyword (&cp, "for") || canMatchKeyword (&cp, "until") || canMatchKeyword (&cp, "while")) { expect_separator = TRUE; enterUnnamedScope (); } else if (canMatchKeyword (&cp, "case") || canMatchKeyword (&cp, "if") || canMatchKeyword (&cp, "unless")) { enterUnnamedScope (); } /* * "module M", "class C" and "def m" should only be at the beginning * of a line. */ if (canMatchKeyword (&cp, "module")) { readAndEmitTag (&cp, K_MODULE); } else if (canMatchKeyword (&cp, "class")) { readAndEmitTag (&cp, K_CLASS); } else if (canMatchKeyword (&cp, "def")) { rubyKind kind = K_METHOD; NestingLevel *nl = nestingLevelsGetCurrent (nesting); tagEntryInfo *e = getEntryOfNestingLevel (nl); /* if the def is inside an unnamed scope at the class level, assume * it's from a singleton from a construct like this: * * class C * class << self * def singleton * ... * end * end * end */ if (e && (e->kind - RubyKinds) == K_CLASS && strlen (e->name) == 0) kind = K_SINGLETON; readAndEmitTag (&cp, kind); } while (*cp != '\0') { /* FIXME: we don't cope with here documents, * or regular expression literals, or ... you get the idea. * Hopefully, the restriction above that insists on seeing * definitions at the starts of lines should keep us out of * mischief. */ if (inMultiLineComment || isspace (*cp)) { ++cp; } else if (*cp == '#') { /* FIXME: this is wrong, but there *probably* won't be a * definition after an interpolated string (where # doesn't * mean 'comment'). */ break; } else if (canMatchKeyword (&cp, "begin")) { enterUnnamedScope (); } else if (canMatchKeyword (&cp, "do")) { if (! expect_separator) enterUnnamedScope (); else expect_separator = FALSE; } else if (canMatchKeyword (&cp, "end") && nesting->n > 0) { /* Leave the most recent scope. */ nestingLevelsPop (nesting); } else if (*cp == '"') { /* Skip string literals. * FIXME: should cope with escapes and interpolation. */ do { ++cp; } while (*cp != 0 && *cp != '"'); if (*cp == '"') cp++; /* skip the last found '"' */ } else if (*cp == ';') { ++cp; expect_separator = FALSE; } else if (*cp != '\0') { do ++cp; while (isIdentChar (*cp)); } } } nestingLevelsFree (nesting); }
/* Algorithm adapted from from GNU etags. * Locates tags for procedures & functions. Doesn't do any type- or * var-definitions. It does look for the keyword "extern" or "forward" * immediately following the procedure statement; if found, the tag is * skipped. */ static void findPascalTags (void) { vString *name = vStringNew (); tagEntryInfo tag; pascalKind kind = K_FUNCTION; /* each of these flags is TRUE iff: */ boolean incomment = FALSE; /* point is inside a comment */ int comment_char = '\0'; /* type of current comment */ boolean inquote = FALSE; /* point is inside '..' string */ boolean get_tagname = FALSE;/* point is after PROCEDURE/FUNCTION keyword, so next item = potential tag */ boolean found_tag = FALSE; /* point is after a potential tag */ boolean inparms = FALSE; /* point is within parameter-list */ boolean verify_tag = FALSE; /* point has passed the parm-list, so the next token will determine * whether this is a FORWARD/EXTERN to be ignored, or whether it is a * real tag */ dbp = readLineFromInputFile (); while (dbp != NULL) { int c = *dbp++; if (c == '\0') /* if end of line */ { dbp = readLineFromInputFile (); if (dbp == NULL || *dbp == '\0') continue; if (!((found_tag && verify_tag) || get_tagname)) c = *dbp++; /* only if don't need *dbp pointing to the beginning of * the name of the procedure or function */ } if (incomment) { if (comment_char == '{' && c == '}') incomment = FALSE; else if (comment_char == '(' && c == '*' && *dbp == ')') { dbp++; incomment = FALSE; } continue; } else if (inquote) { if (c == '\'') inquote = FALSE; continue; } else switch (c) { case '\'': inquote = TRUE; /* found first quote */ continue; case '{': /* found open { comment */ incomment = TRUE; comment_char = c; continue; case '(': if (*dbp == '*') /* found open (* comment */ { incomment = TRUE; comment_char = c; dbp++; } else if (found_tag) /* found '(' after tag, i.e., parm-list */ inparms = TRUE; continue; case ')': /* end of parms list */ if (inparms) inparms = FALSE; continue; case ';': if (found_tag && !inparms) /* end of proc or fn stmt */ { verify_tag = TRUE; break; } continue; } if (found_tag && verify_tag && *dbp != ' ') { /* check if this is an "extern" declaration */ if (*dbp == '\0') continue; if (tolower ((int) *dbp == 'e')) { if (tail ("extern")) /* superfluous, really! */ { found_tag = FALSE; verify_tag = FALSE; } } else if (tolower ((int) *dbp) == 'f') { if (tail ("forward")) /* check for forward reference */ { found_tag = FALSE; verify_tag = FALSE; } } if (found_tag && verify_tag) /* not external proc, so make tag */ { found_tag = FALSE; verify_tag = FALSE; makePascalTag (&tag); continue; } } if (get_tagname) /* grab name of proc or fn */ { const unsigned char *cp; if (*dbp == '\0') continue; /* grab block name */ while (isspace ((int) *dbp)) ++dbp; for (cp = dbp ; *cp != '\0' && !endtoken (*cp) ; cp++) continue; vStringNCopyS (name, (const char*) dbp, cp - dbp); createPascalTag (&tag, name, kind); dbp = cp; /* set dbp to e-o-token */ get_tagname = FALSE; found_tag = TRUE; /* and proceed to check for "extern" */ } else if (!incomment && !inquote && !found_tag) { switch (tolower ((int) c)) { case 'c': if (tail ("onstructor")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'd': if (tail ("estructor")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'p': if (tail ("rocedure")) { get_tagname = TRUE; kind = K_PROCEDURE; } break; case 'f': if (tail ("unction")) { get_tagname = TRUE; kind = K_FUNCTION; } break; } } /* while not eof */ } vStringDelete (name); }