static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind) { if (isspace (**cp)) { vString *name = vStringNew (); rubyKind actual_kind = parseIdentifier (cp, name, expected_kind); if (actual_kind == K_UNDEFINED || vStringLength (name) == 0) { /* * What kind of tags should we create for code like this? * * %w(self.clfloor clfloor).each do |name| * module_eval <<-"end;" * def #{name}(x, y=1) * q, r = x.divmod(y) * q = q.to_i * return q, r * end * end; * end * * Or this? * * class << HTTP * * For now, we don't create any. */ enterUnnamedScope (); } else { emitRubyTag (name, actual_kind); } vStringDelete (name); } }
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); }
static void findRubyTags (void) { const unsigned char *line; boolean inMultiLineComment = FALSE; nesting = stringListNew (); /* 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 = fileReadLine ()) != NULL) { const unsigned char *cp = line; if (canMatch (&cp, "=begin")) { inMultiLineComment = TRUE; continue; } if (canMatch (&cp, "=end")) { inMultiLineComment = FALSE; 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 (canMatch (&cp, "case") || canMatch (&cp, "for") || canMatch (&cp, "if") || canMatch (&cp, "unless") || canMatch (&cp, "while")) { enterUnnamedScope (); } /* * "module M", "class C" and "def m" should only be at the beginning * of a line. */ if (canMatch (&cp, "module")) { readAndEmitTag (&cp, K_MODULE); } else if (canMatch (&cp, "class")) { readAndEmitTag (&cp, K_CLASS); } else if (canMatch (&cp, "def")) { readAndEmitTag (&cp, K_METHOD); } 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 (canMatch (&cp, "begin") || canMatch (&cp, "do")) { enterUnnamedScope (); } else if (canMatch (&cp, "end") && stringListCount (nesting) > 0) { /* Leave the most recent scope. */ vStringDelete (stringListLast (nesting)); stringListRemoveLast (nesting); } else if (*cp == '"') { /* Skip string literals. * FIXME: should cope with escapes and interpolation. */ do { ++cp; } while (*cp != 0 && *cp != '"'); } else if (*cp != '\0') { do ++cp; while (isalnum (*cp) || *cp == '_'); } } } stringListDelete (nesting); }