Backport changes from CTags SVN to fix parse problems in the Ruby parser.
Add filetype extension "*.ruby". git-svn-id: https://geany.svn.sourceforge.net/svnroot/geany/trunk@1563 ea778897-0a13-0410-b9d1-a72fbfd435f5
This commit is contained in:
parent
bd205a7374
commit
12104026d0
@ -4,6 +4,11 @@
|
|||||||
* geany.glade, src/dialogs.c, src/geany.h, src/interface.c,
|
* geany.glade, src/dialogs.c, src/geany.h, src/interface.c,
|
||||||
src/keyfile.c, src/main.c, src/prefs.c:
|
src/keyfile.c, src/main.c, src/prefs.c:
|
||||||
Add default startup directory option (closes #1704988).
|
Add default startup directory option (closes #1704988).
|
||||||
|
* tagmanager/ruby.c, tagmanager/strlist.c, tagmanager/strlist.h:
|
||||||
|
Backport changes from CTags SVN to fix parse problems in the Ruby
|
||||||
|
parser.
|
||||||
|
* data/filetype_extensions.conf, src/filetypes.c:
|
||||||
|
Add filetype extension "*.ruby".
|
||||||
|
|
||||||
|
|
||||||
2007-05-23 Nick Treleaven <nick.treleaven@btinternet.com>
|
2007-05-23 Nick Treleaven <nick.treleaven@btinternet.com>
|
||||||
|
@ -14,7 +14,7 @@ Perl=*.pl;*.perl;*.pm;*.agi;*.pod;
|
|||||||
PHP=*.php;*.php3;*.php4;*.php5;*.phtml;
|
PHP=*.php;*.php3;*.php4;*.php5;*.phtml;
|
||||||
Javascript=*.js;
|
Javascript=*.js;
|
||||||
Python=*.py;*.pyw;
|
Python=*.py;*.pyw;
|
||||||
Ruby=*.rb;*.rhtml;
|
Ruby=*.rb;*.rhtml;*.ruby;
|
||||||
Tcl=*.tcl;*.tk;*.wish;
|
Tcl=*.tcl;*.tk;*.wish;
|
||||||
Lua=*.lua;
|
Lua=*.lua;
|
||||||
Ferite=*.fe;
|
Ferite=*.fe;
|
||||||
|
@ -301,7 +301,7 @@ void filetypes_init_types()
|
|||||||
filetypes[GEANY_FILETYPES_RUBY]->has_tags = TRUE;
|
filetypes[GEANY_FILETYPES_RUBY]->has_tags = TRUE;
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->title = g_strdup(_("Ruby source file"));
|
filetypes[GEANY_FILETYPES_RUBY]->title = g_strdup(_("Ruby source file"));
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->extension = g_strdup("rb");
|
filetypes[GEANY_FILETYPES_RUBY]->extension = g_strdup("rb");
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->pattern = utils_strv_new("*.rb", "*.rhtml", NULL);
|
filetypes[GEANY_FILETYPES_RUBY]->pattern = utils_strv_new("*.rb", "*.rhtml", "*.ruby", NULL);
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->style_func_ptr = styleset_ruby;
|
filetypes[GEANY_FILETYPES_RUBY]->style_func_ptr = styleset_ruby;
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->comment_open = g_strdup("#");
|
filetypes[GEANY_FILETYPES_RUBY]->comment_open = g_strdup("#");
|
||||||
filetypes[GEANY_FILETYPES_RUBY]->comment_close = NULL;
|
filetypes[GEANY_FILETYPES_RUBY]->comment_close = NULL;
|
||||||
|
@ -1,198 +1,399 @@
|
|||||||
/*
|
/*
|
||||||
* $Id: ruby.c,v 1.2 2001/12/18 04:30:18 darren Exp $
|
* $Id$
|
||||||
*
|
*
|
||||||
* Copyright (c) 2000-2001, Thaddeus Covert <sahuagin@mediaone.net>
|
* Copyright (c) 2000-2001, Thaddeus Covert <sahuagin@mediaone.net>
|
||||||
|
* Copyright (c) 2002 Matthias Veit <matthias_veit@yahoo.de>
|
||||||
|
* Copyright (c) 2004 Elliott Hughes <enh@acm.org>
|
||||||
*
|
*
|
||||||
* This source code is released for free distribution under the terms of the
|
* This source code is released for free distribution under the terms of the
|
||||||
* GNU General Public License.
|
* GNU General Public License.
|
||||||
*
|
*
|
||||||
* This module contains functions for generating tags for Ruby language
|
* This module contains functions for generating tags for Ruby language
|
||||||
* files.
|
* files.
|
||||||
*
|
|
||||||
* Copyright (c) 2002, Matthias Veit <matthias_veit@yahoo.de>
|
|
||||||
* Enable parsing of class (in ruby: singleton) methods and mixins.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* INCLUDE FILES
|
* INCLUDE FILES
|
||||||
*/
|
*/
|
||||||
#include "general.h" /* must always come first */
|
#include "general.h" /* must always come first */
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "entry.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
#include "vstring.h"
|
#include "vstring.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DATA DEFINITIONS
|
* DATA DECLARATIONS
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
K_CLASS, K_METHOD, K_SINGLETON, K_MIXIN, K_VARIABLE, K_MEMBER
|
K_UNDEFINED = -1, K_CLASS, K_METHOD, K_MODULE, K_SINGLETON
|
||||||
} rubyKind;
|
} rubyKind;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DATA DEFINITIONS
|
||||||
|
*/
|
||||||
static kindOption RubyKinds [] = {
|
static kindOption RubyKinds [] = {
|
||||||
{ TRUE, 'c', "class", "classes" },
|
{ TRUE, 'c', "class", "classes" },
|
||||||
{ TRUE, 'f', "function", "methods" },
|
{ TRUE, 'f', "method", "methods" },
|
||||||
{ TRUE, 'm', "member", "singleton_methods" },
|
{ TRUE, 'm', "module", "modules" },
|
||||||
{ TRUE, 'd', "macro", "mixins" },
|
{ TRUE, 'F', "singleton method", "singleton methods" }
|
||||||
{ TRUE, 'v', "variable", "variable" },
|
};
|
||||||
{ TRUE, 's', "struct", "member" }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
static stringList* nesting = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FUNCTION DEFINITIONS
|
* FUNCTION DEFINITIONS
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void findRubyTags (void) {
|
/*
|
||||||
vString *name = vStringNew ();
|
* Returns a string describing the scope in 'list'.
|
||||||
const unsigned char *line;
|
* We record the current scope as a list of entered scopes.
|
||||||
boolean inMultilineString = FALSE;
|
* Scopes corresponding to 'if' statements and the like are
|
||||||
|
* represented by empty strings. Scopes corresponding to
|
||||||
|
* modules and classes are represented by the name of the
|
||||||
|
* module or class.
|
||||||
|
*/
|
||||||
|
static vString* stringListToScope (const stringList* list)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
unsigned int chunks_output = 0;
|
||||||
|
vString* result = vStringNew ();
|
||||||
|
const unsigned int max = stringListCount (list);
|
||||||
|
for (i = 0; i < max; ++i)
|
||||||
|
{
|
||||||
|
vString* chunk = stringListItem (list, i);
|
||||||
|
if (vStringLength (chunk) > 0)
|
||||||
|
{
|
||||||
|
vStringCatS (result, (chunks_output++ > 0) ? "." : "");
|
||||||
|
vStringCatS (result, vStringValue (chunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
while ((line = fileReadLine ()) != NULL) {
|
/*
|
||||||
const unsigned char *cp = line;
|
* Attempts to advance 's' past 'literal'.
|
||||||
boolean is_singleton = FALSE;
|
* Returns TRUE if it did, FALSE (and leaves 's' where
|
||||||
|
* it was) otherwise.
|
||||||
|
*/
|
||||||
|
static boolean canMatch (const unsigned char** s, const char* literal)
|
||||||
|
{
|
||||||
|
const int literal_length = strlen (literal);
|
||||||
|
const unsigned char next_char = *(*s + literal_length);
|
||||||
|
if (strncmp ((const char*) *s, literal, literal_length) != 0)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* Additionally check that we're at the end of a token. */
|
||||||
|
if ( ! (next_char == 0 || isspace (next_char) || next_char == '('))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
*s += literal_length;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
while (*cp != '\0')
|
/*
|
||||||
{
|
* Attempts to advance 'cp' past a Ruby operator method name. Returns
|
||||||
if (*cp=='"' &&
|
* TRUE if successful (and copies the name into 'name'), FALSE otherwise.
|
||||||
strncmp ((const char*) cp, "\"\"\"", (size_t) 3) == 0) {
|
*/
|
||||||
inMultilineString = (boolean) !inMultilineString;
|
static boolean parseRubyOperator (vString* name, const unsigned char** cp)
|
||||||
cp += 3;
|
{
|
||||||
}
|
static const char* RUBY_OPERATORS[] = {
|
||||||
/* make sure you include the comments */
|
"[]", "[]=",
|
||||||
if(*cp=='=' && strncmp((const char*)cp, "==begin", (size_t)7) == 0) {
|
"**",
|
||||||
inMultilineString = (boolean)!inMultilineString;
|
"!", "~", "+@", "-@",
|
||||||
cp +=3;
|
"*", "/", "%",
|
||||||
}
|
"+", "-",
|
||||||
/* mark the end of a comment */
|
">>", "<<",
|
||||||
if( *cp=='=' && strncmp((const char*)cp, "==end", (size_t)5) == 0) {
|
"&",
|
||||||
inMultilineString = (boolean)0;
|
"^", "|",
|
||||||
cp+=5;
|
"<=", "<", ">", ">=",
|
||||||
}
|
"<=>", "==", "===", "!=", "=~", "!~",
|
||||||
if (inMultilineString || isspace ((int) *cp))
|
"`",
|
||||||
++cp;
|
0
|
||||||
else if (*cp == '#')
|
};
|
||||||
break;
|
int i;
|
||||||
else if (*cp == '=' && *(cp+1) != '=' && (isspace((int) *(cp-1)) || isalnum((int) *(cp-1)))) {
|
for (i = 0; RUBY_OPERATORS[i] != 0; ++i)
|
||||||
|
{
|
||||||
|
if (canMatch (cp, RUBY_OPERATORS[i]))
|
||||||
|
{
|
||||||
|
vStringCatS (name, RUBY_OPERATORS[i]);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
// try to detect a variable by the = sign - enrico
|
/*
|
||||||
// exlude all what not look like ' = ' or 'l=b'
|
* Emits a tag for the given 'name' of kind 'kind' at the current nesting.
|
||||||
const unsigned char *cur_pos = cp; // store the current position because
|
*/
|
||||||
// we are going backwards with cp
|
static void emitRubyTag (vString* name, rubyKind kind)
|
||||||
// (think about what happens if we don't do this ;-))
|
{
|
||||||
int is_member = 0;
|
tagEntryInfo tag;
|
||||||
cp--;
|
vString* scope;
|
||||||
|
|
||||||
while (isspace ((int) *cp)) --cp;
|
vStringTerminate (name);
|
||||||
|
scope = stringListToScope (nesting);
|
||||||
|
|
||||||
while (isalnum ((int) *cp) || *cp == '_') cp--;
|
initTagEntry (&tag, vStringValue (name));
|
||||||
|
if (vStringLength (scope) > 0) {
|
||||||
|
tag.extensionFields.scope [0] = "class";
|
||||||
|
tag.extensionFields.scope [1] = vStringValue (scope);
|
||||||
|
}
|
||||||
|
tag.kindName = RubyKinds [kind].name;
|
||||||
|
tag.kind = RubyKinds [kind].letter;
|
||||||
|
makeTagEntry (&tag);
|
||||||
|
|
||||||
if (*cp == '@') is_member = 1; // handle @...
|
stringListAdd (nesting, vStringNewCopy (name));
|
||||||
else if (!isspace((int) *cp))
|
|
||||||
{
|
|
||||||
cp = cur_pos + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cp points to the char before the variable name, so go forward
|
vStringClear (name);
|
||||||
cp++;
|
vStringDelete (scope);
|
||||||
|
}
|
||||||
|
|
||||||
while (cp != cur_pos && ! isspace((int) *cp))
|
/* Tests whether 'ch' is a character in 'list'. */
|
||||||
{
|
static boolean charIsIn (char ch, const char* list)
|
||||||
vStringPut (name, (int) *cp);
|
{
|
||||||
cp++;
|
return (strchr (list, ch) != 0);
|
||||||
}
|
}
|
||||||
vStringTerminate (name);
|
|
||||||
if (vStringLength (name) > 0)
|
|
||||||
{
|
|
||||||
if (is_member) makeSimpleTag (name, RubyKinds, K_MEMBER);
|
|
||||||
else makeSimpleTag (name, RubyKinds, K_VARIABLE);
|
|
||||||
}
|
|
||||||
vStringClear (name);
|
|
||||||
cp = cur_pos + 1;
|
|
||||||
|
|
||||||
}
|
/* Advances 'cp' over leading whitespace. */
|
||||||
else if (strncmp ((const char*) cp, "module", (size_t) 6) == 0) {
|
static void skipWhitespace (const unsigned char** cp)
|
||||||
cp += 6;
|
{
|
||||||
if (isspace ((int) *cp)) {
|
while (isspace (**cp))
|
||||||
while (isspace ((int) *cp))
|
{
|
||||||
++cp;
|
++*cp;
|
||||||
while (isalnum ((int) *cp) || *cp == '_' || *cp == ':') {
|
}
|
||||||
vStringPut (name, (int) *cp);
|
}
|
||||||
++cp;
|
|
||||||
}
|
|
||||||
vStringTerminate (name);
|
|
||||||
makeSimpleTag (name, RubyKinds, K_MIXIN);
|
|
||||||
vStringClear (name);
|
|
||||||
}
|
|
||||||
} else if (strncmp ((const char*) cp, "class", (size_t) 5) == 0) {
|
|
||||||
cp += 5;
|
|
||||||
if (isspace ((int) *cp)) {
|
|
||||||
while (isspace ((int) *cp))
|
|
||||||
++cp;
|
|
||||||
while (isalnum ((int) *cp) || *cp == '_' || *cp == ':') {
|
|
||||||
vStringPut (name, (int) *cp);
|
|
||||||
++cp;
|
|
||||||
}
|
|
||||||
vStringTerminate (name);
|
|
||||||
makeSimpleTag (name, RubyKinds, K_CLASS);
|
|
||||||
vStringClear (name);
|
|
||||||
}
|
|
||||||
} else if (strncmp ((const char*) cp, "def", (size_t) 3) == 0) {
|
|
||||||
cp += 3;
|
|
||||||
if (isspace ((int) *cp)) {
|
|
||||||
while (isspace ((int) *cp))
|
|
||||||
++cp;
|
|
||||||
|
|
||||||
/* Put the valid characters allowed in a variable name
|
/*
|
||||||
* in here. In ruby a variable name ENDING in ! means
|
* Copies the characters forming an identifier from *cp into
|
||||||
* it changes the underlying data structure in place.
|
* name, leaving *cp pointing to the character after the identifier.
|
||||||
* A variable name ENDING in ? means that the function
|
*/
|
||||||
* returns a bool. Functions should not start with these
|
static rubyKind parseIdentifier (
|
||||||
* characters.
|
const unsigned char** cp, vString* name, rubyKind kind)
|
||||||
*/
|
{
|
||||||
while (isalnum ((int) *cp) || *cp == '_' || *cp == '!' || *cp =='?' || *cp=='.') {
|
/* Method names are slightly different to class and variable names.
|
||||||
/* classmethods are accesible only via class instance instead
|
* A method name may optionally end with a question mark, exclamation
|
||||||
* of object instance. This difference has to be outlined.
|
* point or equals sign. These are all part of the name.
|
||||||
*/
|
* A method name may also contain a period if it's a singleton method.
|
||||||
if (*cp == '.') {
|
*/
|
||||||
//class method
|
const char* also_ok = (kind == K_METHOD) ? "_.?!=" : "_";
|
||||||
is_singleton = TRUE;
|
|
||||||
vStringTerminate (name);
|
skipWhitespace (cp);
|
||||||
vStringClear(name);
|
|
||||||
} else {
|
/* Check for an anonymous (singleton) class such as "class << HTTP". */
|
||||||
vStringPut (name, (int) *cp);
|
if (kind == K_CLASS && **cp == '<' && *(*cp + 1) == '<')
|
||||||
}
|
{
|
||||||
++cp;
|
return K_UNDEFINED;
|
||||||
}
|
}
|
||||||
vStringTerminate (name);
|
|
||||||
if (is_singleton) {
|
/* Check for operators such as "def []=(key, val)". */
|
||||||
makeSimpleTag (name, RubyKinds, K_SINGLETON);
|
if (kind == K_METHOD || kind == K_SINGLETON)
|
||||||
} else {
|
{
|
||||||
makeSimpleTag (name, RubyKinds, K_METHOD);
|
if (parseRubyOperator (name, cp))
|
||||||
}
|
{
|
||||||
vStringClear (name);
|
return kind;
|
||||||
}
|
}
|
||||||
} else if (*cp != '\0') {
|
}
|
||||||
do
|
|
||||||
++cp;
|
/* Copy the identifier into 'name'. */
|
||||||
while (isalnum ((int) *cp) || *cp == '_');
|
while (**cp != 0 && (isalnum (**cp) || charIsIn (**cp, also_ok)))
|
||||||
}
|
{
|
||||||
}
|
char last_char = **cp;
|
||||||
}
|
|
||||||
vStringDelete (name);
|
vStringPut (name, last_char);
|
||||||
|
++*cp;
|
||||||
|
|
||||||
|
if (kind == K_METHOD)
|
||||||
|
{
|
||||||
|
/* Recognize singleton methods. */
|
||||||
|
if (last_char == '.')
|
||||||
|
{
|
||||||
|
vStringTerminate (name);
|
||||||
|
vStringClear (name);
|
||||||
|
return parseIdentifier (cp, name, K_SINGLETON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Recognize characters which mark the end of a method name. */
|
||||||
|
if (charIsIn (last_char, "?!="))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
|
||||||
|
{
|
||||||
|
if (isspace (**cp))
|
||||||
|
{
|
||||||
|
vString *name = vStringNew ();
|
||||||
|
rubyKind actual_kind = parseIdentifier (cp, name, expected_kind);
|
||||||
|
|
||||||
|
if (actual_kind == K_UNDEFINED || vStringLength (name) == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* What kind of tags should we create for code like this?
|
||||||
|
*
|
||||||
|
* %w(self.clfloor clfloor).each do |name|
|
||||||
|
* module_eval <<-"end;"
|
||||||
|
* def #{name}(x, y=1)
|
||||||
|
* q, r = x.divmod(y)
|
||||||
|
* q = q.to_i
|
||||||
|
* return q, r
|
||||||
|
* end
|
||||||
|
* end;
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* Or this?
|
||||||
|
*
|
||||||
|
* class << HTTP
|
||||||
|
*
|
||||||
|
* For now, we don't create any.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emitRubyTag (name, actual_kind);
|
||||||
|
}
|
||||||
|
vStringDelete (name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enterUnnamedScope (void)
|
||||||
|
{
|
||||||
|
stringListAdd (nesting, vStringNewInit (""));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void findRubyTags (void)
|
||||||
|
{
|
||||||
|
const unsigned char *line;
|
||||||
|
boolean inMultiLineComment = FALSE;
|
||||||
|
|
||||||
|
nesting = stringListNew ();
|
||||||
|
|
||||||
|
/* FIXME: this whole scheme is wrong, because Ruby isn't line-based.
|
||||||
|
* You could perfectly well write:
|
||||||
|
*
|
||||||
|
* def
|
||||||
|
* method
|
||||||
|
* puts("hello")
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* if you wished, and this function would fail to recognize anything.
|
||||||
|
*/
|
||||||
|
while ((line = fileReadLine ()) != NULL)
|
||||||
|
{
|
||||||
|
const unsigned char *cp = line;
|
||||||
|
|
||||||
|
if (canMatch (&cp, "=begin"))
|
||||||
|
{
|
||||||
|
inMultiLineComment = TRUE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (canMatch (&cp, "=end"))
|
||||||
|
{
|
||||||
|
inMultiLineComment = FALSE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
skipWhitespace (&cp);
|
||||||
|
|
||||||
|
/* Avoid mistakenly starting a scope for modifiers such as
|
||||||
|
*
|
||||||
|
* return if <exp>
|
||||||
|
*
|
||||||
|
* FIXME: this is fooled by code such as
|
||||||
|
*
|
||||||
|
* result = if <exp>
|
||||||
|
* <a>
|
||||||
|
* else
|
||||||
|
* <b>
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* FIXME: we're also fooled if someone does something heinous such as
|
||||||
|
*
|
||||||
|
* puts("hello") \
|
||||||
|
* unless <exp>
|
||||||
|
*/
|
||||||
|
if (canMatch (&cp, "case") || canMatch (&cp, "for") ||
|
||||||
|
canMatch (&cp, "if") || canMatch (&cp, "unless") ||
|
||||||
|
canMatch (&cp, "while"))
|
||||||
|
{
|
||||||
|
enterUnnamedScope ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* "module M", "class C" and "def m" should only be at the beginning
|
||||||
|
* of a line.
|
||||||
|
*/
|
||||||
|
if (canMatch (&cp, "module"))
|
||||||
|
{
|
||||||
|
readAndEmitTag (&cp, K_MODULE);
|
||||||
|
}
|
||||||
|
else if (canMatch (&cp, "class"))
|
||||||
|
{
|
||||||
|
readAndEmitTag (&cp, K_CLASS);
|
||||||
|
}
|
||||||
|
else if (canMatch (&cp, "def"))
|
||||||
|
{
|
||||||
|
readAndEmitTag (&cp, K_METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*cp != '\0')
|
||||||
|
{
|
||||||
|
/* FIXME: we don't cope with here documents, or string literals,
|
||||||
|
* or regular expression literals, or ... you get the idea.
|
||||||
|
* Hopefully, the restriction above that insists on seeing
|
||||||
|
* definitions at the starts of lines should keep us out of
|
||||||
|
* mischief.
|
||||||
|
*/
|
||||||
|
if (inMultiLineComment || isspace (*cp))
|
||||||
|
{
|
||||||
|
++cp;
|
||||||
|
}
|
||||||
|
else if (*cp == '#')
|
||||||
|
{
|
||||||
|
/* FIXME: this is wrong, but there *probably* won't be a
|
||||||
|
* definition after an interpolated string (where # doesn't
|
||||||
|
* mean 'comment').
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (canMatch (&cp, "begin") || canMatch (&cp, "do"))
|
||||||
|
{
|
||||||
|
enterUnnamedScope ();
|
||||||
|
}
|
||||||
|
else if (canMatch (&cp, "end") && stringListCount (nesting) > 0)
|
||||||
|
{
|
||||||
|
/* Leave the most recent scope. */
|
||||||
|
vStringDelete (stringListLast (nesting));
|
||||||
|
stringListRemoveLast (nesting);
|
||||||
|
}
|
||||||
|
else if (*cp != '\0')
|
||||||
|
{
|
||||||
|
do
|
||||||
|
++cp;
|
||||||
|
while (isalnum (*cp) || *cp == '_');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stringListDelete (nesting);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern parserDefinition* RubyParser (void)
|
extern parserDefinition* RubyParser (void)
|
||||||
{
|
{
|
||||||
static const char *const extensions [] = { "rb", "rhtml", NULL };
|
static const char *const extensions [] = { "rb", "ruby", "rhtml", NULL };
|
||||||
parserDefinition* def = parserNew ("Ruby");
|
parserDefinition* def = parserNew ("Ruby");
|
||||||
def->kinds = RubyKinds;
|
def->kinds = RubyKinds;
|
||||||
def->kindCount = KIND_COUNT (RubyKinds);
|
def->kindCount = KIND_COUNT (RubyKinds);
|
||||||
def->extensions = extensions;
|
def->extensions = extensions;
|
||||||
def->parser = findRubyTags;
|
def->parser = findRubyTags;
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* vi:set tabstop=4 shiftwidth=4: */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
* $Id$
|
||||||
*
|
*
|
||||||
* Copyright (c) 1999-2001, Darren Hiebert
|
* Copyright (c) 1999-2002, Darren Hiebert
|
||||||
*
|
*
|
||||||
* This source code is released for free distribution under the terms of the
|
* This source code is released for free distribution under the terms of the
|
||||||
* GNU General Public License.
|
* GNU General Public License.
|
||||||
@ -11,14 +12,13 @@
|
|||||||
/*
|
/*
|
||||||
* INCLUDE FILES
|
* INCLUDE FILES
|
||||||
*/
|
*/
|
||||||
#include "general.h" /* must always come first */
|
#include "general.h" /* must always come first */
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#ifdef HAVE_FNMATCH_H
|
#ifdef HAVE_FNMATCH_H
|
||||||
# include <fnmatch.h>
|
# include <fnmatch.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "read.h"
|
#include "read.h"
|
||||||
#include "strlist.h"
|
#include "strlist.h"
|
||||||
@ -29,192 +29,252 @@
|
|||||||
|
|
||||||
extern stringList *stringListNew (void)
|
extern stringList *stringListNew (void)
|
||||||
{
|
{
|
||||||
stringList* const result = xMalloc (1, stringList);
|
stringList* const result = xMalloc (1, stringList);
|
||||||
result->max = 0;
|
result->max = 0;
|
||||||
result->count = 0;
|
result->count = 0;
|
||||||
result->list = NULL;
|
result->list = NULL;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void stringListAdd (stringList *const current, vString *string)
|
extern void stringListAdd (stringList *const current, vString *string)
|
||||||
{
|
{
|
||||||
enum { incrementalIncrease = 10 };
|
enum { incrementalIncrease = 10 };
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
if (current->list == NULL)
|
if (current->list == NULL)
|
||||||
{
|
{
|
||||||
Assert (current->max == 0);
|
Assert (current->max == 0);
|
||||||
current->count = 0;
|
current->count = 0;
|
||||||
current->max = incrementalIncrease;
|
current->max = incrementalIncrease;
|
||||||
current->list = xMalloc (current->max, vString*);
|
current->list = xMalloc (current->max, vString*);
|
||||||
}
|
}
|
||||||
else if (current->count == current->max)
|
else if (current->count == current->max)
|
||||||
{
|
{
|
||||||
current->max += incrementalIncrease;
|
current->max += incrementalIncrease;
|
||||||
current->list = xRealloc (current->list, current->max, vString*);
|
current->list = xRealloc (current->list, current->max, vString*);
|
||||||
}
|
}
|
||||||
current->list [current->count++] = string;
|
current->list [current->count++] = string;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void stringListRemoveLast (stringList *const current)
|
||||||
|
{
|
||||||
|
Assert (current != NULL);
|
||||||
|
Assert (current->count > 0);
|
||||||
|
--current->count;
|
||||||
|
current->list [current->count] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Combine list `from' into `current', deleting `from' */
|
/* Combine list `from' into `current', deleting `from' */
|
||||||
extern void stringListCombine (stringList *const current, stringList *const from)
|
extern void stringListCombine (
|
||||||
|
stringList *const current, stringList *const from)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
Assert (from != NULL);
|
Assert (from != NULL);
|
||||||
for (i = 0 ; i < from->count ; ++i)
|
for (i = 0 ; i < from->count ; ++i)
|
||||||
{
|
{
|
||||||
stringListAdd (current, from->list [i]);
|
stringListAdd (current, from->list [i]);
|
||||||
from->list [i] = NULL;
|
from->list [i] = NULL;
|
||||||
}
|
}
|
||||||
stringListDelete (from);
|
stringListDelete (from);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern stringList* stringListNewFromArgv (const char* const* const argv)
|
extern stringList* stringListNewFromArgv (const char* const* const argv)
|
||||||
{
|
{
|
||||||
stringList* const result = stringListNew ();
|
stringList* const result = stringListNew ();
|
||||||
const char *const *p;
|
const char *const *p;
|
||||||
Assert (argv != NULL);
|
Assert (argv != NULL);
|
||||||
for (p = argv ; *p != NULL ; ++p)
|
for (p = argv ; *p != NULL ; ++p)
|
||||||
stringListAdd (result, vStringNewInit (*p));
|
stringListAdd (result, vStringNewInit (*p));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern stringList* stringListNewFromFile (const char* const fileName)
|
extern stringList* stringListNewFromFile (const char* const fileName)
|
||||||
{
|
{
|
||||||
stringList* result = NULL;
|
stringList* result = NULL;
|
||||||
FILE* const fp = fopen (fileName, "r");
|
FILE* const fp = fopen (fileName, "r");
|
||||||
if (fp == NULL)
|
if (fp != NULL)
|
||||||
error (FATAL | PERROR, "cannot open \"%s\"", fileName);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = stringListNew ();
|
|
||||||
while (! feof (fp))
|
|
||||||
{
|
{
|
||||||
vString* const str = vStringNew ();
|
result = stringListNew ();
|
||||||
readLine (str, fp);
|
while (! feof (fp))
|
||||||
vStringStripTrailing (str);
|
{
|
||||||
if (vStringLength (str) > 0)
|
vString* const str = vStringNew ();
|
||||||
stringListAdd (result, str);
|
readLine (str, fp);
|
||||||
else
|
vStringStripTrailing (str);
|
||||||
vStringDelete (str);
|
if (vStringLength (str) > 0)
|
||||||
|
stringListAdd (result, str);
|
||||||
|
else
|
||||||
|
vStringDelete (str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
return result;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern unsigned int stringListCount (const stringList *const current)
|
extern unsigned int stringListCount (const stringList *const current)
|
||||||
{
|
{
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
return current->count;
|
return current->count;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern vString* stringListItem (const stringList *const current,
|
extern vString* stringListItem (
|
||||||
const unsigned int indx)
|
const stringList *const current, const unsigned int indx)
|
||||||
{
|
{
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
return current->list [indx];
|
return current->list [indx];
|
||||||
|
}
|
||||||
|
|
||||||
|
extern vString* stringListLast (const stringList *const current)
|
||||||
|
{
|
||||||
|
Assert (current != NULL);
|
||||||
|
Assert (current->count > 0);
|
||||||
|
return current->list [current->count - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void stringListClear (stringList *const current)
|
extern void stringListClear (stringList *const current)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
for (i = 0 ; i < current->count ; ++i)
|
for (i = 0 ; i < current->count ; ++i)
|
||||||
{
|
{
|
||||||
vStringDelete (current->list [i]);
|
vStringDelete (current->list [i]);
|
||||||
current->list [i] = NULL;
|
current->list [i] = NULL;
|
||||||
}
|
}
|
||||||
current->count = 0;
|
current->count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void stringListDelete (stringList *const current)
|
extern void stringListDelete (stringList *const current)
|
||||||
{
|
{
|
||||||
if (current != NULL)
|
if (current != NULL)
|
||||||
{
|
|
||||||
if (current->list != NULL)
|
|
||||||
{
|
{
|
||||||
stringListClear (current);
|
if (current->list != NULL)
|
||||||
eFree (current->list);
|
{
|
||||||
current->list = NULL;
|
stringListClear (current);
|
||||||
|
eFree (current->list);
|
||||||
|
current->list = NULL;
|
||||||
|
}
|
||||||
|
current->max = 0;
|
||||||
|
current->count = 0;
|
||||||
|
eFree (current);
|
||||||
}
|
}
|
||||||
current->max = 0;
|
|
||||||
current->count = 0;
|
|
||||||
eFree (current);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boolean stringListHas (const stringList *const current,
|
static boolean compareString (
|
||||||
const char *const str)
|
const char *const string, vString *const itm)
|
||||||
{
|
{
|
||||||
boolean result = FALSE;
|
return (boolean) (strcmp (string, vStringValue (itm)) == 0);
|
||||||
unsigned int i;
|
|
||||||
Assert (current != NULL);
|
|
||||||
for (i = 0 ; ! result && i < current->count ; ++i)
|
|
||||||
result = (boolean) (strcmp (str, vStringValue (current->list [i]))==0);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boolean stringListHasInsensitive (const stringList *const current,
|
static boolean compareStringInsensitive (
|
||||||
const char *const str)
|
const char *const string, vString *const itm)
|
||||||
{
|
{
|
||||||
boolean result = FALSE;
|
return (boolean) (strcasecmp (string, vStringValue (itm)) == 0);
|
||||||
unsigned int i;
|
|
||||||
Assert (current != NULL);
|
|
||||||
for (i = 0 ; ! result && i < current->count ; ++i)
|
|
||||||
result = (boolean) (stricmp (str, vStringValue (current->list [i]))==0);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boolean stringListHasFile (const stringList *const current,
|
static int stringListIndex (
|
||||||
const char *const file)
|
const stringList *const current,
|
||||||
|
const char *const string,
|
||||||
|
boolean (*test)(const char *s, vString *const vs))
|
||||||
{
|
{
|
||||||
boolean result = FALSE;
|
int result = -1;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
for (i = 0 ; ! result && i < current->count ; ++i)
|
Assert (string != NULL);
|
||||||
result = (boolean) (isSameFile (file, vStringValue (current->list [i])));
|
Assert (test != NULL);
|
||||||
return result;
|
for (i = 0 ; result == -1 && i < current->count ; ++i)
|
||||||
|
if ((*test)(string, current->list [i]))
|
||||||
|
result = i;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boolean stringListExtensionMatched (const stringList* const list,
|
extern boolean stringListHas (
|
||||||
const char* const extension)
|
const stringList *const current, const char *const string)
|
||||||
|
{
|
||||||
|
boolean result = FALSE;
|
||||||
|
Assert (current != NULL);
|
||||||
|
result = stringListIndex (current, string, compareString) != -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern boolean stringListHasInsensitive (
|
||||||
|
const stringList *const current, const char *const string)
|
||||||
|
{
|
||||||
|
boolean result = FALSE;
|
||||||
|
Assert (current != NULL);
|
||||||
|
Assert (string != NULL);
|
||||||
|
result = stringListIndex (current, string, compareStringInsensitive) != -1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern boolean stringListHasTest (
|
||||||
|
const stringList *const current, boolean (*test)(const char *s))
|
||||||
|
{
|
||||||
|
boolean result = FALSE;
|
||||||
|
unsigned int i;
|
||||||
|
Assert (current != NULL);
|
||||||
|
for (i = 0 ; ! result && i < current->count ; ++i)
|
||||||
|
result = (*test)(vStringValue (current->list [i]));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern boolean stringListRemoveExtension (
|
||||||
|
stringList* const current, const char* const extension)
|
||||||
|
{
|
||||||
|
boolean result = FALSE;
|
||||||
|
int where;
|
||||||
|
#ifdef CASE_INSENSITIVE_FILENAMES
|
||||||
|
where = stringListIndex (current, extension, compareStringInsensitive);
|
||||||
|
#else
|
||||||
|
where = stringListIndex (current, extension, compareString);
|
||||||
|
#endif
|
||||||
|
if (where != -1)
|
||||||
|
{
|
||||||
|
memmove (current->list + where, current->list + where + 1,
|
||||||
|
(current->count - where) * sizeof (*current->list));
|
||||||
|
current->list [current->count - 1] = NULL;
|
||||||
|
--current->count;
|
||||||
|
result = TRUE;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern boolean stringListExtensionMatched (
|
||||||
|
const stringList* const current, const char* const extension)
|
||||||
{
|
{
|
||||||
#ifdef CASE_INSENSITIVE_FILENAMES
|
#ifdef CASE_INSENSITIVE_FILENAMES
|
||||||
return stringListHasInsensitive (list, extension);
|
return stringListHasInsensitive (current, extension);
|
||||||
#else
|
#else
|
||||||
return stringListHas (list, extension);
|
return stringListHas (current, extension);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean fileNameMatched (const vString* const vpattern,
|
static boolean fileNameMatched (
|
||||||
const char* const fileName)
|
const vString* const vpattern, const char* const fileName)
|
||||||
{
|
{
|
||||||
const char* const pattern = vStringValue (vpattern);
|
const char* const pattern = vStringValue (vpattern);
|
||||||
#if defined (HAVE_FNMATCH)
|
#if defined (HAVE_FNMATCH)
|
||||||
return (boolean) (fnmatch (pattern, fileName, 0) == 0);
|
return (boolean) (fnmatch (pattern, fileName, 0) == 0);
|
||||||
#elif defined (CASE_INSENSITIVE_FILENAMES)
|
#elif defined (CASE_INSENSITIVE_FILENAMES)
|
||||||
return (boolean) (stricmp (pattern, fileName) == 0);
|
return (boolean) (strcasecmp (pattern, fileName) == 0);
|
||||||
#else
|
#else
|
||||||
return (boolean) (strcmp (pattern, fileName) == 0);
|
return (boolean) (strcmp (pattern, fileName) == 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extern boolean stringListFileMatched (const stringList* const list,
|
extern boolean stringListFileMatched (
|
||||||
const char* const fileName)
|
const stringList* const current, const char* const fileName)
|
||||||
{
|
{
|
||||||
boolean result = FALSE;
|
boolean result = FALSE;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
for (i = 0 ; ! result && i < stringListCount (list) ; ++i)
|
for (i = 0 ; ! result && i < stringListCount (current) ; ++i)
|
||||||
result = fileNameMatched (stringListItem (list, i), fileName);
|
result = fileNameMatched (stringListItem (current, i), fileName);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void stringListPrint (const stringList *const current)
|
extern void stringListPrint (const stringList *const current)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
Assert (current != NULL);
|
Assert (current != NULL);
|
||||||
for (i = 0 ; i < current->count ; ++i)
|
for (i = 0 ; i < current->count ; ++i)
|
||||||
printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i]));
|
printf ("%s%s", (i > 0) ? ", " : "", vStringValue (current->list [i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* vi:set tabstop=8 shiftwidth=4: */
|
/* vi:set tabstop=4 shiftwidth=4: */
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
|
* $Id$
|
||||||
*
|
*
|
||||||
* Copyright (c) 1999-2001, Darren Hiebert
|
* Copyright (c) 1999-2002, Darren Hiebert
|
||||||
*
|
*
|
||||||
* This source code is released for free distribution under the terms of the
|
* This source code is released for free distribution under the terms of the
|
||||||
* GNU General Public License.
|
* GNU General Public License.
|
||||||
@ -13,7 +14,7 @@
|
|||||||
/*
|
/*
|
||||||
* INCLUDE FILES
|
* INCLUDE FILES
|
||||||
*/
|
*/
|
||||||
#include "general.h" /* must always come first */
|
#include "general.h" /* must always come first */
|
||||||
|
|
||||||
#include "vstring.h"
|
#include "vstring.h"
|
||||||
|
|
||||||
@ -21,9 +22,9 @@
|
|||||||
* DATA DECLARATIONS
|
* DATA DECLARATIONS
|
||||||
*/
|
*/
|
||||||
typedef struct sStringList {
|
typedef struct sStringList {
|
||||||
unsigned int max;
|
unsigned int max;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
vString **list;
|
vString **list;
|
||||||
} stringList;
|
} stringList;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -31,20 +32,23 @@ typedef struct sStringList {
|
|||||||
*/
|
*/
|
||||||
extern stringList *stringListNew (void);
|
extern stringList *stringListNew (void);
|
||||||
extern void stringListAdd (stringList *const current, vString *string);
|
extern void stringListAdd (stringList *const current, vString *string);
|
||||||
|
extern void stringListRemoveLast (stringList *const current);
|
||||||
extern void stringListCombine (stringList *const current, stringList *const from);
|
extern void stringListCombine (stringList *const current, stringList *const from);
|
||||||
extern stringList* stringListNewFromArgv (const char* const* const list);
|
extern stringList* stringListNewFromArgv (const char* const* const list);
|
||||||
extern stringList* stringListNewFromFile (const char* const fileName);
|
extern stringList* stringListNewFromFile (const char* const fileName);
|
||||||
extern void stringListClear (stringList *const current);
|
extern void stringListClear (stringList *const current);
|
||||||
extern unsigned int stringListCount (const stringList *const current);
|
extern unsigned int stringListCount (const stringList *const current);
|
||||||
extern vString* stringListItem (const stringList *const current, const unsigned int indx);
|
extern vString* stringListItem (const stringList *const current, const unsigned int indx);
|
||||||
|
extern vString* stringListLast (const stringList *const current);
|
||||||
extern void stringListDelete (stringList *const current);
|
extern void stringListDelete (stringList *const current);
|
||||||
extern boolean stringListHasInsensitive (const stringList *const current, const char *const string);
|
extern boolean stringListHasInsensitive (const stringList *const current, const char *const string);
|
||||||
extern boolean stringListHas (const stringList *const current, const char *const string);
|
extern boolean stringListHas (const stringList *const current, const char *const string);
|
||||||
extern boolean stringListHasFile (const stringList *const current, const char *const file);
|
extern boolean stringListHasTest (const stringList *const current, boolean (*test)(const char *s));
|
||||||
|
extern boolean stringListRemoveExtension (stringList* const current, const char* const extension);
|
||||||
extern boolean stringListExtensionMatched (const stringList* const list, const char* const extension);
|
extern boolean stringListExtensionMatched (const stringList* const list, const char* const extension);
|
||||||
extern boolean stringListFileMatched (const stringList* const list, const char* const str);
|
extern boolean stringListFileMatched (const stringList* const list, const char* const str);
|
||||||
extern void stringListPrint (const stringList *const current);
|
extern void stringListPrint (const stringList *const current);
|
||||||
|
|
||||||
#endif /* _STRLIST_H */
|
#endif /* _STRLIST_H */
|
||||||
|
|
||||||
/* vi:set tabstop=8 shiftwidth=4: */
|
/* vi:set tabstop=4 shiftwidth=4: */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user