/* Ensure a constructor is not a type path beginning * with a module */ static void constructorValidation (vString * const ident, ocaToken what) { switch (what) { case Tok_Op: /* if we got a '.' which is an operator */ toDoNext = &globalScope; popStrongContext (); needStrongPoping = FALSE; break; case OcaKEYWORD_of: /* OK, it must be a constructor :) */ makeTagEntry (&tempTag); vStringClear (tempIdent); toDoNext = &tillTokenOrFallback; comeAfter = &typeSpecification; waitedToken = Tok_Pipe; break; case Tok_Pipe: /* OK, it was a constructor :) */ makeTagEntry (&tempTag); vStringClear (tempIdent); toDoNext = &typeSpecification; break; default: /* and mean that we're not facing a module name */ makeTagEntry (&tempTag); vStringClear (tempIdent); toDoNext = &tillTokenOrFallback; comeAfter = &typeSpecification; waitedToken = Tok_Pipe; /* nothing in the context, discard it */ popStrongContext (); /* to be sure we use this token */ globalScope (ident, what); } }
/* Double-quoted strings, we only care about the \" escape. These * last past the end of the line, so be careful not too store too much * of them (see MAX_STRING_LENGTH). The only place we look at their * contents is in the function definitions, and there the valid strings are * things like "C" and "Rust" */ static void scanString (lexerState *lexer) { vStringClear(lexer->token_str); advanceChar(lexer); while (lexer->cur_c != EOF && lexer->cur_c != '"') { if (lexer->cur_c == '\\' && lexer->next_c == '"') advanceChar(lexer); if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) vStringPut(lexer->token_str, (char) lexer->cur_c); advanceChar(lexer); } advanceChar(lexer); }
static const unsigned char *makeTclTag ( const unsigned char *cp, vString *const name, const tclKind kind) { vStringClear (name); while ((int) *cp != '\0' && ! isspace ((int) *cp)) { vStringPut (name, (int) *cp); ++cp; } makeSimpleTag (name, TclKinds, kind); return cp; }
static void popScope (tokenInfo *const token, const tokenInfo *const parent) { char *dot = strrchr (token->scope->buffer, '.'); if (! dot) vStringClear (token->scope); else { *dot = 0; token->scope->length = dot - token->scope->buffer; } token->scopeKind = parent->scopeKind; }
static bool readIdentifier (vString *const name, int c) { vStringClear (name); if (isIdentifierCharacter (c)) { while (isIdentifierCharacter (c)) { vStringPut (name, c); c = vGetc (); } vUngetc (c); } return (bool)(name->length > 0); }
static const unsigned char *readOperator ( const unsigned char *const start, vString *const operator) { const unsigned char *cp = start; vStringClear (operator); while (*cp != '\0' && ! isspace ((int) *cp)) { vStringPut (operator, *cp); ++cp; } vStringTerminate (operator); return cp; }
/* parse object ... * used to be sure the class definition is not a type * alias */ static void classSpecif (vString * const UNUSED (ident), ocaToken what) { switch (what) { case OcaKEYWORD_object: pushStrongContext (lastClass, ContextClass); toDoNext = &globalScope; break; default: vStringClear (lastClass); toDoNext = &globalScope; } }
/* `end' points to the equal sign. Parse from right to left to get the * identifier. Assume we're dealing with something of form \s*\w+\s*=> */ static void makeTagFromLeftSide (const char *begin, const char *end, vString *name, vString *package) { tagEntryInfo entry; const char *b, *e; if (! PerlKinds[K_CONSTANT].enabled) return; for (e = end - 1; e > begin && isspace(*e); --e) ; if (e < begin) return; for (b = e; b >= begin && isIdentifier(*b); --b) ; /* Identifier must be either beginning of line of have some whitespace * on its left: */ if (b < begin || isspace(*b) || ',' == *b) ++b; else if (b != begin) return; Assert(e - b + 1 > 0); vStringClear(name); vStringNCatS(name, b, e - b + 1); initTagEntry(&entry, vStringValue(name)); entry.kind = PerlKinds[K_CONSTANT].letter; entry.kindName = PerlKinds[K_CONSTANT].name; makeTagEntry(&entry); if (Option.include.qualifiedTags && package && vStringLength(package)) { vStringClear(name); vStringCopy(name, package); vStringNCatS(name, b, e - b + 1); initTagEntry(&entry, vStringValue(name)); entry.kind = PerlKinds[K_CONSTANT].letter; entry.kindName = PerlKinds[K_CONSTANT].name; makeTagEntry(&entry); } }
/* Raw strings look like this: r"" or r##""## where the number of * hashes must match */ static void scanRawString (lexerState *lexer) { size_t num_initial_hashes = 0; vStringClear(lexer->token_str); advanceChar(lexer); /* Count how many leading hashes there are */ while (lexer->cur_c == '#') { num_initial_hashes++; advanceChar(lexer); } if (lexer->cur_c != '"') return; advanceChar(lexer); while (lexer->cur_c != EOF) { if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) vStringPut(lexer->token_str, (char) lexer->cur_c); /* Count how many trailing hashes there are. If the number is equal or more * than the number of leading hashes, break. */ if (lexer->cur_c == '"') { size_t num_trailing_hashes = 0; advanceChar(lexer); while (lexer->cur_c == '#' && num_trailing_hashes < num_initial_hashes) { num_trailing_hashes++; if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) vStringPut(lexer->token_str, (char) lexer->cur_c); advanceChar(lexer); } if (num_trailing_hashes == num_initial_hashes) { /* Strip the trailing hashes and quotes */ if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH && vStringLength(lexer->token_str) > num_trailing_hashes + 1) { lexer->token_str->length = vStringLength(lexer->token_str) - num_trailing_hashes - 1; lexer->token_str->buffer[lexer->token_str->length] = '\0'; } break; } } else { advanceChar(lexer); } } }
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; } }
/* The name of the language interpreter, either directly or as the argument * to "env". */ static vString* determineInterpreter (const char* const cmd) { vString* const interpreter = vStringNew (); const char* p = cmd; do { vStringClear (interpreter); for ( ; isspace ((int) *p) ; ++p) ; /* no-op */ for ( ; *p != '\0' && ! isspace ((int) *p) ; ++p) vStringPut (interpreter, (int) *p); vStringTerminate (interpreter); } while (strcmp (vStringValue (interpreter), "env") == 0); return interpreter; }
/* read the @something directives */ static void readIdentifierObjcDirective (lexingState * st) { const unsigned char *p; vStringClear (st->name); /* first char is a simple letter */ if (*st->cp == '@') vStringPut (st->name, (int) *st->cp); /* Go till you get identifier chars */ for (p = st->cp + 1; isIdent (*p); p++) vStringPut (st->name, (int) *p); st->cp = p; }
static void findShTags (void) { vString *name = vStringNew (); const unsigned char *line; while ((line = fileReadLine ()) != NULL) { const unsigned char* cp = line; boolean 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; } vStringTerminate (name); 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 bool readIdentifier (tokenInfo *const token, int c) { vStringClear (token->name); if (isIdentifierCharacter (c)) { while (isIdentifierCharacter (c)) { vStringPut (token->name, c); c = vGetc (); } vUngetc (c); token->lineNumber = getInputLineNumber (); token->filePosition = getInputFilePosition (); } return (bool)(vStringLength (token->name) > 0); }
static const unsigned char *readSymbol ( const unsigned char *const start, vString *const sym) { const unsigned char *cp = start; vStringClear (sym); if (isInitialSymbolCharacter ((int) *cp)) { while (isSymbolCharacter ((int) *cp)) { vStringPut (sym, *cp); ++cp; } } return cp; }
/* Parse type of kind * type bidule = Ctor1 of ... * | Ctor2 * | Ctor3 of ... * or * type bidule = | Ctor1 of ... | Ctor2 * * when type bidule = { ... } is detected, * let typeRecord handle it. */ static void typeSpecification (vString * const ident, ocaToken what) { switch (what) { case OcaIDENTIFIER: if (isUpperAlpha (ident->buffer[0])) { /* here we handle type aliases of type * type foo = AnotherModule.bar * AnotherModule can mistakenly be took * for a constructor. */ if (! OcamlKinds[K_CONSTRUCTOR].enabled) vStringClear (tempIdent); else { vStringCopy (tempIdent, ident); prepareTag (&tempTag, tempIdent, K_CONSTRUCTOR); } toDoNext = &constructorValidation; } else { toDoNext = &tillTokenOrFallback; comeAfter = &typeSpecification; waitedToken = Tok_Pipe; } break; case OcaKEYWORD_and: toDoNext = &typeDecl; break; case Tok_BRL: /* the '[' & ']' are ignored to accommodate */ case Tok_BRR: /* with the revised syntax */ case Tok_Pipe: /* just ignore it */ break; case Tok_CurlL: toDoNext = &typeRecord; break; default: /* don't care */ break; } }
static void readIdentifier (lexingState * st) { const unsigned char *p; vStringClear (st->name); /* first char is a simple letter */ if (isAlpha (*st->cp) || *st->cp == '_') vStringPut (st->name, (int) *st->cp); /* Go till you get identifier chars */ for (p = st->cp + 1; isIdent (*p); p++) vStringPut (st->name, (int) *p); st->cp = p; vStringTerminate (st->name); }
static void initPhpEntry (tagEntryInfo *const e, const tokenInfo *const token, const phpKind kind, const accessType access) { int parentKind = -1; vStringClear (FullScope); if (vStringLength (CurrentNamesapce) > 0) { parentKind = K_NAMESPACE; vStringCat (FullScope, CurrentNamesapce); } initTagEntry (e, vStringValue (token->string), kind); e->lineNumber = token->lineNumber; e->filePosition = token->filePosition; if (access != ACCESS_UNDEFINED) e->extensionFields.access = accessToString (access); if (vStringLength (token->scope) > 0) { parentKind = token->parentKind; if (vStringLength (FullScope) > 0) { const char* sep; sep = phpScopeSeparatorFor (parentKind, K_NAMESPACE); vStringCatS (FullScope, sep); } vStringCat (FullScope, token->scope); } if (vStringLength (FullScope) > 0) { Assert (parentKind >= 0); e->extensionFields.scopeKindIndex = parentKind; e->extensionFields.scopeName = vStringValue (FullScope); } if (token->anonymous) markTagExtraBit (e, XTAG_ANONYMOUS); }
/* Used at the beginning of a new scope (begin of a * definition, parenthesis...) to catch inner let * definition that may be in. */ static void mayRedeclare (vString * const ident, ocaToken what) { switch (what) { case OcaKEYWORD_value: /* let globalScope handle it */ globalScope (ident, what); break; case OcaKEYWORD_let: case OcaKEYWORD_val: toDoNext = localLet; break; case OcaKEYWORD_object: vStringClear (lastClass); pushContext (ContextStrong, ContextClass, &localScope, NULL /*voidName */ ); needStrongPoping = FALSE; toDoNext = &globalScope; break; case OcaKEYWORD_for: case OcaKEYWORD_while: toDoNext = &tillToken; waitedToken = OcaKEYWORD_do; comeAfter = &mayRedeclare; break; case OcaKEYWORD_try: toDoNext = &mayRedeclare; pushSoftContext (matchPattern, ident, ContextFunction); break; case OcaKEYWORD_fun: toDoNext = &matchPattern; break; /* Handle the special ;; from the OCaml * Top level */ case Tok_semi: default: toDoNext = &localScope; localScope (ident, what); } }
static void readIdentifier (const int first, vString *const id) { int depth = 0; int c = first; vStringClear (id); while (isIdentifier (c) || (depth > 0 && c != EOF && c != '\n')) { if (c == '(' || c == '}') depth++; else if (depth > 0 && (c == ')' || c == '}')) depth--; vStringPut (id, c); c = nextChar (); } ungetcToInputFile (c); vStringTerminate (id); }
static void parseFunction (const unsigned char *line) { vString *name = vStringNew (); /* boolean inFunction = FALSE; */ int scope; const unsigned char *cp = line; if ((int) *cp == '!') ++cp; if (isspace ((int) *cp)) { while (*cp && isspace ((int) *cp)) ++cp; if (*cp) { cp = skipPrefix (cp, &scope); if (isupper ((int) *cp) || scope == 's' || /* script scope */ scope == '<' || /* script scope */ scope == 'd' || /* dictionary */ scope == 'a') /* autoload */ { do { vStringPut (name, (int) *cp); ++cp; } while (isalnum ((int) *cp) || *cp == '_' || *cp == '.' || *cp == '#'); vStringTerminate (name); makeSimpleTag (name, VimKinds, K_FUNCTION); vStringClear (name); } } } /* TODO - update struct to indicate inside function */ while ((line = readVimLine ()) != NULL) { if (wordMatchLen (line, "endfunction", 4)) break; parseVimLine(line, TRUE); } vStringDelete (name); }
/* unroll the stack until the last named context. * then discard it. Used to handle the : * let f x y = ... * in ... * where the context is reseted after the in. Context may have * been really nested before that. */ static void popLastNamed ( void ) { int i = getLastNamedIndex (); if (i >= 0) { stackIndex = i; toDoNext = stack[i].callback; vStringClear (stack[i].contextName); } else { /* ok, no named context found... * (should not happen). */ stackIndex = 0; toDoNext = &globalScope; } }
static void makeNamespaceTag (vString * const name, const unsigned char *dbp) { vStringClear (CurrentNamespace); functionName (name, dbp); vStringCopy (CurrentNamespace, name); if (vStringLength (CurrentNamespace) > 0) { tagEntryInfo e; initTagEntry (&e, vStringValue (CurrentNamespace)); e.lineNumber = getSourceLineNumber (); e.filePosition = getInputFilePosition (); e.kindName = ClojureKinds[K_NAMESPACE].name; e.kind = (char) ClojureKinds[K_NAMESPACE].letter; makeTagEntry (&e); } }
/* 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); }
/* Reset everything until the last global space. * a strong context can be : * - module * - class definition * - the initial global space * - a _global_ delcaration (let at global scope or in a module). * Created to exit quickly deeply nested context */ static contextType popStrongContext ( void ) { int i; for (i = stackIndex - 1; i >= 0; --i) { if (stack[i].kind == ContextStrong) { stackIndex = i; toDoNext = stack[i].callback; vStringClear (stack[i].contextName); return stack[i].type; } } /* ok, no strong context found... */ stackIndex = 0; toDoNext = &globalScope; return -1; }
static void L_getit (vString *const name, const unsigned char *dbp) { const unsigned char *p; if (*dbp == '\'') /* Skip prefix quote */ dbp++; else if (*dbp == '(' && L_isquote (dbp)) /* Skip "(quote " */ { dbp += 7; while (isspace (*dbp)) dbp++; } for (p=dbp ; *p!='\0' && *p!='(' && !isspace ((int) *p) && *p!=')' ; p++) vStringPut (name, *p); if (vStringLength (name) > 0) makeSimpleTag (name, LispKinds, K_FUNCTION); vStringClear (name); }
/* Essentially grabs the last ident before 'for', '<' and '{', which * tends to correspond to what we want as the impl tag entry name */ static void parseQualifiedType (lexerState *lexer, vString* name) { while (lexer->cur_token != TOKEN_EOF) { if (lexer->cur_token == TOKEN_IDENT) { if (strcmp(lexer->token_str->buffer, "for") == 0) break; vStringClear(name); vStringCat(name, lexer->token_str); } else if (lexer->cur_token == '<' || lexer->cur_token == '{') { break; } advanceToken(lexer, TRUE); } skipTypeBlock(lexer); }
static void parseStructMembers (vString * const ident, objcToken what) { static parseNext prev = NULL; if (prev != NULL) { comeAfter = prev; prev = NULL; } switch (what) { case ObjcIDENTIFIER: vStringCopy (tempName, ident); break; case Tok_semi: /* ';' */ addTag (tempName, K_FIELD); vStringClear (tempName); break; /* some types are complex, the only one * we will loose is the function type. */ case Tok_CurlL: /* '{' */ case Tok_PARL: /* '(' */ case Tok_SQUAREL: /* '[' */ toDoNext = &ignoreBalanced; prev = comeAfter; comeAfter = &parseStructMembers; ignoreBalanced (ident, what); break; case Tok_CurlR: toDoNext = comeAfter; break; default: /* don't care */ break; } }
extern boolean convertString (vString *const string) { size_t dest_len, src_len; char *dest, *dest_ptr, *src; if (iconv_fd == (iconv_t) -1) return FALSE; src_len = vStringLength (string); /* Should be longest length of bytes. so maybe utf8. */ dest_len = src_len * 4; dest_ptr = dest = xCalloc (dest_len, char); if (!dest) return FALSE; src = vStringValue (string); retry: if (iconv (iconv_fd, &src, &src_len, &dest_ptr, &dest_len) == (size_t) -1) { if (errno == EILSEQ) { *dest_ptr++ = '?'; dest_len--; src++; src_len--; verbose (" Encoding: %s\n", strerror(errno)); goto retry; } eFree (dest); return FALSE; } dest_len = dest_ptr - dest; vStringClear (string); while (vStringSize (string) <= dest_len + 1) vStringAutoResize (string); memcpy (vStringValue (string), dest, dest_len + 1); vStringLength (string) = dest_len; eFree (dest); iconv (iconv_fd, NULL, NULL, NULL, NULL); return TRUE; }
/** * Reads from 'start' to 'end' and eliminates all spaces * inbetween. */ static char *readBetweenDelimitersWhileTrimmingSpaces(char start, char end) { static vString *wordBuffer = 0; int z; if (!wordBuffer) wordBuffer = vStringNew(); else vStringClear(wordBuffer); z = skipToNonWhite(); if (z != start) return NULL; while ((z = cppGetc()) != EOF && z != end) { if (isspace(z)) continue; vStringPut(wordBuffer, z); } if (z == EOF) return NULL; vStringPut(wordBuffer, z); vStringPut(wordBuffer, 0); return vStringValue(wordBuffer); }