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