xref: /Universal-ctags/parsers/ruby.c (revision 6b212cad3c28e21e9fae93d633216afdcb6c9af2)
13ae02089SMasatake YAMATO /*
23ae02089SMasatake YAMATO *   Copyright (c) 2000-2001, Thaddeus Covert <sahuagin@mediaone.net>
33ae02089SMasatake YAMATO *   Copyright (c) 2002 Matthias Veit <matthias_veit@yahoo.de>
43ae02089SMasatake YAMATO *   Copyright (c) 2004 Elliott Hughes <enh@acm.org>
53ae02089SMasatake YAMATO *
63ae02089SMasatake YAMATO *   This source code is released for free distribution under the terms of the
70ce38835Sviccuad *   GNU General Public License version 2 or (at your option) any later version.
83ae02089SMasatake YAMATO *
93ae02089SMasatake YAMATO *   This module contains functions for generating tags for Ruby language
103ae02089SMasatake YAMATO *   files.
113ae02089SMasatake YAMATO */
123ae02089SMasatake YAMATO 
133ae02089SMasatake YAMATO /*
143ae02089SMasatake YAMATO *   INCLUDE FILES
153ae02089SMasatake YAMATO */
163ae02089SMasatake YAMATO #include "general.h"  /* must always come first */
173ae02089SMasatake YAMATO 
18ebf51addSMasatake YAMATO #include <ctype.h>
193ae02089SMasatake YAMATO #include <string.h>
203ae02089SMasatake YAMATO 
211ac210a5SColomban Wendling #include "debug.h"
223ae02089SMasatake YAMATO #include "entry.h"
233ae02089SMasatake YAMATO #include "parse.h"
24*6b212cadSMasatake YAMATO #include "promise.h"
25bb7b947aSColomban Wendling #include "nestlevel.h"
263ae02089SMasatake YAMATO #include "read.h"
273db72c21SMasatake YAMATO #include "routines.h"
287af21814SMasatake YAMATO #include "strlist.h"
29ea6c7e11SMasatake YAMATO #include "subparser.h"
303ae02089SMasatake YAMATO #include "vstring.h"
313ae02089SMasatake YAMATO 
32ea6c7e11SMasatake YAMATO #include "ruby.h"
33ea6c7e11SMasatake YAMATO 
343ae02089SMasatake YAMATO /*
353ae02089SMasatake YAMATO *   DATA DECLARATIONS
363ae02089SMasatake YAMATO */
373ae02089SMasatake YAMATO typedef enum {
3884ab6d2cSMasatake YAMATO 	K_UNDEFINED = -1,
3984ab6d2cSMasatake YAMATO 	K_CLASS,
4084ab6d2cSMasatake YAMATO 	K_METHOD,
4184ab6d2cSMasatake YAMATO 	K_MODULE,
4284ab6d2cSMasatake YAMATO 	K_SINGLETON,
4384ab6d2cSMasatake YAMATO 	K_CONST,
4484ab6d2cSMasatake YAMATO 	K_ACCESSOR,
4584ab6d2cSMasatake YAMATO 	K_ALIAS,
46b28df83aSMasatake YAMATO 	K_LIBRARY,
473ae02089SMasatake YAMATO } rubyKind;
483ae02089SMasatake YAMATO 
49b28df83aSMasatake YAMATO typedef enum {
50b28df83aSMasatake YAMATO 	RUBY_LIBRARY_REQUIRED,
51b28df83aSMasatake YAMATO 	RUBY_LIBRARY_REQUIRED_REL,
52b28df83aSMasatake YAMATO 	RUBY_LIBRARY_LOADED,
53b28df83aSMasatake YAMATO } rubyLibraryRole;
54b28df83aSMasatake YAMATO 
553ae02089SMasatake YAMATO /*
563ae02089SMasatake YAMATO *   DATA DEFINITIONS
573ae02089SMasatake YAMATO */
58b28df83aSMasatake YAMATO 
59b28df83aSMasatake YAMATO static roleDefinition RubyLibraryRoles [] = {
60b28df83aSMasatake YAMATO 	{ true, "required",  "loaded by \"require\" method" },
61b28df83aSMasatake YAMATO 	{ true, "requiredRel", "loaded by \"require_relative\" method" },
62b28df83aSMasatake YAMATO 	{ true, "loaded", "loaded by \"load\" method" },
63b28df83aSMasatake YAMATO };
64b28df83aSMasatake YAMATO 
65e112e8abSMasatake YAMATO static kindDefinition RubyKinds [] = {
66ce990805SThomas Braun 	{ true, 'c', "class",  "classes" },
67ce990805SThomas Braun 	{ true, 'f', "method", "methods" },
68ce990805SThomas Braun 	{ true, 'm', "module", "modules" },
698050d8baSMasatake YAMATO 	{ true, 'S', "singletonMethod", "singleton methods" },
70ce990805SThomas Braun 	{ true, 'C', "constant", "constants" },
71673eb0e5SMasatake YAMATO 	{ true, 'A', "accessor", "accessors" },
7284ab6d2cSMasatake YAMATO 	{ true, 'a', "alias",    "aliases" },
73b28df83aSMasatake YAMATO 	{ true, 'L', "library",  "libraries",
74b28df83aSMasatake YAMATO 	  .referenceOnly = true, ATTACH_ROLES(RubyLibraryRoles) },
753ae02089SMasatake YAMATO };
763ae02089SMasatake YAMATO 
77c4711e83SMasatake YAMATO typedef enum {
78c4711e83SMasatake YAMATO 	F_MIXIN,
79c4711e83SMasatake YAMATO } rubyField;
80c4711e83SMasatake YAMATO 
81c4711e83SMasatake YAMATO static fieldDefinition RubyFields[] = {
82c4711e83SMasatake YAMATO 	{ .name = "mixin",
83c4711e83SMasatake YAMATO 	  .description = "how the class or module is mixed in (mixin:HOW:MODULE)",
84c4711e83SMasatake YAMATO 	  .enabled = true },
85c4711e83SMasatake YAMATO };
86c4711e83SMasatake YAMATO 
877af21814SMasatake YAMATO struct blockData {
887af21814SMasatake YAMATO 	stringList *mixin;
89ea6c7e11SMasatake YAMATO 	rubySubparser *subparser;
90ea6c7e11SMasatake YAMATO 	int subparserCorkIndex;
917af21814SMasatake YAMATO };
927af21814SMasatake YAMATO 
93bb7b947aSColomban Wendling static NestingLevels* nesting = NULL;
943ae02089SMasatake YAMATO 
9502e6a59bSColomban Wendling #define SCOPE_SEPARATOR '.'
9602e6a59bSColomban Wendling 
973ae02089SMasatake YAMATO /*
983ae02089SMasatake YAMATO *   FUNCTION DEFINITIONS
993ae02089SMasatake YAMATO */
1003ae02089SMasatake YAMATO 
101dc21df87SColomban Wendling static void enterUnnamedScope (void);
102dc21df87SColomban Wendling 
1033ae02089SMasatake YAMATO /*
104bb7b947aSColomban Wendling * Returns a string describing the scope in 'nls'.
1053ae02089SMasatake YAMATO * We record the current scope as a list of entered scopes.
1063ae02089SMasatake YAMATO * Scopes corresponding to 'if' statements and the like are
1073ae02089SMasatake YAMATO * represented by empty strings. Scopes corresponding to
1083ae02089SMasatake YAMATO * modules and classes are represented by the name of the
1093ae02089SMasatake YAMATO * module or class.
1103ae02089SMasatake YAMATO */
nestingLevelsToScope(const NestingLevels * nls)111bb7b947aSColomban Wendling static vString* nestingLevelsToScope (const NestingLevels* nls)
1123ae02089SMasatake YAMATO {
113bb7b947aSColomban Wendling 	int i;
1143ae02089SMasatake YAMATO 	unsigned int chunks_output = 0;
1153ae02089SMasatake YAMATO 	vString* result = vStringNew ();
116bb7b947aSColomban Wendling 	for (i = 0; i < nls->n; ++i)
1173ae02089SMasatake YAMATO 	{
1180f753a22SMasatake YAMATO 	    NestingLevel *nl = nestingLevelsGetNthFromRoot (nls, i);
119090afdd9SMasatake YAMATO 	    tagEntryInfo *e = getEntryOfNestingLevel (nl);
120885fbc2cSMasatake YAMATO 	    if (e && strlen (e->name) > 0 && (!e->placeholder))
1213ae02089SMasatake YAMATO 	    {
12202e6a59bSColomban Wendling 	        if (chunks_output++ > 0)
12302e6a59bSColomban Wendling 	            vStringPut (result, SCOPE_SEPARATOR);
124885fbc2cSMasatake YAMATO 	        vStringCatS (result, e->name);
1253ae02089SMasatake YAMATO 	    }
1263ae02089SMasatake YAMATO 	}
1273ae02089SMasatake YAMATO 	return result;
1283ae02089SMasatake YAMATO }
1293ae02089SMasatake YAMATO 
1303ae02089SMasatake YAMATO /*
1313ae02089SMasatake YAMATO * Attempts to advance 's' past 'literal'.
132ce990805SThomas Braun * Returns true if it did, false (and leaves 's' where
1333ae02089SMasatake YAMATO * it was) otherwise.
1343ae02089SMasatake YAMATO */
canMatch(const unsigned char ** s,const char * literal,bool (* end_check)(int))135ce990805SThomas Braun static bool canMatch (const unsigned char** s, const char* literal,
136ce990805SThomas Braun                          bool (*end_check) (int))
1373ae02089SMasatake YAMATO {
1383ae02089SMasatake YAMATO 	const int literal_length = strlen (literal);
139597fd3ddSMasatake YAMATO 	const int s_length = strlen ((const char *)*s);
140597fd3ddSMasatake YAMATO 
141597fd3ddSMasatake YAMATO 	if (s_length < literal_length)
142ce990805SThomas Braun 		return false;
143597fd3ddSMasatake YAMATO 
1443ae02089SMasatake YAMATO 	const unsigned char next_char = *(*s + literal_length);
1453ae02089SMasatake YAMATO 	if (strncmp ((const char*) *s, literal, literal_length) != 0)
1463ae02089SMasatake YAMATO 	{
147ce990805SThomas Braun 	    return false;
1483ae02089SMasatake YAMATO 	}
1493ae02089SMasatake YAMATO 	/* Additionally check that we're at the end of a token. */
1505a560633SColomban Wendling 	if (! end_check (next_char))
1513ae02089SMasatake YAMATO 	{
152ce990805SThomas Braun 	    return false;
1533ae02089SMasatake YAMATO 	}
1543ae02089SMasatake YAMATO 	*s += literal_length;
155ce990805SThomas Braun 	return true;
1563ae02089SMasatake YAMATO }
1573ae02089SMasatake YAMATO 
isIdentChar(int c)1589782f2a5STambet Arak static bool isIdentChar (int c)
1599782f2a5STambet Arak {
1609782f2a5STambet Arak 	return (isalnum (c) || c == '_');
1619782f2a5STambet Arak }
1629782f2a5STambet Arak 
notIdentCharButColon(int c)16307c69744SMasatake YAMATO static bool notIdentCharButColon (int c)
1645a560633SColomban Wendling {
16507c69744SMasatake YAMATO 	return ! (isIdentChar (c) || c == ':');
1665a560633SColomban Wendling }
1675a560633SColomban Wendling 
isOperatorChar(int c)16848ae2e7bSTambet Arak static bool isOperatorChar (int c)
1695a560633SColomban Wendling {
17068ea0f84STambet Arak 	return (c == '[' || c == ']' ||
1715a560633SColomban Wendling 	        c == '=' || c == '!' || c == '~' ||
1725a560633SColomban Wendling 	        c == '+' || c == '-' ||
1735a560633SColomban Wendling 	        c == '@' || c == '*' || c == '/' || c == '%' ||
1745a560633SColomban Wendling 	        c == '<' || c == '>' ||
1755a560633SColomban Wendling 	        c == '&' || c == '^' || c == '|');
1765a560633SColomban Wendling }
1775a560633SColomban Wendling 
notOperatorChar(int c)17868ea0f84STambet Arak static bool notOperatorChar (int c)
17968ea0f84STambet Arak {
18048ae2e7bSTambet Arak 	return ! isOperatorChar (c);
18148ae2e7bSTambet Arak }
18248ae2e7bSTambet Arak 
isSigilChar(int c)18348ae2e7bSTambet Arak static bool isSigilChar (int c)
18448ae2e7bSTambet Arak {
18548ae2e7bSTambet Arak 	return (c == '@' || c == '$');
18668ea0f84STambet Arak }
18768ea0f84STambet Arak 
isWhitespace(int c)188ce990805SThomas Braun static bool isWhitespace (int c)
1895a560633SColomban Wendling {
1905a560633SColomban Wendling 	return c == 0 || isspace (c);
1915a560633SColomban Wendling }
1925a560633SColomban Wendling 
1939782f2a5STambet Arak /*
1949782f2a5STambet Arak  * Advance 's' while the passed predicate is true. Returns true if
1959782f2a5STambet Arak  * advanced by at least one position.
1969782f2a5STambet Arak  */
advanceWhile(const unsigned char ** s,bool (* predicate)(int))19768ea0f84STambet Arak static bool advanceWhile (const unsigned char** s, bool (*predicate) (int))
1989782f2a5STambet Arak {
1999782f2a5STambet Arak 	const unsigned char* original_pos = *s;
2009782f2a5STambet Arak 
2019782f2a5STambet Arak 	while (**s != '\0')
2029782f2a5STambet Arak 	{
2039782f2a5STambet Arak 		if (! predicate (**s))
2049782f2a5STambet Arak 		{
2059782f2a5STambet Arak 			return *s != original_pos;
2069782f2a5STambet Arak 		}
2079782f2a5STambet Arak 
2089782f2a5STambet Arak 		(*s)++;
2099782f2a5STambet Arak 	}
2109782f2a5STambet Arak 
2119782f2a5STambet Arak 	return *s != original_pos;
2129782f2a5STambet Arak }
2139782f2a5STambet Arak 
214ea6c7e11SMasatake YAMATO #define canMatchKeyword rubyCanMatchKeyword
rubyCanMatchKeyword(const unsigned char ** s,const char * literal)215ea6c7e11SMasatake YAMATO extern bool rubyCanMatchKeyword (const unsigned char** s, const char* literal)
2165a560633SColomban Wendling {
21707c69744SMasatake YAMATO 	/* Using notIdentCharButColon() here.
21807c69744SMasatake YAMATO 	 *
21907c69744SMasatake YAMATO 	 * A hash can be defined like {for: nil, foo: 0}.
22007c69744SMasatake YAMATO 	 *"for" in the above example is not a keyword.
22107c69744SMasatake YAMATO 	 */
22207c69744SMasatake YAMATO 	return canMatch (s, literal, notIdentCharButColon);
2235a560633SColomban Wendling }
2245a560633SColomban Wendling 
2253ae02089SMasatake YAMATO /*
2269782f2a5STambet Arak  * Extends canMatch. Works similarly, but allows assignment to precede
2279782f2a5STambet Arak  * the keyword, as block assignment is a common Ruby idiom.
2289782f2a5STambet Arak  */
229ea6c7e11SMasatake YAMATO #define canMatchKeywordWithAssign rubyCanMatchKeywordWithAssign
rubyCanMatchKeywordWithAssign(const unsigned char ** s,const char * literal)230ea6c7e11SMasatake YAMATO extern bool rubyCanMatchKeywordWithAssign (const unsigned char** s, const char* literal)
2319782f2a5STambet Arak {
2329782f2a5STambet Arak 	const unsigned char* original_pos = *s;
2339782f2a5STambet Arak 
2349782f2a5STambet Arak 	if (canMatchKeyword (s, literal))
2359782f2a5STambet Arak 	{
2369782f2a5STambet Arak 		return true;
2379782f2a5STambet Arak 	}
2389782f2a5STambet Arak 
23948ae2e7bSTambet Arak 	advanceWhile (s, isSigilChar);
24048ae2e7bSTambet Arak 
24168ea0f84STambet Arak 	if (! advanceWhile (s, isIdentChar))
2429782f2a5STambet Arak 	{
2439782f2a5STambet Arak 		*s = original_pos;
2449782f2a5STambet Arak 		return false;
2459782f2a5STambet Arak 	}
2469782f2a5STambet Arak 
24768ea0f84STambet Arak 	advanceWhile (s, isWhitespace);
2489782f2a5STambet Arak 
24948ae2e7bSTambet Arak 	if (! (advanceWhile (s, isOperatorChar) && *(*s - 1) == '='))
25068ea0f84STambet Arak 	{
2519782f2a5STambet Arak 		*s = original_pos;
2529782f2a5STambet Arak 		return false;
2539782f2a5STambet Arak 	}
2549782f2a5STambet Arak 
25568ea0f84STambet Arak 	advanceWhile (s, isWhitespace);
2569782f2a5STambet Arak 
2579782f2a5STambet Arak 	if (canMatchKeyword (s, literal))
2589782f2a5STambet Arak 	{
2599782f2a5STambet Arak 		return true;
2609782f2a5STambet Arak 	}
2619782f2a5STambet Arak 
2629782f2a5STambet Arak 	*s = original_pos;
2639782f2a5STambet Arak 	return false;
2649782f2a5STambet Arak }
2659782f2a5STambet Arak 
2669782f2a5STambet Arak /*
2673ae02089SMasatake YAMATO * Attempts to advance 'cp' past a Ruby operator method name. Returns
268ce990805SThomas Braun * true if successful (and copies the name into 'name'), false otherwise.
2693ae02089SMasatake YAMATO */
parseRubyOperator(vString * name,const unsigned char ** cp)270ce990805SThomas Braun static bool parseRubyOperator (vString* name, const unsigned char** cp)
2713ae02089SMasatake YAMATO {
2723ae02089SMasatake YAMATO 	static const char* RUBY_OPERATORS[] = {
2733ae02089SMasatake YAMATO 	    "[]", "[]=",
2743ae02089SMasatake YAMATO 	    "**",
2753ae02089SMasatake YAMATO 	    "!", "~", "+@", "-@",
2763ae02089SMasatake YAMATO 	    "*", "/", "%",
2773ae02089SMasatake YAMATO 	    "+", "-",
2783ae02089SMasatake YAMATO 	    ">>", "<<",
2793ae02089SMasatake YAMATO 	    "&",
2803ae02089SMasatake YAMATO 	    "^", "|",
2813ae02089SMasatake YAMATO 	    "<=", "<", ">", ">=",
2823ae02089SMasatake YAMATO 	    "<=>", "==", "===", "!=", "=~", "!~",
2833ae02089SMasatake YAMATO 	    "`",
2843ae02089SMasatake YAMATO 	    NULL
2853ae02089SMasatake YAMATO 	};
2863ae02089SMasatake YAMATO 	int i;
2873ae02089SMasatake YAMATO 	for (i = 0; RUBY_OPERATORS[i] != NULL; ++i)
2883ae02089SMasatake YAMATO 	{
2895a560633SColomban Wendling 	    if (canMatch (cp, RUBY_OPERATORS[i], notOperatorChar))
2903ae02089SMasatake YAMATO 	    {
2913ae02089SMasatake YAMATO 	        vStringCatS (name, RUBY_OPERATORS[i]);
292ce990805SThomas Braun 	        return true;
2933ae02089SMasatake YAMATO 	    }
2943ae02089SMasatake YAMATO 	}
295ce990805SThomas Braun 	return false;
2963ae02089SMasatake YAMATO }
2973ae02089SMasatake YAMATO 
2983ae02089SMasatake YAMATO /*
2993ae02089SMasatake YAMATO * Emits a tag for the given 'name' of kind 'kind' at the current nesting.
3003ae02089SMasatake YAMATO */
emitRubyTagFull(vString * name,rubyKind kind,bool pushLevel,bool clearName)301673eb0e5SMasatake YAMATO static int emitRubyTagFull (vString* name, rubyKind kind, bool pushLevel, bool clearName)
3023ae02089SMasatake YAMATO {
3033ae02089SMasatake YAMATO 	tagEntryInfo tag;
3043ae02089SMasatake YAMATO 	vString* scope;
305885fbc2cSMasatake YAMATO 	tagEntryInfo *parent;
3061ac210a5SColomban Wendling 	rubyKind parent_kind = K_UNDEFINED;
3071ac210a5SColomban Wendling 	NestingLevel *lvl;
30802e6a59bSColomban Wendling 	const char *unqualified_name;
30902e6a59bSColomban Wendling 	const char *qualified_name;
310885fbc2cSMasatake YAMATO 	int r;
3114f481d0eSMasatake YAMATO 	bool anonymous = false;
3124f481d0eSMasatake YAMATO 
3134f481d0eSMasatake YAMATO 	if (!name)
3144f481d0eSMasatake YAMATO 	{
3154f481d0eSMasatake YAMATO 		name = anonGenerateNew ("__anon", K_CLASS);
3164f481d0eSMasatake YAMATO 		anonymous = true;
3174f481d0eSMasatake YAMATO 	}
3183ae02089SMasatake YAMATO 
3193ae02089SMasatake YAMATO         if (!RubyKinds[kind].enabled) {
32067997237SMasatake YAMATO             return CORK_NIL;
3213ae02089SMasatake YAMATO         }
3223ae02089SMasatake YAMATO 
323bb7b947aSColomban Wendling 	scope = nestingLevelsToScope (nesting);
3241ac210a5SColomban Wendling 	lvl = nestingLevelsGetCurrent (nesting);
325885fbc2cSMasatake YAMATO 	parent = getEntryOfNestingLevel (lvl);
326885fbc2cSMasatake YAMATO 	if (parent)
327f92e6bf2SMasatake YAMATO 		parent_kind =  parent->kindIndex;
3283ae02089SMasatake YAMATO 
32902e6a59bSColomban Wendling 	qualified_name = vStringValue (name);
33002e6a59bSColomban Wendling 	unqualified_name = strrchr (qualified_name, SCOPE_SEPARATOR);
33102e6a59bSColomban Wendling 	if (unqualified_name && unqualified_name[1])
33202e6a59bSColomban Wendling 	{
33302e6a59bSColomban Wendling 		if (unqualified_name > qualified_name)
33402e6a59bSColomban Wendling 		{
33502e6a59bSColomban Wendling 			if (vStringLength (scope) > 0)
33602e6a59bSColomban Wendling 				vStringPut (scope, SCOPE_SEPARATOR);
33702e6a59bSColomban Wendling 			vStringNCatS (scope, qualified_name,
33802e6a59bSColomban Wendling 			              unqualified_name - qualified_name);
3391ac210a5SColomban Wendling 			/* assume module parent type for a lack of a better option */
3401ac210a5SColomban Wendling 			parent_kind = K_MODULE;
34102e6a59bSColomban Wendling 		}
34202e6a59bSColomban Wendling 		unqualified_name++;
34302e6a59bSColomban Wendling 	}
34402e6a59bSColomban Wendling 	else
34502e6a59bSColomban Wendling 		unqualified_name = qualified_name;
34602e6a59bSColomban Wendling 
34716a2541cSMasatake YAMATO 	initTagEntry (&tag, unqualified_name, kind);
34884ab6d2cSMasatake YAMATO 
34984ab6d2cSMasatake YAMATO 	/* Don't fill the scope field for a tag entry representing
35084ab6d2cSMasatake YAMATO 	 * a global variable. */
35184ab6d2cSMasatake YAMATO 	if (unqualified_name[0] != '$'
35284ab6d2cSMasatake YAMATO 		&& vStringLength (scope) > 0) {
3531ac210a5SColomban Wendling 		Assert (0 <= parent_kind &&
354158a3387SMasatake YAMATO 		        (size_t) parent_kind < (ARRAY_SIZE (RubyKinds)));
3551ac210a5SColomban Wendling 
356f92e6bf2SMasatake YAMATO 		tag.extensionFields.scopeKindIndex = parent_kind;
357015ab54cSMasatake YAMATO 		tag.extensionFields.scopeName = vStringValue (scope);
3583ae02089SMasatake YAMATO 	}
3594f481d0eSMasatake YAMATO 
3604f481d0eSMasatake YAMATO 	if (anonymous)
3614f481d0eSMasatake YAMATO 		markTagExtraBit (&tag, XTAG_ANONYMOUS);
3624f481d0eSMasatake YAMATO 
363885fbc2cSMasatake YAMATO 	r = makeTagEntry (&tag);
3643ae02089SMasatake YAMATO 
365673eb0e5SMasatake YAMATO 	if (pushLevel)
366885fbc2cSMasatake YAMATO 		nestingLevelsPush (nesting, r);
3673ae02089SMasatake YAMATO 
368673eb0e5SMasatake YAMATO 	if (clearName)
3693ae02089SMasatake YAMATO 		vStringClear (name);
3704f481d0eSMasatake YAMATO 
3714f481d0eSMasatake YAMATO 	if (anonymous)
3724f481d0eSMasatake YAMATO 		vStringDelete (name);
3734f481d0eSMasatake YAMATO 
3743ae02089SMasatake YAMATO 	vStringDelete (scope);
37567997237SMasatake YAMATO 
37667997237SMasatake YAMATO 	return r;
3773ae02089SMasatake YAMATO }
3783ae02089SMasatake YAMATO 
emitRubyTag(vString * name,rubyKind kind)379673eb0e5SMasatake YAMATO static int emitRubyTag (vString* name, rubyKind kind)
380673eb0e5SMasatake YAMATO {
381673eb0e5SMasatake YAMATO 	return emitRubyTagFull (name, kind, kind != K_CONST, true);
382673eb0e5SMasatake YAMATO }
383673eb0e5SMasatake YAMATO 
3843ae02089SMasatake YAMATO /* Tests whether 'ch' is a character in 'list'. */
charIsIn(char ch,const char * list)385ce990805SThomas Braun static bool charIsIn (char ch, const char* list)
3863ae02089SMasatake YAMATO {
3873ae02089SMasatake YAMATO 	return (strchr (list, ch) != NULL);
3883ae02089SMasatake YAMATO }
3893ae02089SMasatake YAMATO 
3903ae02089SMasatake YAMATO /* Advances 'cp' over leading whitespace. */
391ea6c7e11SMasatake YAMATO #define skipWhitespace rubySkipWhitespace
rubySkipWhitespace(const unsigned char ** cp)392ea6c7e11SMasatake YAMATO extern void rubySkipWhitespace (const unsigned char** cp)
3933ae02089SMasatake YAMATO {
3943ae02089SMasatake YAMATO 	while (isspace (**cp))
3953ae02089SMasatake YAMATO 	{
3963ae02089SMasatake YAMATO 	    ++*cp;
3973ae02089SMasatake YAMATO 	}
3983ae02089SMasatake YAMATO }
3993ae02089SMasatake YAMATO 
4003ae02089SMasatake YAMATO /*
4013ae02089SMasatake YAMATO * Copies the characters forming an identifier from *cp into
4023ae02089SMasatake YAMATO * name, leaving *cp pointing to the character after the identifier.
4033ae02089SMasatake YAMATO */
parseIdentifier(const unsigned char ** cp,vString * name,rubyKind kind)4043ae02089SMasatake YAMATO static rubyKind parseIdentifier (
4053ae02089SMasatake YAMATO 		const unsigned char** cp, vString* name, rubyKind kind)
4063ae02089SMasatake YAMATO {
4073ae02089SMasatake YAMATO 	/* Method names are slightly different to class and variable names.
4083ae02089SMasatake YAMATO 	 * A method name may optionally end with a question mark, exclamation
4093ae02089SMasatake YAMATO 	 * point or equals sign. These are all part of the name.
4103ae02089SMasatake YAMATO 	 * A method name may also contain a period if it's a singleton method.
4113ae02089SMasatake YAMATO 	 */
412ce990805SThomas Braun 	bool had_sep = false;
4133ae02089SMasatake YAMATO 	const char* also_ok;
4143ae02089SMasatake YAMATO 	if (kind == K_METHOD)
4153ae02089SMasatake YAMATO 	{
416995979c6SColomban Wendling 		also_ok = ".?!=";
4173ae02089SMasatake YAMATO 	}
418d9ba5df9SMasatake YAMATO 	else if (kind == K_SINGLETON)
419d9ba5df9SMasatake YAMATO 	{
420995979c6SColomban Wendling 		also_ok = "?!=";
421d9ba5df9SMasatake YAMATO 	}
4223ae02089SMasatake YAMATO 	else
4233ae02089SMasatake YAMATO 	{
424995979c6SColomban Wendling 		also_ok = "";
4253ae02089SMasatake YAMATO 	}
4263ae02089SMasatake YAMATO 
4273ae02089SMasatake YAMATO 	skipWhitespace (cp);
4283ae02089SMasatake YAMATO 
4293ae02089SMasatake YAMATO 	/* Check for an anonymous (singleton) class such as "class << HTTP". */
4303ae02089SMasatake YAMATO 	if (kind == K_CLASS && **cp == '<' && *(*cp + 1) == '<')
4313ae02089SMasatake YAMATO 	{
4323ae02089SMasatake YAMATO 		return K_UNDEFINED;
4333ae02089SMasatake YAMATO 	}
4343ae02089SMasatake YAMATO 
4353ae02089SMasatake YAMATO 	/* Check for operators such as "def []=(key, val)". */
4363ae02089SMasatake YAMATO 	if (kind == K_METHOD || kind == K_SINGLETON)
4373ae02089SMasatake YAMATO 	{
4383ae02089SMasatake YAMATO 		if (parseRubyOperator (name, cp))
4393ae02089SMasatake YAMATO 		{
4403ae02089SMasatake YAMATO 			return kind;
4413ae02089SMasatake YAMATO 		}
4423ae02089SMasatake YAMATO 	}
4433ae02089SMasatake YAMATO 
4443ae02089SMasatake YAMATO 	/* Copy the identifier into 'name'. */
445995979c6SColomban Wendling 	while (**cp != 0 && (**cp == ':' || isIdentChar (**cp) || charIsIn (**cp, also_ok)))
4463ae02089SMasatake YAMATO 	{
4473ae02089SMasatake YAMATO 		char last_char = **cp;
4483ae02089SMasatake YAMATO 
44902e6a59bSColomban Wendling 		if (last_char == ':')
450ce990805SThomas Braun 			had_sep = true;
45102e6a59bSColomban Wendling 		else
45202e6a59bSColomban Wendling 		{
45302e6a59bSColomban Wendling 			if (had_sep)
45402e6a59bSColomban Wendling 			{
45502e6a59bSColomban Wendling 				vStringPut (name, SCOPE_SEPARATOR);
456ce990805SThomas Braun 				had_sep = false;
45702e6a59bSColomban Wendling 			}
4583ae02089SMasatake YAMATO 			vStringPut (name, last_char);
45902e6a59bSColomban Wendling 		}
4603ae02089SMasatake YAMATO 		++*cp;
4613ae02089SMasatake YAMATO 
4623ae02089SMasatake YAMATO 		if (kind == K_METHOD)
4633ae02089SMasatake YAMATO 		{
4643ae02089SMasatake YAMATO 			/* Recognize singleton methods. */
4653ae02089SMasatake YAMATO 			if (last_char == '.')
4663ae02089SMasatake YAMATO 			{
4673ae02089SMasatake YAMATO 				vStringClear (name);
4683ae02089SMasatake YAMATO 				return parseIdentifier (cp, name, K_SINGLETON);
4693ae02089SMasatake YAMATO 			}
470d9ba5df9SMasatake YAMATO 		}
4713ae02089SMasatake YAMATO 
472d9ba5df9SMasatake YAMATO 		if (kind == K_METHOD || kind == K_SINGLETON)
473d9ba5df9SMasatake YAMATO 		{
4743ae02089SMasatake YAMATO 			/* Recognize characters which mark the end of a method name. */
4753ae02089SMasatake YAMATO 			if (charIsIn (last_char, "?!="))
4763ae02089SMasatake YAMATO 			{
4773ae02089SMasatake YAMATO 				break;
4783ae02089SMasatake YAMATO 			}
4793ae02089SMasatake YAMATO 		}
4803ae02089SMasatake YAMATO 	}
4813ae02089SMasatake YAMATO 	return kind;
4823ae02089SMasatake YAMATO }
4833ae02089SMasatake YAMATO 
rubyParseMethodName(const unsigned char ** cp,vString * vstr)484ea6c7e11SMasatake YAMATO extern bool rubyParseMethodName (const unsigned char **cp, vString* vstr)
485ea6c7e11SMasatake YAMATO {
486ea6c7e11SMasatake YAMATO 	return (parseIdentifier (cp, vstr, K_METHOD) == K_METHOD);
487ea6c7e11SMasatake YAMATO }
488ea6c7e11SMasatake YAMATO 
rubyParseModuleName(const unsigned char ** cp,vString * vstr)489ea6c7e11SMasatake YAMATO extern bool rubyParseModuleName (const unsigned char **cp, vString* vstr)
490ea6c7e11SMasatake YAMATO {
491ea6c7e11SMasatake YAMATO 	return (parseIdentifier (cp, vstr, K_MODULE) == K_MODULE);
492ea6c7e11SMasatake YAMATO }
493ea6c7e11SMasatake YAMATO 
parseString(const unsigned char ** cp,unsigned char boundary,vString * vstr)49442bc7f49SMasatake YAMATO static void parseString (const unsigned char** cp, unsigned char boundary, vString* vstr)
495d8a7fab3SMasatake YAMATO {
49642bc7f49SMasatake YAMATO 	while (**cp != 0 && **cp != boundary)
497d8a7fab3SMasatake YAMATO 	{
498d8a7fab3SMasatake YAMATO 		if (vstr)
499d8a7fab3SMasatake YAMATO 			vStringPut (vstr, **cp);
500d8a7fab3SMasatake YAMATO 		++*cp;
501d8a7fab3SMasatake YAMATO 	}
502d8a7fab3SMasatake YAMATO 
503d8a7fab3SMasatake YAMATO 	/* skip the last found '"' */
50442bc7f49SMasatake YAMATO 	if (**cp == boundary)
505d8a7fab3SMasatake YAMATO 		++*cp;
506d8a7fab3SMasatake YAMATO }
507d8a7fab3SMasatake YAMATO 
rubyParseString(const unsigned char ** cp,unsigned char boundary,vString * vstr)508ea6c7e11SMasatake YAMATO extern bool rubyParseString (const unsigned char** cp, unsigned char boundary, vString* vstr)
509ea6c7e11SMasatake YAMATO {
510ea6c7e11SMasatake YAMATO 	const unsigned char *p = *cp;
511ea6c7e11SMasatake YAMATO 	parseString (cp, boundary, vstr);
512ea6c7e11SMasatake YAMATO 	return (p != *cp);
513ea6c7e11SMasatake YAMATO }
514ea6c7e11SMasatake YAMATO 
parseSignature(const unsigned char ** cp,vString * vstr)515cc26cc1dSMasatake YAMATO static void parseSignature (const unsigned char** cp, vString* vstr)
516cc26cc1dSMasatake YAMATO {
517cc26cc1dSMasatake YAMATO 	int depth = 1;
518cc26cc1dSMasatake YAMATO 
519cc26cc1dSMasatake YAMATO 	while (1)
520cc26cc1dSMasatake YAMATO 	{
521cc26cc1dSMasatake YAMATO 		/* FIXME:
522cc26cc1dSMasatake YAMATO 		 * - handle string literals including ( or ), and
523cc26cc1dSMasatake YAMATO 		 * - skip comments.
524cc26cc1dSMasatake YAMATO 		 */
525cc26cc1dSMasatake YAMATO 		while (! (depth == 0 || **cp == '\0'))
526cc26cc1dSMasatake YAMATO 		{
527cc26cc1dSMasatake YAMATO 			if (**cp == '(' || **cp == ')')
528cc26cc1dSMasatake YAMATO 			{
529cc26cc1dSMasatake YAMATO 				depth += (**cp == '(')? 1: -1;
530cc26cc1dSMasatake YAMATO 				vStringPut (vstr, **cp);
531cc26cc1dSMasatake YAMATO 			}
5328ab66d92SMasatake YAMATO 			else if (**cp == '#')
5338ab66d92SMasatake YAMATO 			{
5348ab66d92SMasatake YAMATO 				++*cp;
5358ab66d92SMasatake YAMATO 				while (**cp != '\0')
5368ab66d92SMasatake YAMATO 					++*cp;
5378ab66d92SMasatake YAMATO 				break;
5388ab66d92SMasatake YAMATO 			}
5398ab66d92SMasatake YAMATO 			else if (**cp == '\'' || **cp == '"')
5408ab66d92SMasatake YAMATO 			{
5418ab66d92SMasatake YAMATO 				unsigned char b = **cp;
5428ab66d92SMasatake YAMATO 				vStringPut (vstr, b);
5438ab66d92SMasatake YAMATO 				++*cp;
5448ab66d92SMasatake YAMATO 				parseString (cp, b, vstr);
5458ab66d92SMasatake YAMATO 				vStringPut (vstr, b);
5468ab66d92SMasatake YAMATO 				continue;
5478ab66d92SMasatake YAMATO 			}
548cc26cc1dSMasatake YAMATO 			else if (isspace (vStringLast (vstr)))
549cc26cc1dSMasatake YAMATO 			{
550cc26cc1dSMasatake YAMATO 				if (! (isspace (**cp)))
551cc26cc1dSMasatake YAMATO 				{
552cc26cc1dSMasatake YAMATO 					if (**cp == ',')
553cc26cc1dSMasatake YAMATO 						vStringChop (vstr);
554cc26cc1dSMasatake YAMATO 					vStringPut (vstr, **cp);
555cc26cc1dSMasatake YAMATO 				}
556cc26cc1dSMasatake YAMATO 			}
557cc26cc1dSMasatake YAMATO 			else
558cc26cc1dSMasatake YAMATO 				vStringPut (vstr, **cp);
559cc26cc1dSMasatake YAMATO 			++*cp;
560cc26cc1dSMasatake YAMATO 		}
561cc26cc1dSMasatake YAMATO 		if (depth == 0)
562cc26cc1dSMasatake YAMATO 			return;
563cc26cc1dSMasatake YAMATO 
564cc26cc1dSMasatake YAMATO 		const unsigned char *line = readLineFromInputFile ();
565cc26cc1dSMasatake YAMATO 		if (line == NULL)
566cc26cc1dSMasatake YAMATO 			return;
567cc26cc1dSMasatake YAMATO 		else
568cc26cc1dSMasatake YAMATO 			*cp = line;
569cc26cc1dSMasatake YAMATO 	}
570cc26cc1dSMasatake YAMATO }
571cc26cc1dSMasatake YAMATO 
readAndEmitTagFull(const unsigned char ** cp,rubyKind expected_kind,bool pushLevel,bool clearName)57284ab6d2cSMasatake YAMATO static int readAndEmitTagFull (const unsigned char** cp, rubyKind expected_kind,
57384ab6d2cSMasatake YAMATO 							   bool pushLevel, bool clearName)
5743ae02089SMasatake YAMATO {
57567997237SMasatake YAMATO 	int r = CORK_NIL;
5763ae02089SMasatake YAMATO 	if (isspace (**cp))
5773ae02089SMasatake YAMATO 	{
5783ae02089SMasatake YAMATO 		vString *name = vStringNew ();
5793ae02089SMasatake YAMATO 		rubyKind actual_kind = parseIdentifier (cp, name, expected_kind);
5803ae02089SMasatake YAMATO 
5813ae02089SMasatake YAMATO 		if (actual_kind == K_UNDEFINED || vStringLength (name) == 0)
5823ae02089SMasatake YAMATO 		{
5833ae02089SMasatake YAMATO 			/*
5843ae02089SMasatake YAMATO 			* What kind of tags should we create for code like this?
5853ae02089SMasatake YAMATO 			*
5863ae02089SMasatake YAMATO 			*    %w(self.clfloor clfloor).each do |name|
5873ae02089SMasatake YAMATO 			*        module_eval <<-"end;"
5883ae02089SMasatake YAMATO 			*            def #{name}(x, y=1)
5893ae02089SMasatake YAMATO 			*                q, r = x.divmod(y)
5903ae02089SMasatake YAMATO 			*                q = q.to_i
5913ae02089SMasatake YAMATO 			*                return q, r
5923ae02089SMasatake YAMATO 			*            end
5933ae02089SMasatake YAMATO 			*        end;
5943ae02089SMasatake YAMATO 			*    end
5953ae02089SMasatake YAMATO 			*
5963ae02089SMasatake YAMATO 			* Or this?
5973ae02089SMasatake YAMATO 			*
5983ae02089SMasatake YAMATO 			*    class << HTTP
5993ae02089SMasatake YAMATO 			*
6003ae02089SMasatake YAMATO 			* For now, we don't create any.
6013ae02089SMasatake YAMATO 			*/
602dc21df87SColomban Wendling 			enterUnnamedScope ();
6033ae02089SMasatake YAMATO 		}
6043ae02089SMasatake YAMATO 		else
6053ae02089SMasatake YAMATO 		{
60684ab6d2cSMasatake YAMATO 			r = emitRubyTagFull (name, actual_kind, pushLevel, clearName);
6073ae02089SMasatake YAMATO 		}
6083ae02089SMasatake YAMATO 		vStringDelete (name);
6093ae02089SMasatake YAMATO 	}
61067997237SMasatake YAMATO 	return r;
6113ae02089SMasatake YAMATO }
6123ae02089SMasatake YAMATO 
readAndEmitTag(const unsigned char ** cp,rubyKind expected_kind)61384ab6d2cSMasatake YAMATO static int readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
61484ab6d2cSMasatake YAMATO {
61584ab6d2cSMasatake YAMATO 	return readAndEmitTagFull (cp, expected_kind, expected_kind != K_CONST, true);
61684ab6d2cSMasatake YAMATO }
61784ab6d2cSMasatake YAMATO 
readAndStoreMixinSpec(const unsigned char ** cp,const char * how_mixin)6187af21814SMasatake YAMATO static void readAndStoreMixinSpec (const unsigned char** cp, const char *how_mixin)
6197af21814SMasatake YAMATO {
6207af21814SMasatake YAMATO 
621ea87271eSMasatake YAMATO 	NestingLevel *nl = NULL;
622ea87271eSMasatake YAMATO 	tagEntryInfo *e = NULL;
623ea87271eSMasatake YAMATO 	int ownerLevel = 0;
624ea87271eSMasatake YAMATO 
625ea87271eSMasatake YAMATO 	for (ownerLevel = 0; ownerLevel < nesting->n; ownerLevel++)
626ea87271eSMasatake YAMATO 	{
627ea87271eSMasatake YAMATO 		nl = nestingLevelsGetNthParent (nesting, ownerLevel);
628ea87271eSMasatake YAMATO 		e = nl? getEntryOfNestingLevel (nl): NULL;
629ea87271eSMasatake YAMATO 
630ea87271eSMasatake YAMATO 		/* Ignore "if", "unless", "while" ... */
631ea87271eSMasatake YAMATO 		if ((nl && (nl->corkIndex == CORK_NIL)) || (e && e->placeholder))
632ea87271eSMasatake YAMATO 			continue;
633ea87271eSMasatake YAMATO 		break;
634ea87271eSMasatake YAMATO 	}
635ea87271eSMasatake YAMATO 
636ea87271eSMasatake YAMATO 	if (!e)
637ea87271eSMasatake YAMATO 		return;
6387af21814SMasatake YAMATO 
639f9a77b5dSMasatake YAMATO 	if (e->kindIndex == K_SINGLETON)
640f9a77b5dSMasatake YAMATO 	{
641ea87271eSMasatake YAMATO 		nl = nestingLevelsGetNthParent (nesting,
642ea87271eSMasatake YAMATO 										ownerLevel + 1);
643f9a77b5dSMasatake YAMATO 		if (nl == NULL)
644f9a77b5dSMasatake YAMATO 			return;
645f9a77b5dSMasatake YAMATO 		e = getEntryOfNestingLevel (nl);
646f9a77b5dSMasatake YAMATO 	}
647f9a77b5dSMasatake YAMATO 
648ea87271eSMasatake YAMATO 	if (!e)
649ea87271eSMasatake YAMATO 		return;
650ea87271eSMasatake YAMATO 
6517af21814SMasatake YAMATO 	if (! (e->kindIndex == K_CLASS || e->kindIndex == K_MODULE))
6527af21814SMasatake YAMATO 		return;
6537af21814SMasatake YAMATO 
6543fd929a8SMasatake YAMATO 	if (isspace (**cp) || (**cp == '('))
6557af21814SMasatake YAMATO 	{
6563fd929a8SMasatake YAMATO 		if (isspace (**cp))
6573fd929a8SMasatake YAMATO 			skipWhitespace (cp);
6583fd929a8SMasatake YAMATO 		if (**cp == '(')
6593fd929a8SMasatake YAMATO 			++*cp;
6603fd929a8SMasatake YAMATO 
6617af21814SMasatake YAMATO 		vString *spec = vStringNewInit (how_mixin);
6627af21814SMasatake YAMATO 		vStringPut(spec, ':');
6637af21814SMasatake YAMATO 
6647af21814SMasatake YAMATO 		size_t len = vStringLength (spec);
6657af21814SMasatake YAMATO 		parseIdentifier (cp, spec, K_MODULE);
6667af21814SMasatake YAMATO 		if (len == vStringLength (spec))
6677af21814SMasatake YAMATO 		{
6687af21814SMasatake YAMATO 			vStringDelete (spec);
6697af21814SMasatake YAMATO 			return;
6707af21814SMasatake YAMATO 		}
6717af21814SMasatake YAMATO 
6727af21814SMasatake YAMATO 		struct blockData *bdata =  nestingLevelGetUserData (nl);
6737af21814SMasatake YAMATO 		if (bdata->mixin == NULL)
6747af21814SMasatake YAMATO 			bdata->mixin = stringListNew ();
6757af21814SMasatake YAMATO 		stringListAdd (bdata->mixin, spec);
6767af21814SMasatake YAMATO 	}
6777af21814SMasatake YAMATO }
6787af21814SMasatake YAMATO 
enterUnnamedScope(void)6793ae02089SMasatake YAMATO static void enterUnnamedScope (void)
6803ae02089SMasatake YAMATO {
681885fbc2cSMasatake YAMATO 	int r = CORK_NIL;
682bb7b947aSColomban Wendling 	NestingLevel *parent = nestingLevelsGetCurrent (nesting);
683885fbc2cSMasatake YAMATO 	tagEntryInfo *e_parent = getEntryOfNestingLevel (parent);
684885fbc2cSMasatake YAMATO 
685885fbc2cSMasatake YAMATO 	if (e_parent)
686885fbc2cSMasatake YAMATO 	{
687885fbc2cSMasatake YAMATO 		tagEntryInfo e;
688f92e6bf2SMasatake YAMATO 		initTagEntry (&e, "", e_parent->kindIndex);
689885fbc2cSMasatake YAMATO 		e.placeholder = 1;
690885fbc2cSMasatake YAMATO 		r = makeTagEntry (&e);
691885fbc2cSMasatake YAMATO 	}
692885fbc2cSMasatake YAMATO 	nestingLevelsPush (nesting, r);
6933ae02089SMasatake YAMATO }
6943ae02089SMasatake YAMATO 
parasiteToScope(rubySubparser * subparser,int subparserCorkIndex)695ea6c7e11SMasatake YAMATO static void parasiteToScope (rubySubparser *subparser, int subparserCorkIndex)
696ea6c7e11SMasatake YAMATO {
697ea6c7e11SMasatake YAMATO 	NestingLevel *nl = nestingLevelsGetCurrent (nesting);
698ea6c7e11SMasatake YAMATO 	struct blockData *bdata =  nestingLevelGetUserData (nl);
699ea6c7e11SMasatake YAMATO 	bdata->subparser = subparser;
700ea6c7e11SMasatake YAMATO 	bdata->subparserCorkIndex = subparserCorkIndex;
701ea6c7e11SMasatake YAMATO 
702ea6c7e11SMasatake YAMATO 	if (subparser->enterBlockNotify)
703ea6c7e11SMasatake YAMATO 		subparser->enterBlockNotify (subparser, subparserCorkIndex);
704ea6c7e11SMasatake YAMATO }
705ea6c7e11SMasatake YAMATO 
attachMixinField(int corkIndex,stringList * mixinSpec)7067af21814SMasatake YAMATO static void attachMixinField (int corkIndex, stringList *mixinSpec)
7077af21814SMasatake YAMATO {
7087af21814SMasatake YAMATO 	vString *mixinField = stringListItem (mixinSpec, 0);
7097af21814SMasatake YAMATO 	for (unsigned int i = 1; i < stringListCount (mixinSpec); i++)
7107af21814SMasatake YAMATO 	{
7117af21814SMasatake YAMATO 		vStringPut (mixinField, ',');
712999a44acSMasatake YAMATO 		vStringCat (mixinField, stringListItem (mixinSpec, i));
7137af21814SMasatake YAMATO 	}
7147af21814SMasatake YAMATO 
7157af21814SMasatake YAMATO 	attachParserFieldToCorkEntry (corkIndex, RubyFields [F_MIXIN].ftype,
7167af21814SMasatake YAMATO 								  vStringValue (mixinField));
7177af21814SMasatake YAMATO }
7187af21814SMasatake YAMATO 
deleteBlockData(NestingLevel * nl,void * data CTAGS_ATTR_UNUSED)7192e3d0c31SMasatake YAMATO static void deleteBlockData (NestingLevel *nl, void *data CTAGS_ATTR_UNUSED)
7207af21814SMasatake YAMATO {
7217af21814SMasatake YAMATO 	struct blockData *bdata = nestingLevelGetUserData (nl);
7227af21814SMasatake YAMATO 
7237af21814SMasatake YAMATO 	if (nl->corkIndex != CORK_NIL
7247af21814SMasatake YAMATO 		&& bdata->mixin != NULL
7257af21814SMasatake YAMATO 		&& stringListCount (bdata->mixin) > 0)
7267af21814SMasatake YAMATO 		attachMixinField (nl->corkIndex, bdata->mixin);
7277af21814SMasatake YAMATO 
72836d2dd51SMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (nl->corkIndex);
72936d2dd51SMasatake YAMATO 	if (e && !e->placeholder)
73036d2dd51SMasatake YAMATO 			e->extensionFields.endLine = getInputLineNumber ();
73136d2dd51SMasatake YAMATO 
732ea6c7e11SMasatake YAMATO 	tagEntryInfo *sub_e;
733ea6c7e11SMasatake YAMATO 	if (bdata->subparserCorkIndex != CORK_NIL
734ea6c7e11SMasatake YAMATO 		&& (sub_e = getEntryInCorkQueue (bdata->subparserCorkIndex)))
735ea6c7e11SMasatake YAMATO 	{
736ea6c7e11SMasatake YAMATO 		sub_e->extensionFields.endLine = getInputLineNumber ();
737ea6c7e11SMasatake YAMATO 		if (bdata->subparser)
738ea6c7e11SMasatake YAMATO 			bdata->subparser->leaveBlockNotify (bdata->subparser,
739ea6c7e11SMasatake YAMATO 												bdata->subparserCorkIndex);
740ea6c7e11SMasatake YAMATO 	}
741ea6c7e11SMasatake YAMATO 
7427af21814SMasatake YAMATO 	if (bdata->mixin)
7437af21814SMasatake YAMATO 		stringListDelete (bdata->mixin);
7447af21814SMasatake YAMATO }
7457af21814SMasatake YAMATO 
doesLineIncludeConstant(const unsigned char ** cp,vString * constant)746ebf51addSMasatake YAMATO static bool doesLineIncludeConstant (const unsigned char **cp, vString *constant)
747ebf51addSMasatake YAMATO {
748319f7605SMasatake YAMATO 	const unsigned char *p = *cp;
749ebf51addSMasatake YAMATO 
750319f7605SMasatake YAMATO 	if (isspace (*p))
751319f7605SMasatake YAMATO 		skipWhitespace (&p);
752ebf51addSMasatake YAMATO 
753319f7605SMasatake YAMATO 	if (isupper (*p))
754ebf51addSMasatake YAMATO 	{
755319f7605SMasatake YAMATO 		while (*p != 0 && isIdentChar (*p))
756ebf51addSMasatake YAMATO 		{
757319f7605SMasatake YAMATO 			vStringPut (constant, *p);
758319f7605SMasatake YAMATO 			++p;
759ebf51addSMasatake YAMATO 		}
760319f7605SMasatake YAMATO 		if (isspace (*p))
761319f7605SMasatake YAMATO 			skipWhitespace (&p);
762319f7605SMasatake YAMATO 		if (*p == '=')
763ebf51addSMasatake YAMATO 		{
764319f7605SMasatake YAMATO 			*cp = p;
765ebf51addSMasatake YAMATO 			return true;
766ebf51addSMasatake YAMATO 		}
767ebf51addSMasatake YAMATO 		vStringClear (constant);
768ebf51addSMasatake YAMATO 	}
769ebf51addSMasatake YAMATO 
770ebf51addSMasatake YAMATO 	return false;
771ebf51addSMasatake YAMATO }
772ebf51addSMasatake YAMATO 
emitRubyAccessorTags(vString * a,bool reader,bool writer)773673eb0e5SMasatake YAMATO static void emitRubyAccessorTags (vString *a, bool reader, bool writer)
774673eb0e5SMasatake YAMATO {
775673eb0e5SMasatake YAMATO 	if (vStringLength (a) == 0)
776673eb0e5SMasatake YAMATO 		return;
777ebf51addSMasatake YAMATO 
778673eb0e5SMasatake YAMATO 	if (reader)
779673eb0e5SMasatake YAMATO 		emitRubyTagFull (a, K_ACCESSOR, false, !writer);
780673eb0e5SMasatake YAMATO 	if (writer)
781673eb0e5SMasatake YAMATO 	{
782673eb0e5SMasatake YAMATO 		vStringPut (a, '=');
783673eb0e5SMasatake YAMATO 		emitRubyTagFull (a, K_ACCESSOR, false, true);
784673eb0e5SMasatake YAMATO 	}
785673eb0e5SMasatake YAMATO }
786673eb0e5SMasatake YAMATO 
readAttrsAndEmitTags(const unsigned char ** cp,bool reader,bool writer)787673eb0e5SMasatake YAMATO static void readAttrsAndEmitTags (const unsigned char **cp, bool reader, bool writer)
788673eb0e5SMasatake YAMATO {
789673eb0e5SMasatake YAMATO 	vString *a = vStringNew ();
790673eb0e5SMasatake YAMATO 
791673eb0e5SMasatake YAMATO 	skipWhitespace (cp);
792673eb0e5SMasatake YAMATO 	if (**cp == '(')
793673eb0e5SMasatake YAMATO 		++*cp;
794673eb0e5SMasatake YAMATO 
795673eb0e5SMasatake YAMATO 	do {
796673eb0e5SMasatake YAMATO 		skipWhitespace (cp);
797673eb0e5SMasatake YAMATO 		if (**cp == ':')
798673eb0e5SMasatake YAMATO 		{
799673eb0e5SMasatake YAMATO 			++*cp;
800673eb0e5SMasatake YAMATO 			if (K_METHOD == parseIdentifier (cp, a, K_METHOD))
801673eb0e5SMasatake YAMATO 			{
802673eb0e5SMasatake YAMATO 				emitRubyAccessorTags (a, reader, writer);
803673eb0e5SMasatake YAMATO 				skipWhitespace (cp);
804673eb0e5SMasatake YAMATO 				if (**cp == ',')
805673eb0e5SMasatake YAMATO 				{
806673eb0e5SMasatake YAMATO 					++*cp;
807673eb0e5SMasatake YAMATO 					continue;
808673eb0e5SMasatake YAMATO 				}
809673eb0e5SMasatake YAMATO 			}
810673eb0e5SMasatake YAMATO 		}
81142bc7f49SMasatake YAMATO 		else if (**cp == '"' || **cp == '\'')
812673eb0e5SMasatake YAMATO 		{
81342bc7f49SMasatake YAMATO 			unsigned char b = **cp;
814673eb0e5SMasatake YAMATO 			++*cp;
81542bc7f49SMasatake YAMATO 			parseString (cp, b, a);
816673eb0e5SMasatake YAMATO 
817673eb0e5SMasatake YAMATO 			emitRubyAccessorTags (a, reader, writer);
818673eb0e5SMasatake YAMATO 			skipWhitespace (cp);
819673eb0e5SMasatake YAMATO 			if (**cp == ',')
820673eb0e5SMasatake YAMATO 			{
821673eb0e5SMasatake YAMATO 				++*cp;
822673eb0e5SMasatake YAMATO 				continue;
823673eb0e5SMasatake YAMATO 			}
824673eb0e5SMasatake YAMATO 		}
825673eb0e5SMasatake YAMATO 		break;
826673eb0e5SMasatake YAMATO 	} while (1);
827673eb0e5SMasatake YAMATO 
828673eb0e5SMasatake YAMATO 	vStringDelete (a);
829673eb0e5SMasatake YAMATO }
830ebf51addSMasatake YAMATO 
readAliasMethodAndEmitTags(const unsigned char ** cp)8316e6300d2SMasatake YAMATO static int readAliasMethodAndEmitTags (const unsigned char **cp)
8326e6300d2SMasatake YAMATO {
8336e6300d2SMasatake YAMATO 	int r = CORK_NIL;
8346e6300d2SMasatake YAMATO 	vString *a = vStringNew ();
8356e6300d2SMasatake YAMATO 
8366e6300d2SMasatake YAMATO 	skipWhitespace (cp);
8376e6300d2SMasatake YAMATO 	if (**cp == '(')
8386e6300d2SMasatake YAMATO 		++*cp;
8396e6300d2SMasatake YAMATO 
8406e6300d2SMasatake YAMATO 	skipWhitespace (cp);
8416e6300d2SMasatake YAMATO 	if (**cp == ':')
8426e6300d2SMasatake YAMATO 	{
8436e6300d2SMasatake YAMATO 		++*cp;
8446e6300d2SMasatake YAMATO 		if (K_METHOD != parseIdentifier (cp, a, K_METHOD))
8456e6300d2SMasatake YAMATO 			vStringClear (a);
8466e6300d2SMasatake YAMATO 	}
84742bc7f49SMasatake YAMATO 	else if (**cp == '"' || **cp == '\'')
8486e6300d2SMasatake YAMATO 	{
84942bc7f49SMasatake YAMATO 		unsigned char b = **cp;
8506e6300d2SMasatake YAMATO 		++*cp;
85142bc7f49SMasatake YAMATO 		parseString (cp, b, a);
8526e6300d2SMasatake YAMATO 	}
8536e6300d2SMasatake YAMATO 
8546e6300d2SMasatake YAMATO 	if (vStringLength (a) > 0)
8556e6300d2SMasatake YAMATO 		r = emitRubyTagFull (a, K_ALIAS, false, false);
8566e6300d2SMasatake YAMATO 
8576e6300d2SMasatake YAMATO 	vStringDelete (a);
8586e6300d2SMasatake YAMATO 	return r;
8596e6300d2SMasatake YAMATO }
8606e6300d2SMasatake YAMATO 
readStringAndEmitTag(const unsigned char ** cp,rubyKind kind,int role)861b28df83aSMasatake YAMATO static int readStringAndEmitTag (const unsigned char **cp, rubyKind kind, int role)
862b28df83aSMasatake YAMATO {
863b28df83aSMasatake YAMATO 	int r = CORK_NIL;
864b28df83aSMasatake YAMATO 	vString *s = NULL;
865b28df83aSMasatake YAMATO 
866b28df83aSMasatake YAMATO 	skipWhitespace (cp);
867b28df83aSMasatake YAMATO 	if (**cp == '(')
868b28df83aSMasatake YAMATO 		++*cp;
869b28df83aSMasatake YAMATO 
870b28df83aSMasatake YAMATO 	skipWhitespace (cp);
87142bc7f49SMasatake YAMATO 	if (**cp == '"' || **cp == '\'')
872b28df83aSMasatake YAMATO 	{
87342bc7f49SMasatake YAMATO 		unsigned char b = **cp;
874b28df83aSMasatake YAMATO 		++*cp;
875b28df83aSMasatake YAMATO 		s = vStringNew ();
87642bc7f49SMasatake YAMATO 		parseString (cp, b, s);
877b28df83aSMasatake YAMATO 	}
878b28df83aSMasatake YAMATO 
879b28df83aSMasatake YAMATO 	if (s && vStringLength (s) > 0)
880b28df83aSMasatake YAMATO 		r = makeSimpleRefTag (s, kind, role);
881b28df83aSMasatake YAMATO 
882b28df83aSMasatake YAMATO 	vStringDelete (s);
883b28df83aSMasatake YAMATO 	return r;
884b28df83aSMasatake YAMATO }
885b28df83aSMasatake YAMATO 
readAndEmitDef(const unsigned char ** cp)8861ea4d72eSMasatake YAMATO static int readAndEmitDef (const unsigned char **cp)
8871ea4d72eSMasatake YAMATO {
8881ea4d72eSMasatake YAMATO 	rubyKind kind = K_METHOD;
8891ea4d72eSMasatake YAMATO 	NestingLevel *nl = nestingLevelsGetCurrent (nesting);
8901ea4d72eSMasatake YAMATO 	tagEntryInfo *e_scope  = getEntryOfNestingLevel (nl);
8911ea4d72eSMasatake YAMATO 
8921ea4d72eSMasatake YAMATO 	/* if the def is inside an unnamed scope at the class level, assume
8931ea4d72eSMasatake YAMATO 	 * it's from a singleton from a construct like this:
8941ea4d72eSMasatake YAMATO 	 *
8951ea4d72eSMasatake YAMATO 	 * class C
8961ea4d72eSMasatake YAMATO 	 *   class << self
8971ea4d72eSMasatake YAMATO 	 *     def singleton
8981ea4d72eSMasatake YAMATO 	 *       ...
8991ea4d72eSMasatake YAMATO 	 *     end
9001ea4d72eSMasatake YAMATO 	 *   end
9011ea4d72eSMasatake YAMATO 	 * end
9021ea4d72eSMasatake YAMATO 	 */
9031ea4d72eSMasatake YAMATO 	if (e_scope && e_scope->kindIndex == K_CLASS && strlen (e_scope->name) == 0)
9041ea4d72eSMasatake YAMATO 		kind = K_SINGLETON;
9051ea4d72eSMasatake YAMATO 	int corkIndex = readAndEmitTag (cp, kind);
9061ea4d72eSMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
9071ea4d72eSMasatake YAMATO 
9081ea4d72eSMasatake YAMATO 	/* Fill signature: field. */
9091ea4d72eSMasatake YAMATO 	if (e)
9101ea4d72eSMasatake YAMATO 	{
9111ea4d72eSMasatake YAMATO 		vString *signature = vStringNewInit ("(");
9121ea4d72eSMasatake YAMATO 		skipWhitespace (cp);
9131ea4d72eSMasatake YAMATO 		if (**cp == '(')
9141ea4d72eSMasatake YAMATO 		{
9151ea4d72eSMasatake YAMATO 			++(*cp);
9161ea4d72eSMasatake YAMATO 			parseSignature (cp, signature);
9171ea4d72eSMasatake YAMATO 			if (vStringLast(signature) != ')')
9181ea4d72eSMasatake YAMATO 			{
9191ea4d72eSMasatake YAMATO 				vStringDelete (signature);
9201ea4d72eSMasatake YAMATO 				signature = NULL;
9211ea4d72eSMasatake YAMATO 			}
9221ea4d72eSMasatake YAMATO 		}
9231ea4d72eSMasatake YAMATO 		else
9241ea4d72eSMasatake YAMATO 			vStringPut (signature, ')');
9251ea4d72eSMasatake YAMATO 		e->extensionFields.signature = vStringDeleteUnwrap (signature);
9261ea4d72eSMasatake YAMATO 		signature = NULL;;
9271ea4d72eSMasatake YAMATO 		vStringDelete (signature);
9281ea4d72eSMasatake YAMATO 	}
9291ea4d72eSMasatake YAMATO 	return corkIndex;
9301ea4d72eSMasatake YAMATO }
9311ea4d72eSMasatake YAMATO 
notifyLine(const unsigned char ** cp)932ea6c7e11SMasatake YAMATO static rubySubparser *notifyLine (const unsigned char **cp)
933ea6c7e11SMasatake YAMATO {
934ea6c7e11SMasatake YAMATO 	subparser *sub;
935ea6c7e11SMasatake YAMATO 	rubySubparser *rubysub = NULL;
936ea6c7e11SMasatake YAMATO 
937ea6c7e11SMasatake YAMATO 	foreachSubparser (sub, false)
938ea6c7e11SMasatake YAMATO 	{
939ea6c7e11SMasatake YAMATO 		rubysub = (rubySubparser *)sub;
940ea6c7e11SMasatake YAMATO 		rubysub->corkIndex = CORK_NIL;
941ea6c7e11SMasatake YAMATO 
942ea6c7e11SMasatake YAMATO 		if (rubysub->lineNotify)
943ea6c7e11SMasatake YAMATO 		{
944ea6c7e11SMasatake YAMATO 			enterSubparser(sub);
945ea6c7e11SMasatake YAMATO 			const unsigned char *base = *cp;
946ea6c7e11SMasatake YAMATO 			rubysub->corkIndex = rubysub->lineNotify(rubysub, cp);
947ea6c7e11SMasatake YAMATO 			leaveSubparser();
948ea6c7e11SMasatake YAMATO 			if (rubysub->corkIndex != CORK_NIL)
949ea6c7e11SMasatake YAMATO 				break;
950ea6c7e11SMasatake YAMATO 			*cp = base;
951ea6c7e11SMasatake YAMATO 		}
952ea6c7e11SMasatake YAMATO 	}
953ea6c7e11SMasatake YAMATO 
954ea6c7e11SMasatake YAMATO 	if (rubysub && rubysub->corkIndex != CORK_NIL)
955ea6c7e11SMasatake YAMATO 		return rubysub;
956ea6c7e11SMasatake YAMATO 	return NULL;
957ea6c7e11SMasatake YAMATO }
958ea6c7e11SMasatake YAMATO 
findRubyTags(void)9593ae02089SMasatake YAMATO static void findRubyTags (void)
9603ae02089SMasatake YAMATO {
9613ae02089SMasatake YAMATO 	const unsigned char *line;
962ce990805SThomas Braun 	bool inMultiLineComment = false;
963ebf51addSMasatake YAMATO 	vString *constant = vStringNew ();
964*6b212cadSMasatake YAMATO 	bool found_rdoc = false;
9653ae02089SMasatake YAMATO 
9667af21814SMasatake YAMATO 	nesting = nestingLevelsNewFull (sizeof (struct blockData), deleteBlockData);
9673ae02089SMasatake YAMATO 
9683ae02089SMasatake YAMATO 	/* FIXME: this whole scheme is wrong, because Ruby isn't line-based.
9693ae02089SMasatake YAMATO 	* You could perfectly well write:
9703ae02089SMasatake YAMATO 	*
9713ae02089SMasatake YAMATO 	*  def
9723ae02089SMasatake YAMATO 	*  method
9733ae02089SMasatake YAMATO 	*   puts("hello")
9743ae02089SMasatake YAMATO 	*  end
9753ae02089SMasatake YAMATO 	*
9763ae02089SMasatake YAMATO 	* if you wished, and this function would fail to recognize anything.
9773ae02089SMasatake YAMATO 	*/
9781b312fe7SMasatake YAMATO 	while ((line = readLineFromInputFile ()) != NULL)
9793ae02089SMasatake YAMATO 	{
980ea6c7e11SMasatake YAMATO 		rubySubparser *subparser = CORK_NIL;
9813ae02089SMasatake YAMATO 		const unsigned char *cp = line;
982cb41c1b3SColomban Wendling 		/* if we expect a separator after a while, for, or until statement
983cb41c1b3SColomban Wendling 		 * separators are "do", ";" or newline */
984ce990805SThomas Braun 		bool expect_separator = false;
9853ae02089SMasatake YAMATO 
986*6b212cadSMasatake YAMATO 		if (found_rdoc == false && strncmp ((const char*)cp, "# =", 3) == 0)
987*6b212cadSMasatake YAMATO 		{
988*6b212cadSMasatake YAMATO 			found_rdoc = true;
989*6b212cadSMasatake YAMATO 			makePromise ("RDoc", 0, 0, 0, 0, 0);
990*6b212cadSMasatake YAMATO 		}
991*6b212cadSMasatake YAMATO 
9925a560633SColomban Wendling 		if (canMatch (&cp, "=begin", isWhitespace))
9933ae02089SMasatake YAMATO 		{
994ce990805SThomas Braun 			inMultiLineComment = true;
9953ae02089SMasatake YAMATO 			continue;
9963ae02089SMasatake YAMATO 		}
9975a560633SColomban Wendling 		if (canMatch (&cp, "=end", isWhitespace))
9983ae02089SMasatake YAMATO 		{
999ce990805SThomas Braun 			inMultiLineComment = false;
10003ae02089SMasatake YAMATO 			continue;
10013ae02089SMasatake YAMATO 		}
100252e4bb3cSColomban Wendling 		if (inMultiLineComment)
100352e4bb3cSColomban Wendling 			continue;
10043ae02089SMasatake YAMATO 
10053ae02089SMasatake YAMATO 		skipWhitespace (&cp);
10063ae02089SMasatake YAMATO 
10073ae02089SMasatake YAMATO 		/* Avoid mistakenly starting a scope for modifiers such as
10083ae02089SMasatake YAMATO 		*
10093ae02089SMasatake YAMATO 		*   return if <exp>
10103ae02089SMasatake YAMATO 		*
10119782f2a5STambet Arak 		* FIXME: we're fooled if someone does something heinous such as
10123ae02089SMasatake YAMATO 		*
10133ae02089SMasatake YAMATO 		*   puts("hello") \
10143ae02089SMasatake YAMATO 		*       unless <exp>
10153ae02089SMasatake YAMATO 		*/
10169782f2a5STambet Arak 
10179782f2a5STambet Arak 		if (canMatchKeywordWithAssign (&cp, "for") ||
10189782f2a5STambet Arak 		    canMatchKeywordWithAssign (&cp, "until") ||
10199782f2a5STambet Arak 		    canMatchKeywordWithAssign (&cp, "while"))
10203ae02089SMasatake YAMATO 		{
1021ce990805SThomas Braun 			expect_separator = true;
1022cb41c1b3SColomban Wendling 			enterUnnamedScope ();
1023cb41c1b3SColomban Wendling 		}
10249782f2a5STambet Arak 		else if (canMatchKeywordWithAssign (&cp, "case") ||
10259782f2a5STambet Arak 		         canMatchKeywordWithAssign (&cp, "if") ||
10269782f2a5STambet Arak 		         canMatchKeywordWithAssign (&cp, "unless"))
1027cb41c1b3SColomban Wendling 		{
10283ae02089SMasatake YAMATO 			enterUnnamedScope ();
10293ae02089SMasatake YAMATO 		}
10303ae02089SMasatake YAMATO 
10313ae02089SMasatake YAMATO 		/*
10323ae02089SMasatake YAMATO 		* "module M", "class C" and "def m" should only be at the beginning
10333ae02089SMasatake YAMATO 		* of a line.
10343ae02089SMasatake YAMATO 		*/
10359782f2a5STambet Arak 		if (canMatchKeywordWithAssign (&cp, "module"))
10363ae02089SMasatake YAMATO 		{
10373ae02089SMasatake YAMATO 			readAndEmitTag (&cp, K_MODULE);
10383ae02089SMasatake YAMATO 		}
10394f481d0eSMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "class")
10404f481d0eSMasatake YAMATO 				 || (canMatchKeywordWithAssign (&cp, "Class.new")))
10414f481d0eSMasatake YAMATO 
10423ae02089SMasatake YAMATO 		{
10434f481d0eSMasatake YAMATO 
10444f481d0eSMasatake YAMATO 			int r;
10454f481d0eSMasatake YAMATO 			if (*(cp - 1) != 's')
10464f481d0eSMasatake YAMATO 				r = emitRubyTagFull(NULL, K_CLASS, true, false);
10474f481d0eSMasatake YAMATO 			else
10484f481d0eSMasatake YAMATO 				r = readAndEmitTag (&cp, K_CLASS); /* "class" */
10494f481d0eSMasatake YAMATO 
10503671ad72SMasatake YAMATO 			tagEntryInfo *e = getEntryInCorkQueue (r);
105167997237SMasatake YAMATO 
10523671ad72SMasatake YAMATO 			if (e)
105367997237SMasatake YAMATO 			{
105467997237SMasatake YAMATO 				skipWhitespace (&cp);
105567997237SMasatake YAMATO 				if (*cp == '<' && *(cp + 1) != '<')
105667997237SMasatake YAMATO 				{
105767997237SMasatake YAMATO 					cp++;
105867997237SMasatake YAMATO 					vString *parent = vStringNew ();
105967997237SMasatake YAMATO 					parseIdentifier (&cp, parent, K_CLASS);
106067997237SMasatake YAMATO 					if (vStringLength (parent) > 0)
106167997237SMasatake YAMATO 						e->extensionFields.inheritance = vStringDeleteUnwrap (parent);
106267997237SMasatake YAMATO 					else
106367997237SMasatake YAMATO 						vStringDelete (parent);
106467997237SMasatake YAMATO 				}
106567997237SMasatake YAMATO 			}
10663ae02089SMasatake YAMATO 		}
10670912d1e5SMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "include"))
10680912d1e5SMasatake YAMATO 		{
10690912d1e5SMasatake YAMATO 			readAndStoreMixinSpec (&cp, "include");
10700912d1e5SMasatake YAMATO 		}
107183096cc4SAmaiKinono 		else if (canMatchKeywordWithAssign (&cp, "prepend"))
107283096cc4SAmaiKinono 		{
107383096cc4SAmaiKinono 			readAndStoreMixinSpec (&cp, "prepend");
107483096cc4SAmaiKinono 		}
107583096cc4SAmaiKinono 		else if (canMatchKeywordWithAssign (&cp, "extend"))
107683096cc4SAmaiKinono 		{
107783096cc4SAmaiKinono 			readAndStoreMixinSpec (&cp, "extend");
107883096cc4SAmaiKinono 		}
10799782f2a5STambet Arak 		else if (canMatchKeywordWithAssign (&cp, "def"))
10803ae02089SMasatake YAMATO 		{
10811ea4d72eSMasatake YAMATO 			readAndEmitDef (&cp);
10823ae02089SMasatake YAMATO 		}
1083673eb0e5SMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "attr_reader"))
1084673eb0e5SMasatake YAMATO 		{
1085673eb0e5SMasatake YAMATO 			readAttrsAndEmitTags (&cp, true, false);
1086673eb0e5SMasatake YAMATO 		}
1087673eb0e5SMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "attr_writer"))
1088673eb0e5SMasatake YAMATO 		{
1089673eb0e5SMasatake YAMATO 			readAttrsAndEmitTags (&cp, false, true);
1090673eb0e5SMasatake YAMATO 		}
1091673eb0e5SMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "attr_accessor"))
1092673eb0e5SMasatake YAMATO 		{
1093673eb0e5SMasatake YAMATO 			readAttrsAndEmitTags (&cp, true, true);
1094673eb0e5SMasatake YAMATO 		}
1095ebf51addSMasatake YAMATO 		else if (doesLineIncludeConstant (&cp, constant))
1096ebf51addSMasatake YAMATO 		{
1097ebf51addSMasatake YAMATO 			emitRubyTag (constant, K_CONST);
1098ebf51addSMasatake YAMATO 			vStringClear (constant);
1099ebf51addSMasatake YAMATO 		}
1100b28df83aSMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "require"))
1101b28df83aSMasatake YAMATO 		{
1102b28df83aSMasatake YAMATO 			readStringAndEmitTag (&cp, K_LIBRARY, RUBY_LIBRARY_REQUIRED);
1103b28df83aSMasatake YAMATO 		}
1104b28df83aSMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "require_relative"))
1105b28df83aSMasatake YAMATO 		{
1106b28df83aSMasatake YAMATO 			readStringAndEmitTag (&cp, K_LIBRARY, RUBY_LIBRARY_REQUIRED_REL);
1107b28df83aSMasatake YAMATO 		}
1108b28df83aSMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "load"))
1109b28df83aSMasatake YAMATO 		{
1110b28df83aSMasatake YAMATO 			readStringAndEmitTag (&cp, K_LIBRARY, RUBY_LIBRARY_LOADED);
1111b28df83aSMasatake YAMATO 		}
111284ab6d2cSMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "alias"))
111384ab6d2cSMasatake YAMATO 		{
111484ab6d2cSMasatake YAMATO 			if (!readAndEmitTagFull (&cp, K_ALIAS, false, true)
111584ab6d2cSMasatake YAMATO 				&& (*cp == '$'))
111684ab6d2cSMasatake YAMATO 			{
111784ab6d2cSMasatake YAMATO 				/* Alias for a global variable. */
111884ab6d2cSMasatake YAMATO 				++cp;
111984ab6d2cSMasatake YAMATO 				vString *alias = vStringNew ();
112084ab6d2cSMasatake YAMATO 				vStringPut (alias, '$');
112184ab6d2cSMasatake YAMATO 				if (K_METHOD == parseIdentifier (&cp, alias, K_METHOD)
112284ab6d2cSMasatake YAMATO 					&& vStringLength (alias) > 0)
112384ab6d2cSMasatake YAMATO 					emitRubyTagFull (alias, K_ALIAS, false, false);
112484ab6d2cSMasatake YAMATO 				vStringDelete (alias);
112584ab6d2cSMasatake YAMATO 			}
112684ab6d2cSMasatake YAMATO 		}
11276e6300d2SMasatake YAMATO 		else if (canMatchKeywordWithAssign (&cp, "alias_method"))
11286e6300d2SMasatake YAMATO 			readAliasMethodAndEmitTags (&cp);
11292b079e4dSMasatake YAMATO 		else if ((canMatchKeywordWithAssign (&cp, "private")
11301ea4d72eSMasatake YAMATO 				  || canMatchKeywordWithAssign (&cp, "protected")
11312b079e4dSMasatake YAMATO 				  || canMatchKeywordWithAssign (&cp, "public")
11322b079e4dSMasatake YAMATO 				  || canMatchKeywordWithAssign (&cp, "private_class_method")
11332b079e4dSMasatake YAMATO 				  || canMatchKeywordWithAssign (&cp, "public_class_method")))
11341ea4d72eSMasatake YAMATO 		{
11351ea4d72eSMasatake YAMATO 			skipWhitespace (&cp);
11361ea4d72eSMasatake YAMATO 			if (canMatchKeywordWithAssign (&cp, "def"))
11371ea4d72eSMasatake YAMATO 				readAndEmitDef (&cp);
11381ea4d72eSMasatake YAMATO 			/* TODO: store the method for controlling visibility
11391ea4d72eSMasatake YAMATO 			 * to the "access:" field of the tag.*/
11401ea4d72eSMasatake YAMATO 		}
1141ea6c7e11SMasatake YAMATO 		else
1142ea6c7e11SMasatake YAMATO 			subparser = notifyLine(&cp);
11431ea4d72eSMasatake YAMATO 
1144ebf51addSMasatake YAMATO 
11453ae02089SMasatake YAMATO 		while (*cp != '\0')
11463ae02089SMasatake YAMATO 		{
11473ae02089SMasatake YAMATO 			/* FIXME: we don't cope with here documents,
11483ae02089SMasatake YAMATO 			* or regular expression literals, or ... you get the idea.
11493ae02089SMasatake YAMATO 			* Hopefully, the restriction above that insists on seeing
11503ae02089SMasatake YAMATO 			* definitions at the starts of lines should keep us out of
11513ae02089SMasatake YAMATO 			* mischief.
11523ae02089SMasatake YAMATO 			*/
11533ae02089SMasatake YAMATO 			if (inMultiLineComment || isspace (*cp))
11543ae02089SMasatake YAMATO 			{
11553ae02089SMasatake YAMATO 				++cp;
11563ae02089SMasatake YAMATO 			}
11573ae02089SMasatake YAMATO 			else if (*cp == '#')
11583ae02089SMasatake YAMATO 			{
11593ae02089SMasatake YAMATO 				/* FIXME: this is wrong, but there *probably* won't be a
11603ae02089SMasatake YAMATO 				* definition after an interpolated string (where # doesn't
11613ae02089SMasatake YAMATO 				* mean 'comment').
11623ae02089SMasatake YAMATO 				*/
11633ae02089SMasatake YAMATO 				break;
11643ae02089SMasatake YAMATO 			}
1165cb41c1b3SColomban Wendling 			else if (canMatchKeyword (&cp, "begin"))
11663ae02089SMasatake YAMATO 			{
11673ae02089SMasatake YAMATO 				enterUnnamedScope ();
11683ae02089SMasatake YAMATO 			}
1169cb41c1b3SColomban Wendling 			else if (canMatchKeyword (&cp, "do"))
1170cb41c1b3SColomban Wendling 			{
1171cb41c1b3SColomban Wendling 				if (! expect_separator)
1172ea6c7e11SMasatake YAMATO 				{
1173cb41c1b3SColomban Wendling 					enterUnnamedScope ();
1174ea6c7e11SMasatake YAMATO 					if (subparser && subparser->corkIndex)
1175ea6c7e11SMasatake YAMATO 						parasiteToScope (subparser, subparser->corkIndex);
1176ea6c7e11SMasatake YAMATO 				}
1177cb41c1b3SColomban Wendling 				else
1178ce990805SThomas Braun 					expect_separator = false;
1179cb41c1b3SColomban Wendling 			}
1180bb7b947aSColomban Wendling 			else if (canMatchKeyword (&cp, "end") && nesting->n > 0)
11813ae02089SMasatake YAMATO 			{
11823ae02089SMasatake YAMATO 				/* Leave the most recent scope. */
1183bb7b947aSColomban Wendling 				nestingLevelsPop (nesting);
11843ae02089SMasatake YAMATO 			}
118542bc7f49SMasatake YAMATO 			else if (*cp == '"' || *cp == '\'')
11863ae02089SMasatake YAMATO 			{
118742bc7f49SMasatake YAMATO 				unsigned char b = *cp;
11883ae02089SMasatake YAMATO 				/* Skip string literals.
11893ae02089SMasatake YAMATO 				 * FIXME: should cope with escapes and interpolation.
11903ae02089SMasatake YAMATO 				 */
11913ae02089SMasatake YAMATO 				++cp;
119242bc7f49SMasatake YAMATO 				parseString (&cp, b, NULL);
11933ae02089SMasatake YAMATO 			}
1194cb41c1b3SColomban Wendling 			else if (*cp == ';')
1195cb41c1b3SColomban Wendling 			{
1196cb41c1b3SColomban Wendling 				++cp;
1197ce990805SThomas Braun 				expect_separator = false;
1198cb41c1b3SColomban Wendling 			}
11993ae02089SMasatake YAMATO 			else if (*cp != '\0')
12003ae02089SMasatake YAMATO 			{
12013ae02089SMasatake YAMATO 				do
12023ae02089SMasatake YAMATO 					++cp;
1203995979c6SColomban Wendling 				while (isIdentChar (*cp));
12043ae02089SMasatake YAMATO 			}
12053ae02089SMasatake YAMATO 		}
12063ae02089SMasatake YAMATO 	}
1207bb7b947aSColomban Wendling 	nestingLevelsFree (nesting);
1208ebf51addSMasatake YAMATO 	vStringDelete (constant);
12093ae02089SMasatake YAMATO }
12103ae02089SMasatake YAMATO 
RubyParser(void)12113ae02089SMasatake YAMATO extern parserDefinition* RubyParser (void)
12123ae02089SMasatake YAMATO {
12133ae02089SMasatake YAMATO 	static const char *const extensions [] = { "rb", "ruby", NULL };
1214b29ae60fSMasatake YAMATO 	parserDefinition* def = parserNew ("Ruby");
121509ae690fSMasatake YAMATO 	def->kindTable      = RubyKinds;
12163db72c21SMasatake YAMATO 	def->kindCount  = ARRAY_SIZE (RubyKinds);
12173ae02089SMasatake YAMATO 	def->extensions = extensions;
12183ae02089SMasatake YAMATO 	def->parser     = findRubyTags;
1219c4711e83SMasatake YAMATO 	def->fieldTable = RubyFields;
1220c4711e83SMasatake YAMATO 	def->fieldCount = ARRAY_SIZE (RubyFields);
12216b1a862eSMasatake YAMATO 	def->useCork    = CORK_QUEUE;
12223ae02089SMasatake YAMATO 	return def;
12233ae02089SMasatake YAMATO }
1224