Пример #1
0
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);
	}
}
Пример #2
0
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);
}
Пример #3
0
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);
}