[anjuta] symbol-db: anjuta-tags updated to ctags-5.8 version.
- From: Massimo Cora' <mcora src gnome org>
- To: svn-commits-list gnome org
- Subject: [anjuta] symbol-db: anjuta-tags updated to ctags-5.8 version.
- Date: Sun, 12 Jul 2009 12:38:49 +0000 (UTC)
commit df8cfa55a04adbbe4249cc6ded6ca64485891da2
Author: Massimo Corà <mcora src gnome org>
Date: Sun Jul 12 14:34:13 2009 +0200
symbol-db: anjuta-tags updated to ctags-5.8 version.
Updated some files and added ocaml.c.
plugins/symbol-db/anjuta-tags/Makefile.am | 1 +
plugins/symbol-db/anjuta-tags/ant.c | 84 +-
plugins/symbol-db/anjuta-tags/asp.c | 123 +-
plugins/symbol-db/anjuta-tags/dosbatch.c | 6 +-
plugins/symbol-db/anjuta-tags/flex.c | 4487 ++++++++++++++---------------
plugins/symbol-db/anjuta-tags/keyword.c | 5 +-
plugins/symbol-db/anjuta-tags/lisp.c | 4 +-
plugins/symbol-db/anjuta-tags/matlab.c | 6 +-
plugins/symbol-db/anjuta-tags/ocaml.c | 1842 ++++++++++++
plugins/symbol-db/anjuta-tags/parsers.h | 3 +-
plugins/symbol-db/anjuta-tags/php.c | 22 +-
plugins/symbol-db/anjuta-tags/python.c | 245 ++-
plugins/symbol-db/anjuta-tags/read.c | 4 +-
plugins/symbol-db/anjuta-tags/tex.c | 4 +-
plugins/symbol-db/anjuta-tags/vstring.h | 3 +-
plugins/symbol-db/symbol-db-engine-core.c | 2 +-
16 files changed, 4457 insertions(+), 2384 deletions(-)
---
diff --git a/plugins/symbol-db/anjuta-tags/Makefile.am b/plugins/symbol-db/anjuta-tags/Makefile.am
index eea7d23..62ec5a2 100644
--- a/plugins/symbol-db/anjuta-tags/Makefile.am
+++ b/plugins/symbol-db/anjuta-tags/Makefile.am
@@ -58,6 +58,7 @@ anjuta_tags_SOURCES = \
make.c \
Makefile.am \
matlab.c \
+ ocaml.c \
options.c \
options.h \
parse.c \
diff --git a/plugins/symbol-db/anjuta-tags/ant.c b/plugins/symbol-db/anjuta-tags/ant.c
index e729276..eedfcec 100644
--- a/plugins/symbol-db/anjuta-tags/ant.c
+++ b/plugins/symbol-db/anjuta-tags/ant.c
@@ -1,42 +1,42 @@
-/*
-* $Id: yacc.c 443 2006-05-30 04:37:13Z darren $
-*
-* Copyright (c) 2001-2002, Nick Hibma <n_hibma van-laarhoven org>
-*
-* This source code is released for free distribution under the terms of the
-* GNU General Public License.
-*
-* This module contains functions for generating tags for YACC language files.
-*/
-
-/*
-* INCLUDE FILES
-*/
-#include "general.h" /* must always come first */
-
-#include <string.h>
-#include "parse.h"
-
-/*
-* FUNCTION DEFINITIONS
-*/
-
-static void installAntRegex (const langType language)
-{
- addTagRegex (language,
- "^[ \t]*<[ \t]*project.*name=\"([^\"]+)\".*", "\\1", "p,project,projects", NULL);
- addTagRegex (language,
- "^[ \t]*<[ \t]*target.*name=\"([^\"]+)\".*", "\\1", "t,target,targets", NULL);
-}
-
-extern parserDefinition* AntParser ()
-{
- static const char *const extensions [] = { "build.xml", NULL };
- parserDefinition* const def = parserNew ("Ant");
- def->extensions = extensions;
- def->initialize = installAntRegex;
- def->regex = TRUE;
- return def;
-}
-
-/* vi:set tabstop=4 shiftwidth=4: */
+/*
+* $Id$
+*
+* Copyright (c) 2008, David Fishburn
+*
+* This source code is released for free distribution under the terms of the
+* GNU General Public License.
+*
+* This module contains functions for generating tags for Ant language files.
+*/
+
+/*
+* INCLUDE FILES
+*/
+#include "general.h" /* must always come first */
+
+#include <string.h>
+#include "parse.h"
+
+/*
+* FUNCTION DEFINITIONS
+*/
+
+static void installAntRegex (const langType language)
+{
+ addTagRegex (language,
+ "^[ \t]*<[ \t]*project.*name=\"([^\"]+)\".*", "\\1", "p,project,projects", NULL);
+ addTagRegex (language,
+ "^[ \t]*<[ \t]*target.*name=\"([^\"]+)\".*", "\\1", "t,target,targets", NULL);
+}
+
+extern parserDefinition* AntParser ()
+{
+ static const char *const extensions [] = { "build.xml", NULL };
+ parserDefinition* const def = parserNew ("Ant");
+ def->extensions = extensions;
+ def->initialize = installAntRegex;
+ def->regex = TRUE;
+ return def;
+}
+
+/* vi:set tabstop=4 shiftwidth=4: */
diff --git a/plugins/symbol-db/anjuta-tags/asp.c b/plugins/symbol-db/anjuta-tags/asp.c
index 27e8474..7290ad8 100644
--- a/plugins/symbol-db/anjuta-tags/asp.c
+++ b/plugins/symbol-db/anjuta-tags/asp.c
@@ -1,5 +1,5 @@
/*
-* $Id: asp.c 443 2006-05-30 04:37:13Z darren $
+* $Id: asp.c 711 2009-07-04 16:52:11Z dhiebert $
*
* Copyright (c) 2000, Patrick Dehne <patrick steidle net>
*
@@ -25,11 +25,12 @@
* DATA DEFINITIONS
*/
typedef enum {
- K_CONST, K_FUNCTION, K_SUB, K_DIM
+ K_CONST, K_CLASS, K_FUNCTION, K_SUB, K_DIM
} aspKind;
static kindOption AspKinds [] = {
- { TRUE, 'c', "constant", "constants"},
+ { TRUE, 'd', "constant", "constants"},
+ { TRUE, 'c', "class", "classes"},
{ TRUE, 'f', "function", "functions"},
{ TRUE, 's', "subroutine", "subroutines"},
{ TRUE, 'v', "variable", "variables"}
@@ -112,6 +113,102 @@ static void findAspTags (void)
}
}
+ /* class member? */
+ else if (strncasecmp ((const char*) cp, "public", (size_t) 6) == 0)
+ {
+ cp += 6;
+ if (isspace ((int) *cp))
+ {
+ while (isspace ((int) *cp))
+ ++cp;
+ if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
+ {
+ cp+=8;
+ while (isspace ((int) *cp))
+ ++cp;
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_FUNCTION);
+ vStringClear (name);
+ }
+ else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
+ {
+ cp+=3;
+ while (isspace ((int) *cp))
+ ++cp;
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_SUB);
+ vStringClear (name);
+ }
+ else {
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_DIM);
+ vStringClear (name);
+ }
+ }
+ }
+ else if (strncasecmp ((const char*) cp, "private", (size_t) 7) == 0)
+ {
+ cp += 7;
+ if (isspace ((int) *cp))
+ {
+ while (isspace ((int) *cp))
+ ++cp;
+ if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
+ {
+ cp+=8;
+ while (isspace ((int) *cp))
+ ++cp;
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_FUNCTION);
+ vStringClear (name);
+ }
+ else if (strncasecmp ((const char*) cp, "sub", (size_t) 3) == 0)
+ {
+ cp+=3;
+ while (isspace ((int) *cp))
+ ++cp;
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_SUB);
+ vStringClear (name);
+ }
+ else {
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_DIM);
+ vStringClear (name);
+ }
+ }
+ }
+
/* function? */
else if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0)
{
@@ -170,6 +267,25 @@ static void findAspTags (void)
}
}
+ /* class declaration? */
+ else if (strncasecmp ((const char*) cp, "class", (size_t) 5) == 0)
+ {
+ cp += 5;
+ if (isspace ((int) *cp))
+ {
+ while (isspace ((int) *cp))
+ ++cp;
+ while (isalnum ((int) *cp) || *cp == '_')
+ {
+ vStringPut (name, (int) *cp);
+ ++cp;
+ }
+ vStringTerminate (name);
+ makeSimpleTag (name, AspKinds, K_CLASS);
+ vStringClear (name);
+ }
+ }
+
/* const declaration? */
else if (strncasecmp ((const char*) cp, "const", (size_t) 5) == 0)
{
@@ -209,3 +325,4 @@ extern parserDefinition* AspParser (void)
}
/* vi:set tabstop=4 shiftwidth=4: */
+
diff --git a/plugins/symbol-db/anjuta-tags/dosbatch.c b/plugins/symbol-db/anjuta-tags/dosbatch.c
index f137657..c165183 100644
--- a/plugins/symbol-db/anjuta-tags/dosbatch.c
+++ b/plugins/symbol-db/anjuta-tags/dosbatch.c
@@ -1,12 +1,12 @@
/*
-* $Id: yacc.c 443 2006-05-30 04:37:13Z darren $
+* $Id$
*
-* Copyright (c) 2001-2002, Nick Hibma <n_hibma van-laarhoven org>
+* Copyright (c) 2009, David Fishburn
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
-* This module contains functions for generating tags for YACC language files.
+* This module contains functions for generating tags for DOS Batch language files.
*/
/*
diff --git a/plugins/symbol-db/anjuta-tags/flex.c b/plugins/symbol-db/anjuta-tags/flex.c
index 7fb6977..06ca243 100644
--- a/plugins/symbol-db/anjuta-tags/flex.c
+++ b/plugins/symbol-db/anjuta-tags/flex.c
@@ -1,2244 +1,2243 @@
-/*
- * $Id: flex.c 666 2008-05-15 17:47:31Z dfishburn $
- *
- * Copyright (c) 2003, Darren Hiebert
- *
- * This source code is released for free distribution under the terms of the
- * GNU General Public License.
- *
- * This module contains functions for generating tags for Adobe languages.
- * There are a number of different ones, but this will begin with:
- * Flex
- * MXML files (*.mMacromedia XML)
- * ActionScript files (*.as)
- *
- *
- * Flex 3 language reference
- * http://livedocs.adobe.com/flex/3/langref/index.html
- */
-
-/*
- * INCLUDE FILES
- */
-#include "general.h" /* must always come first */
-#include <ctype.h> /* to define isalpha () */
-#include <setjmp.h>
-#ifdef DEBUG
-#include <stdio.h>
-#endif
-
-#include "debug.h"
-#include "entry.h"
-#include "keyword.h"
-#include "parse.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-
-/*
- * MACROS
- */
-#define isType(token,t) (boolean) ((token)->type == (t))
-#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
-
-/*
- * DATA DECLARATIONS
- */
-
-typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
-
-/*
- * Tracks class and function names already created
- */
-static stringList *ClassNames;
-static stringList *FunctionNames;
-
-/* Used to specify type of keyword.
-*/
-typedef enum eKeywordId {
- KEYWORD_NONE = -1,
- KEYWORD_function,
- KEYWORD_capital_function,
- KEYWORD_object,
- KEYWORD_capital_object,
- KEYWORD_prototype,
- KEYWORD_var,
- KEYWORD_new,
- KEYWORD_this,
- KEYWORD_for,
- KEYWORD_while,
- KEYWORD_do,
- KEYWORD_if,
- KEYWORD_else,
- KEYWORD_switch,
- KEYWORD_try,
- KEYWORD_catch,
- KEYWORD_finally,
- KEYWORD_public,
- KEYWORD_private,
- KEYWORD_static,
- KEYWORD_class,
- KEYWORD_id,
- KEYWORD_script,
- KEYWORD_cdata,
- KEYWORD_mx
-} keywordId;
-
-/* Used to determine whether keyword is valid for the token language and
- * what its ID is.
- */
-typedef struct sKeywordDesc {
- const char *name;
- keywordId id;
-} keywordDesc;
-
-typedef enum eTokenType {
- TOKEN_UNDEFINED,
- TOKEN_CHARACTER,
- TOKEN_CLOSE_PAREN,
- TOKEN_SEMICOLON,
- TOKEN_COLON,
- TOKEN_COMMA,
- TOKEN_KEYWORD,
- TOKEN_OPEN_PAREN,
- TOKEN_OPERATOR,
- TOKEN_IDENTIFIER,
- TOKEN_STRING,
- TOKEN_PERIOD,
- TOKEN_OPEN_CURLY,
- TOKEN_CLOSE_CURLY,
- TOKEN_EQUAL_SIGN,
- TOKEN_EXCLAMATION,
- TOKEN_FORWARD_SLASH,
- TOKEN_OPEN_SQUARE,
- TOKEN_CLOSE_SQUARE,
- TOKEN_OPEN_MXML,
- TOKEN_CLOSE_MXML,
- TOKEN_CLOSE_SGML,
- TOKEN_LESS_THAN,
- TOKEN_GREATER_THAN,
- TOKEN_QUESTION_MARK
-} tokenType;
-
-typedef struct sTokenInfo {
- tokenType type;
- keywordId keyword;
- vString * string;
- vString * scope;
- unsigned long lineNumber;
- fpos_t filePosition;
- int nestLevel;
- boolean ignoreTag;
- boolean isClass;
-} tokenInfo;
-
-/*
- * DATA DEFINITIONS
- */
-
-static langType Lang_js;
-
-static jmp_buf Exception;
-
-typedef enum {
- FLEXTAG_FUNCTION,
- FLEXTAG_CLASS,
- FLEXTAG_METHOD,
- FLEXTAG_PROPERTY,
- FLEXTAG_VARIABLE,
- FLEXTAG_MXTAG,
- FLEXTAG_COUNT
-} flexKind;
-
-static kindOption FlexKinds [] = {
- { TRUE, 'f', "function", "functions" },
- { TRUE, 'c', "class", "classes" },
- { TRUE, 'm', "method", "methods" },
- { TRUE, 'p', "property", "properties" },
- { TRUE, 'v', "variable", "global variables" },
- { TRUE, 'x', "mxtag", "mxtags" }
-};
-
-static const keywordDesc FlexKeywordTable [] = {
- /* keyword keyword ID */
- { "function", KEYWORD_function },
- { "Function", KEYWORD_capital_function },
- { "object", KEYWORD_object },
- { "Object", KEYWORD_capital_object },
- { "prototype", KEYWORD_prototype },
- { "var", KEYWORD_var },
- { "new", KEYWORD_new },
- { "this", KEYWORD_this },
- { "for", KEYWORD_for },
- { "while", KEYWORD_while },
- { "do", KEYWORD_do },
- { "if", KEYWORD_if },
- { "else", KEYWORD_else },
- { "switch", KEYWORD_switch },
- { "try", KEYWORD_try },
- { "catch", KEYWORD_catch },
- { "finally", KEYWORD_finally },
- { "public", KEYWORD_public },
- { "private", KEYWORD_private },
- { "static", KEYWORD_static },
- { "class", KEYWORD_class },
- { "id", KEYWORD_id },
- { "script", KEYWORD_script },
- { "cdata", KEYWORD_cdata },
- { "mx", KEYWORD_mx }
-};
-
-/*
- * FUNCTION DEFINITIONS
- */
-
-/* Recursive functions */
-static void parseFunction (tokenInfo *const token);
-static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
-static boolean parseLine (tokenInfo *const token);
-static boolean parseActionScript (tokenInfo *const token);
-
-static boolean isIdentChar (const int c)
-{
- return (boolean)
- (isalpha (c) || isdigit (c) || c == '$' ||
- c == '@' || c == '_' || c == '#');
-}
-
-static void buildFlexKeywordHash (void)
-{
- const size_t count = sizeof (FlexKeywordTable) /
- sizeof (FlexKeywordTable [0]);
- size_t i;
- for (i = 0 ; i < count ; ++i)
- {
- const keywordDesc* const p = &FlexKeywordTable [i];
- addKeyword (p->name, Lang_js, (int) p->id);
- }
-}
-
-static tokenInfo *newToken (void)
-{
- tokenInfo *const token = xMalloc (1, tokenInfo);
-
- token->type = TOKEN_UNDEFINED;
- token->keyword = KEYWORD_NONE;
- token->string = vStringNew ();
- token->scope = vStringNew ();
- token->nestLevel = 0;
- token->isClass = FALSE;
- token->ignoreTag = FALSE;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
-
- return token;
-}
-
-static void deleteToken (tokenInfo *const token)
-{
- vStringDelete (token->string);
- vStringDelete (token->scope);
- eFree (token);
-}
-
-/*
- * Tag generation functions
- */
-
-static void makeConstTag (tokenInfo *const token, const flexKind kind)
-{
- if (FlexKinds [kind].enabled && ! token->ignoreTag )
- {
- const char *const name = vStringValue (token->string);
- tagEntryInfo e;
- initTagEntry (&e, name);
-
- e.lineNumber = token->lineNumber;
- e.filePosition = token->filePosition;
- e.kindName = FlexKinds [kind].name;
- e.kind = FlexKinds [kind].letter;
-
- makeTagEntry (&e);
- }
-}
-
-static void makeFlexTag (tokenInfo *const token, flexKind kind)
-{
- vString * fulltag;
-
- if (FlexKinds [kind].enabled && ! token->ignoreTag )
- {
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n makeFlexTag start: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- if (kind == FLEXTAG_FUNCTION && token->isClass )
- {
- kind = FLEXTAG_METHOD;
- }
- /*
- * If a scope has been added to the token, change the token
- * string to include the scope when making the tag.
- */
- if ( vStringLength(token->scope) > 0 )
- {
- fulltag = vStringNew ();
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- vStringTerminate(fulltag);
- vStringCopy(token->string, fulltag);
- vStringDelete (fulltag);
- }
- makeConstTag (token, kind);
- }
-}
-
-static void makeClassTag (tokenInfo *const token)
-{
- vString * fulltag;
-
- if ( ! token->ignoreTag )
- {
- fulltag = vStringNew ();
- if (vStringLength (token->scope) > 0)
- {
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- }
- else
- {
- vStringCopy(fulltag, token->string);
- }
- vStringTerminate(fulltag);
- if ( ! stringListHas(ClassNames, vStringValue (fulltag)) )
- {
- stringListAdd (ClassNames, vStringNewCopy (fulltag));
- makeFlexTag (token, FLEXTAG_CLASS);
- }
- vStringDelete (fulltag);
- }
-}
-
-static void makeMXTag (tokenInfo *const token)
-{
- vString * fulltag;
-
- if ( ! token->ignoreTag )
- {
- fulltag = vStringNew ();
- if (vStringLength (token->scope) > 0)
- {
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- }
- else
- {
- vStringCopy(fulltag, token->string);
- }
- vStringTerminate(fulltag);
- makeFlexTag (token, FLEXTAG_MXTAG);
- vStringDelete (fulltag);
- }
-}
-
-static void makeFunctionTag (tokenInfo *const token)
-{
- vString * fulltag;
-
- if ( ! token->ignoreTag )
- {
- fulltag = vStringNew ();
- if (vStringLength (token->scope) > 0)
- {
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- }
- else
- {
- vStringCopy(fulltag, token->string);
- }
- vStringTerminate(fulltag);
- if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) )
- {
- stringListAdd (FunctionNames, vStringNewCopy (fulltag));
- makeFlexTag (token, FLEXTAG_FUNCTION);
- }
- vStringDelete (fulltag);
- }
-}
-
-/*
- * Parsing functions
- */
-
-static void parseString (vString *const string, const int delimiter)
-{
- boolean end = FALSE;
- while (! end)
- {
- int c = fileGetc ();
- if (c == EOF)
- end = TRUE;
- else if (c == '\\')
- {
- c = fileGetc(); /* This maybe a ' or ". */
- vStringPut(string, c);
- }
- else if (c == delimiter)
- end = TRUE;
- else
- vStringPut (string, c);
- }
- vStringTerminate (string);
-}
-
-/* Read a C identifier beginning with "firstChar" and places it into
- * "name".
- */
-static void parseIdentifier (vString *const string, const int firstChar)
-{
- int c = firstChar;
- Assert (isIdentChar (c));
- do
- {
- vStringPut (string, c);
- c = fileGetc ();
- } while (isIdentChar (c));
- vStringTerminate (string);
- if (!isspace (c))
- fileUngetc (c); /* unget non-identifier character */
-}
-
-static void readToken (tokenInfo *const token)
-{
- int c;
-
- token->type = TOKEN_UNDEFINED;
- token->keyword = KEYWORD_NONE;
- vStringClear (token->string);
-
-getNextChar:
- do
- {
- c = fileGetc ();
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- while (c == '\t' || c == ' ' || c == '\n');
-
- switch (c)
- {
- case EOF: longjmp (Exception, (int)ExceptionEOF); break;
- case '(': token->type = TOKEN_OPEN_PAREN; break;
- case ')': token->type = TOKEN_CLOSE_PAREN; break;
- case ';': token->type = TOKEN_SEMICOLON; break;
- case ',': token->type = TOKEN_COMMA; break;
- case '.': token->type = TOKEN_PERIOD; break;
- case ':': token->type = TOKEN_COLON; break;
- case '{': token->type = TOKEN_OPEN_CURLY; break;
- case '}': token->type = TOKEN_CLOSE_CURLY; break;
- case '=': token->type = TOKEN_EQUAL_SIGN; break;
- case '[': token->type = TOKEN_OPEN_SQUARE; break;
- case ']': token->type = TOKEN_CLOSE_SQUARE; break;
- case '?': token->type = TOKEN_QUESTION_MARK; break;
-
- case '\'':
- case '"':
- token->type = TOKEN_STRING;
- parseString (token->string, c);
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- break;
-
- case '\\':
- c = fileGetc ();
- if (c != '\\' && c != '"' && !isspace (c))
- fileUngetc (c);
- token->type = TOKEN_CHARACTER;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- break;
-
- case '/':
- {
- int d = fileGetc ();
- if ( (d != '*') && /* is this the start of a comment? */
- (d != '/') && /* is a one line comment? */
- (d != '>') ) /* is this a close XML tag? */
- {
- fileUngetc (d);
- token->type = TOKEN_FORWARD_SLASH;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (d == '*')
- {
- do
- {
- fileSkipToCharacter ('*');
- c = fileGetc ();
- if (c == '/')
- break;
- else
- fileUngetc (c);
- } while (c != EOF && c != '\0');
- goto getNextChar;
- }
- else if (d == '/') /* is this the start of a comment? */
- {
- fileSkipToCharacter ('\n');
- goto getNextChar;
- }
- else if (d == '>') /* is this the start of a comment? */
- {
- token->type = TOKEN_CLOSE_SGML;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- }
- break;
- }
-
- case '<':
- {
- /*
- * An XML comment looks like this
- * <!-- anything over multiple lines -->
- */
- int d = fileGetc ();
-
- if ( (d != '!' ) && /* is this the start of a comment? */
- (d != '/' ) && /* is this the start of a closing mx tag */
- (d != 'm' ) ) /* is this the start of a mx tag */
- {
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
-
- }
- else
- {
- if (d == '!')
- {
- int e = fileGetc ();
- if ( e != '-' ) /* is this the start of a comment? */
- {
- fileUngetc (e);
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (e == '-')
- {
- int f = fileGetc ();
- if ( f != '-' ) /* is this the start of a comment? */
- {
- fileUngetc (f);
- fileUngetc (e);
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (f == '-')
- {
- do
- {
- fileSkipToCharacter ('-');
- c = fileGetc ();
- if (c == '-')
- {
- d = fileGetc ();
- if (d == '>')
- break;
- else
- {
- fileUngetc (d);
- fileUngetc (c);
- }
- break;
- }
- else
- fileUngetc (c);
- } while (c != EOF && c != '\0');
- goto getNextChar;
- }
- }
- }
- }
- }
- else if (d == 'm')
- {
- int e = fileGetc ();
- if ( e != 'x' ) /* continuing an mx tag */
- {
- fileUngetc (e);
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (e == 'x')
- {
- int f = fileGetc ();
- if ( f != ':' ) /* is this the start of a comment? */
- {
- fileUngetc (f);
- fileUngetc (e);
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (f == ':')
- {
- token->type = TOKEN_OPEN_MXML;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- }
- }
- }
- }
- else if (d == '/')
- {
- int e = fileGetc ();
- if ( e != 'm' ) /* continuing an mx tag */
- {
- fileUngetc (e);
- fileUngetc (d);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- int f = fileGetc ();
- if ( f != 'x' ) /* continuing an mx tag */
- {
- fileUngetc (f);
- fileUngetc (e);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (f == 'x')
- {
- int g = fileGetc ();
- if ( g != ':' ) /* is this the start of a comment? */
- {
- fileUngetc (g);
- fileUngetc (f);
- fileUngetc (e);
- token->type = TOKEN_LESS_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- else
- {
- if (g == ':')
- {
- token->type = TOKEN_CLOSE_MXML;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- }
- }
- }
- }
- }
- }
- }
- break;
- }
-
- case '>':
- token->type = TOKEN_GREATER_THAN;
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- break;
-
- case '!':
- token->type = TOKEN_EXCLAMATION;
- /*token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();*/
- break;
-
- default:
- if (! isIdentChar (c))
- token->type = TOKEN_UNDEFINED;
- else
- {
- parseIdentifier (token->string, c);
- token->lineNumber = getSourceLineNumber ();
- token->filePosition = getInputFilePosition ();
- token->keyword = analyzeToken (token->string, Lang_js);
- if (isKeyword (token, KEYWORD_NONE))
- token->type = TOKEN_IDENTIFIER;
- else
- token->type = TOKEN_KEYWORD;
- }
- break;
- }
-}
-
-static void copyToken (tokenInfo *const dest, tokenInfo *const src)
-{
- dest->nestLevel = src->nestLevel;
- dest->lineNumber = src->lineNumber;
- dest->filePosition = src->filePosition;
- dest->type = src->type;
- dest->keyword = src->keyword;
- dest->isClass = src->isClass;
- vStringCopy(dest->string, src->string);
- vStringCopy(dest->scope, src->scope);
-}
-
-/*
- * Token parsing functions
- */
-
-static void skipArgumentList (tokenInfo *const token)
-{
- int nest_level = 0;
-
- /*
- * Other databases can have arguments with fully declared
- * datatypes:
- * ( name varchar(30), text binary(10) )
- * So we must check for nested open and closing parantheses
- */
-
- if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
- {
- nest_level++;
- while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
- {
- readToken (token);
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- nest_level++;
- }
- if (isType (token, TOKEN_CLOSE_PAREN))
- {
- if (nest_level > 0)
- {
- nest_level--;
- }
- }
- }
- readToken (token);
- }
-}
-
-static void skipArrayList (tokenInfo *const token)
-{
- int nest_level = 0;
-
- /*
- * Handle square brackets
- * var name[1]
- * So we must check for nested open and closing square brackets
- */
-
- if (isType (token, TOKEN_OPEN_SQUARE)) /* arguments? */
- {
- nest_level++;
- while (! (isType (token, TOKEN_CLOSE_SQUARE) && (nest_level == 0)))
- {
- readToken (token);
- if (isType (token, TOKEN_OPEN_SQUARE))
- {
- nest_level++;
- }
- if (isType (token, TOKEN_CLOSE_SQUARE))
- {
- if (nest_level > 0)
- {
- nest_level--;
- }
- }
- }
- readToken (token);
- }
-}
-
-static void addContext (tokenInfo* const parent, const tokenInfo* const child)
-{
- if (vStringLength (parent->string) > 0)
- {
- vStringCatS (parent->string, ".");
- }
- vStringCatS (parent->string, vStringValue(child->string));
- vStringTerminate(parent->string);
-}
-
-static void addToScope (tokenInfo* const token, vString* const extra)
-{
- if (vStringLength (token->scope) > 0)
- {
- vStringCatS (token->scope, ".");
- }
- vStringCatS (token->scope, vStringValue(extra));
- vStringTerminate(token->scope);
-}
-
-/*
- * Scanning functions
- */
-
-static void findCmdTerm (tokenInfo *const token)
-{
- /*
- * Read until we find either a semicolon or closing brace.
- * Any nested braces will be handled within.
- */
- while (! ( isType (token, TOKEN_SEMICOLON) ||
- isType (token, TOKEN_CLOSE_CURLY) ) )
- {
- /* Handle nested blocks */
- if ( isType (token, TOKEN_OPEN_CURLY))
- {
- parseBlock (token, token);
- }
- else if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- skipArgumentList(token);
- }
- else
- {
- readToken (token);
- }
- }
-}
-
-static void parseSwitch (tokenInfo *const token)
-{
- /*
- * switch (expression){
- * case value1:
- * statement;
- * break;
- * case value2:
- * statement;
- * break;
- * default : statement;
- * }
- */
-
- readToken (token);
-
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- skipArgumentList(token);
- }
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- do
- {
- readToken (token);
- } while (! (isType (token, TOKEN_CLOSE_SGML) ||
- isType (token, TOKEN_CLOSE_MXML) ||
- isType (token, TOKEN_CLOSE_CURLY) ||
- isType (token, TOKEN_GREATER_THAN)) );
- }
-
-}
-
-static void parseLoop (tokenInfo *const token)
-{
- /*
- * Handles these statements
- * for (x=0; x<3; x++)
- * document.write("This text is repeated three times<br>");
- *
- * for (x=0; x<3; x++)
- * {
- * document.write("This text is repeated three times<br>");
- * }
- *
- * while (number<5){
- * document.write(number+"<br>");
- * number++;
- * }
- *
- * do{
- * document.write(number+"<br>");
- * number++;
- * }
- * while (number<5);
- */
-
- if (isKeyword (token, KEYWORD_for) || isKeyword (token, KEYWORD_while))
- {
- readToken(token);
-
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /*
- * Handle nameless functions, these will only
- * be considered methods.
- */
- skipArgumentList(token);
- }
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * This will be either a function or a class.
- * We can only determine this by checking the body
- * of the function. If we find a "this." we know
- * it is a class, otherwise it is a function.
- */
- parseBlock (token, token);
- }
- else
- {
- parseLine(token);
- }
- }
- else if (isKeyword (token, KEYWORD_do))
- {
- readToken(token);
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * This will be either a function or a class.
- * We can only determine this by checking the body
- * of the function. If we find a "this." we know
- * it is a class, otherwise it is a function.
- */
- parseBlock (token, token);
- }
- else
- {
- parseLine(token);
- }
-
- readToken(token);
-
- if (isKeyword (token, KEYWORD_while))
- {
- readToken(token);
-
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /*
- * Handle nameless functions, these will only
- * be considered methods.
- */
- skipArgumentList(token);
- }
- }
- }
-}
-
-static boolean parseIf (tokenInfo *const token)
-{
- boolean read_next_token = TRUE;
- /*
- * If statements have two forms
- * if ( ... )
- * one line;
- *
- * if ( ... )
- * statement;
- * else
- * statement
- *
- * if ( ... ) {
- * multiple;
- * statements;
- * }
- *
- *
- * if ( ... ) {
- * return elem
- * }
- *
- * This example if correctly written, but the
- * else contains only 1 statement without a terminator
- * since the function finishes with the closing brace.
- *
- * function a(flag){
- * if(flag)
- * test(1);
- * else
- * test(2)
- * }
- *
- * TODO: Deal with statements that can optional end
- * without a semi-colon. Currently this messes up
- * the parsing of blocks.
- * Need to somehow detect this has happened, and either
- * backup a token, or skip reading the next token if
- * that is possible from all code locations.
- *
- */
-
- readToken (token);
-
- if (isKeyword (token, KEYWORD_if))
- {
- /*
- * Check for an "else if" and consume the "if"
- */
- readToken (token);
- }
-
- if (isType (token, TOKEN_OPEN_PAREN))
- {
- /*
- * Handle nameless functions, these will only
- * be considered methods.
- */
- skipArgumentList(token);
- }
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * This will be either a function or a class.
- * We can only determine this by checking the body
- * of the function. If we find a "this." we know
- * it is a class, otherwise it is a function.
- */
- parseBlock (token, token);
- }
- else
- {
- findCmdTerm (token);
-
- /*
- * The IF could be followed by an ELSE statement.
- * This too could have two formats, a curly braced
- * multiline section, or another single line.
- */
-
- if (isType (token, TOKEN_CLOSE_CURLY))
- {
- /*
- * This statement did not have a line terminator.
- */
- read_next_token = FALSE;
- }
- else
- {
- readToken (token);
-
- if (isType (token, TOKEN_CLOSE_CURLY))
- {
- /*
- * This statement did not have a line terminator.
- */
- read_next_token = FALSE;
- }
- else
- {
- if (isKeyword (token, KEYWORD_else))
- read_next_token = parseIf (token);
- }
- }
- }
- return read_next_token;
-}
-
-static void parseFunction (tokenInfo *const token)
-{
- tokenInfo *const name = newToken ();
-
- /*
- * This deals with these formats
- * private static function ioErrorHandler( event:IOErrorEvent ):void {
- */
-
- if ( isKeyword(token, KEYWORD_function) )
- {
- readToken (token);
- }
-
- copyToken (name, token);
- /* Add scope in case this is an INNER function
- addToScope(name, token->scope);
- */
-
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction: name isClass:%d scope:%s name:%s\n"
- , name->isClass
- , vStringValue(name->scope)
- , vStringValue(name->string)
- );
- );
-
- readToken (token);
-
- if ( isType (token, TOKEN_OPEN_PAREN) )
- skipArgumentList(token);
-
- if ( isType (token, TOKEN_COLON) )
- {
- /*
- * function fname ():ReturnType
- */
- readToken (token);
- readToken (token);
- }
-
- if ( isType (token, TOKEN_OPEN_CURLY) )
- {
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction end: name isClass:%d scope:%s name:%s\n"
- , name->isClass
- , vStringValue(name->scope)
- , vStringValue(name->string)
- );
- );
- parseBlock (token, name);
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseFunction end3: name isClass:%d scope:%s name:%s\n"
- , name->isClass
- , vStringValue(name->scope)
- , vStringValue(name->string)
- );
- );
- makeFunctionTag (name);
- }
-
- findCmdTerm (token);
-
- deleteToken (name);
-}
-
-static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
-{
- boolean read_next_token = TRUE;
- vString * saveScope = vStringNew ();
-
- vStringClear(saveScope);
- vStringCopy (saveScope, token->scope);
- token->nestLevel++;
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseBlock start: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- /*
- * Make this routine a bit more forgiving.
- * If called on an open_curly advance it
- */
- if ( isType (token, TOKEN_OPEN_CURLY) &&
- isKeyword(token, KEYWORD_NONE) )
- readToken(token);
-
- if (! isType (token, TOKEN_CLOSE_CURLY))
- {
- /*
- * Read until we find the closing brace,
- * any nested braces will be handled within
- */
- do
- {
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /* Handle nested blocks */
- parseBlock (token, parent);
- }
- else
- {
- /*
- * It is possible for a line to have no terminator
- * if the following line is a closing brace.
- * parseLine will detect this case and indicate
- * whether we should read an additional token.
- */
- read_next_token = parseLine (token);
- }
-
- /*
- * Always read a new token unless we find a statement without
- * a ending terminator
- */
- if( read_next_token )
- readToken(token);
-
- /*
- * If we find a statement without a terminator consider the
- * block finished, otherwise the stack will be off by one.
- */
- } while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token );
- }
-
- vStringDelete(saveScope);
- token->nestLevel--;
-
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseBlock end: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- return FALSE;
-}
-
-static void parseMethods (tokenInfo *const token, tokenInfo *const class)
-{
- tokenInfo *const name = newToken ();
-
- /*
- * This deals with these formats
- * validProperty : 2,
- * validMethod : function(a,b) {}
- * 'validMethod2' : function(a,b) {}
- * container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false}
- */
-
- do
- {
- readToken (token);
- if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE))
- {
- copyToken (name, token);
-
- readToken (token);
- if ( isType (token, TOKEN_COLON) )
- {
- readToken (token);
- if ( isKeyword (token, KEYWORD_function) )
- {
- readToken (token);
- if ( isType (token, TOKEN_OPEN_PAREN) )
- {
- skipArgumentList(token);
- }
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- addToScope (name, class->string);
- makeFlexTag (name, FLEXTAG_METHOD);
- parseBlock (token, name);
-
- /*
- * Read to the closing curly, check next
- * token, if a comma, we must loop again
- */
- readToken (token);
- }
- }
- else
- {
- addToScope (name, class->string);
- makeFlexTag (name, FLEXTAG_PROPERTY);
-
- /*
- * Read the next token, if a comma
- * we must loop again
- */
- readToken (token);
- }
- }
- }
- } while ( isType(token, TOKEN_COMMA) );
-
- findCmdTerm (token);
-
- deleteToken (name);
-}
-
-static boolean parseVar (tokenInfo *const token, boolean is_public)
-{
- tokenInfo *const name = newToken ();
- tokenInfo *const secondary_name = newToken ();
- vString * saveScope = vStringNew ();
- boolean is_terminated = TRUE;
-
- vStringClear(saveScope);
- vStringCopy (saveScope, token->scope);
- /*
- * Variables are defined as:
- * private static var lastFaultMessage:Date = new Date( 0 );
- * private static var webRequests:ArrayCollection = new ArrayCollection();
- */
-
- if ( isKeyword(token, KEYWORD_var) )
- {
- readToken(token);
- }
-
- /* Variable name */
- copyToken (name, token);
- readToken(token);
-
- if ( isType (token, TOKEN_COLON) )
- {
- /*
- * var vname ():DataType = new Date();
- * var vname ():DataType;
- */
- readToken (token);
- readToken (token);
- }
-
- while (! isType (token, TOKEN_SEMICOLON) )
- {
- readToken (token);
- }
-
- if ( isType (token, TOKEN_SEMICOLON) )
- {
- /*
- * Only create variables for global scope
- */
- /* if ( token->nestLevel == 0 && is_global ) */
- if ( is_public )
- {
- if (isType (token, TOKEN_SEMICOLON))
- makeFlexTag (name, FLEXTAG_VARIABLE);
- }
- }
-
- vStringCopy(token->scope, saveScope);
- deleteToken (name);
- deleteToken (secondary_name);
- vStringDelete(saveScope);
-
- return is_terminated;
-}
-
-static boolean parseClass (tokenInfo *const token)
-{
- tokenInfo *const name = newToken ();
- vString * saveScope = vStringNew ();
- boolean saveIsClass = token->isClass;
-
- vStringClear(saveScope);
- vStringCopy (saveScope, token->scope);
- /*
- * Variables are defined as:
- * private static var lastFaultMessage:Date = new Date( 0 );
- * private static var webRequests:ArrayCollection = new ArrayCollection();
- */
-
- if ( isKeyword(token, KEYWORD_class) )
- {
- readToken(token);
- }
-
- token->isClass = TRUE;
- /* Add class name to scope */
- addToScope(token, token->string);
- /* Class name */
- copyToken (name, token);
- readToken(token);
-
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseClass start: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- if ( isType (token, TOKEN_OPEN_CURLY) )
- {
- makeClassTag (name);
- parseBlock (token, name);
- }
-
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseClass end: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- vStringCopy(token->scope, saveScope);
- token->isClass = saveIsClass;
- deleteToken (name);
- vStringDelete(saveScope);
-
- return TRUE;
-}
-
-static boolean parseStatement (tokenInfo *const token)
-{
- tokenInfo *const name = newToken ();
- tokenInfo *const secondary_name = newToken ();
- vString * saveScope = vStringNew ();
- boolean is_public = FALSE;
- boolean is_class = FALSE;
- boolean is_terminated = TRUE;
- boolean is_global = FALSE;
- boolean is_prototype = FALSE;
- vString * fulltag;
-
- vStringClear(saveScope);
- vStringCopy (saveScope, token->scope);
- DebugStatement (
- debugPrintf (DEBUG_PARSE
- , "\n parseStatement: token isClass:%d scope:%s name:%s\n"
- , token->isClass
- , vStringValue(token->scope)
- , vStringValue(token->string)
- );
- );
- /*
- * Functions can be named or unnamed.
- * This deals with these formats:
- * Function
- * validFunctionOne = function(a,b) {}
- * testlib.validFunctionFive = function(a,b) {}
- * var innerThree = function(a,b) {}
- * var innerFour = (a,b) {}
- * var D2 = secondary_fcn_name(a,b) {}
- * var D3 = new Function("a", "b", "return a+b;");
- * Class
- * testlib.extras.ValidClassOne = function(a,b) {
- * this.a = a;
- * }
- * Class Methods
- * testlib.extras.ValidClassOne.prototype = {
- * 'validMethodOne' : function(a,b) {},
- * 'validMethodTwo' : function(a,b) {}
- * }
- * ValidClassTwo = function ()
- * {
- * this.validMethodThree = function() {}
- * // unnamed method
- * this.validMethodFour = () {}
- * }
- * Database.prototype.validMethodThree = Database_getTodaysDate;
- */
-
- if ( isKeyword(token, KEYWORD_public) )
- {
- is_public = TRUE;
- readToken(token);
- }
-
- if ( isKeyword(token, KEYWORD_private) )
- {
- readToken(token);
- }
-
- if ( isKeyword(token, KEYWORD_static) )
- {
- readToken(token);
- }
-
- if (isType(token, TOKEN_KEYWORD))
- {
- switch (token->keyword)
- {
- case KEYWORD_for:
- case KEYWORD_while:
- case KEYWORD_do:
- parseLoop (token);
- break;
- case KEYWORD_if:
- case KEYWORD_else:
- case KEYWORD_try:
- case KEYWORD_catch:
- case KEYWORD_finally:
- /* Common semantics */
- is_terminated = parseIf (token);
- break;
- case KEYWORD_switch:
- parseSwitch (token);
- break;
- case KEYWORD_class:
- parseClass (token);
- return is_terminated;
- break;
- case KEYWORD_function:
- parseFunction (token);
- return is_terminated;
- break;
- case KEYWORD_var:
- parseVar (token, is_public);
- return is_terminated;
- break;
- default:
- readToken(token);
- break;
- }
- }
-
- copyToken (name, token);
-
- while (! isType (token, TOKEN_CLOSE_CURLY) &&
- ! isType (token, TOKEN_SEMICOLON) &&
- ! isType (token, TOKEN_EQUAL_SIGN) )
- {
- /* Potentially the name of the function */
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- /*
- * Cannot be a global variable is it has dot references in the name
- */
- is_global = FALSE;
- do
- {
- readToken (token);
- if ( isKeyword(token, KEYWORD_NONE) )
- {
- if ( is_class )
- {
- vStringCopy(saveScope, token->scope);
- addToScope(token, name->string);
- }
- else
- addContext (name, token);
- }
- else if ( isKeyword(token, KEYWORD_prototype) )
- {
- /*
- * When we reach the "prototype" tag, we infer:
- * "BindAgent" is a class
- * "build" is a method
- *
- * function BindAgent( repeatableIdName, newParentIdName ) {
- * }
- *
- * CASE 1
- * Specified function name: "build"
- * BindAgent.prototype.build = function( mode ) {
- * ignore everything within this function
- * }
- *
- * CASE 2
- * Prototype listing
- * ValidClassOne.prototype = {
- * 'validMethodOne' : function(a,b) {},
- * 'validMethodTwo' : function(a,b) {}
- * }
- *
- */
- makeClassTag (name);
- is_class = TRUE;
- is_prototype = TRUE;
-
- /*
- * There should a ".function_name" next.
- */
- readToken (token);
- if (isType (token, TOKEN_PERIOD))
- {
- /*
- * Handle CASE 1
- */
- readToken (token);
- if ( isKeyword(token, KEYWORD_NONE) )
- {
- vStringCopy(saveScope, token->scope);
- addToScope(token, name->string);
-
- makeFlexTag (token, FLEXTAG_METHOD);
- /*
- * We can read until the end of the block / statement.
- * We need to correctly parse any nested blocks, but
- * we do NOT want to create any tags based on what is
- * within the blocks.
- */
- token->ignoreTag = TRUE;
- /*
- * Find to the end of the statement
- */
- findCmdTerm (token);
- token->ignoreTag = FALSE;
- is_terminated = TRUE;
- goto cleanUp;
- }
- }
- else if (isType (token, TOKEN_EQUAL_SIGN))
- {
- readToken (token);
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * Handle CASE 2
- *
- * Creates tags for each of these class methods
- * ValidClassOne.prototype = {
- * 'validMethodOne' : function(a,b) {},
- * 'validMethodTwo' : function(a,b) {}
- * }
- */
- parseMethods(token, name);
- /*
- * Find to the end of the statement
- */
- findCmdTerm (token);
- token->ignoreTag = FALSE;
- is_terminated = TRUE;
- goto cleanUp;
- }
- }
- }
- readToken (token);
- } while (isType (token, TOKEN_PERIOD));
- }
-
- if ( isType (token, TOKEN_OPEN_PAREN) )
- skipArgumentList(token);
-
- if ( isType (token, TOKEN_COLON) )
- {
- /*
- * Functions are of this form:
- * function fname ():ReturnType {
- */
- readToken (token);
- readToken (token);
- }
-
- if ( isType (token, TOKEN_OPEN_SQUARE) )
- skipArrayList(token);
-
- }
-
- if ( isType (token, TOKEN_CLOSE_CURLY) )
- {
- /*
- * Reaching this section without having
- * processed an open curly brace indicates
- * the statement is most likely not terminated.
- */
- is_terminated = FALSE;
- goto cleanUp;
- }
-
- if ( isType (token, TOKEN_SEMICOLON) )
- {
- /*
- * Only create variables for global scope
- */
- if ( token->nestLevel == 0 && is_global )
- {
- /*
- * Handles this syntax:
- * var g_var2;
- */
- if (isType (token, TOKEN_SEMICOLON))
- makeFlexTag (name, FLEXTAG_VARIABLE);
- }
- /*
- * Statement has ended.
- * This deals with calls to functions, like:
- * alert(..);
- */
- goto cleanUp;
- }
-
- if ( isType (token, TOKEN_EQUAL_SIGN) )
- {
- readToken (token);
-
- if ( isKeyword (token, KEYWORD_function) )
- {
- readToken (token);
-
- if ( isKeyword (token, KEYWORD_NONE) &&
- ! isType (token, TOKEN_OPEN_PAREN) )
- {
- /*
- * Functions of this format:
- * var D2A = function theAdd(a, b)
- * {
- * return a+b;
- * }
- * Are really two separate defined functions and
- * can be referenced in two ways:
- * alert( D2A(1,2) ); // produces 3
- * alert( theAdd(1,2) ); // also produces 3
- * So it must have two tags:
- * D2A
- * theAdd
- * Save the reference to the name for later use, once
- * we have established this is a valid function we will
- * create the secondary reference to it.
- */
- copyToken (secondary_name, token);
- readToken (token);
- }
-
- if ( isType (token, TOKEN_OPEN_PAREN) )
- skipArgumentList(token);
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * This will be either a function or a class.
- * We can only determine this by checking the body
- * of the function. If we find a "this." we know
- * it is a class, otherwise it is a function.
- */
- if ( token->isClass )
- {
- makeFlexTag (name, FLEXTAG_METHOD);
- if ( vStringLength(secondary_name->string) > 0 )
- makeFunctionTag (secondary_name);
- parseBlock (token, name);
- }
- else
- {
- parseBlock (token, name);
- makeFunctionTag (name);
-
- if ( vStringLength(secondary_name->string) > 0 )
- makeFunctionTag (secondary_name);
-
- /*
- * Find to the end of the statement
- */
- goto cleanUp;
- }
- }
- }
- else if (isType (token, TOKEN_OPEN_PAREN))
- {
- /*
- * Handle nameless functions
- * this.method_name = () {}
- */
- skipArgumentList(token);
-
- if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * Nameless functions are only setup as methods.
- */
- makeFlexTag (name, FLEXTAG_METHOD);
- parseBlock (token, name);
- }
- }
- else if (isType (token, TOKEN_OPEN_CURLY))
- {
- /*
- * Creates tags for each of these class methods
- * ValidClassOne.prototype = {
- * 'validMethodOne' : function(a,b) {},
- * 'validMethodTwo' : function(a,b) {}
- * }
- */
- parseMethods(token, name);
- if (isType (token, TOKEN_CLOSE_CURLY))
- {
- /*
- * Assume the closing parantheses terminates
- * this statements.
- */
- is_terminated = TRUE;
- }
- }
- else if (isKeyword (token, KEYWORD_new))
- {
- readToken (token);
- if ( isKeyword (token, KEYWORD_function) ||
- isKeyword (token, KEYWORD_capital_function) ||
- isKeyword (token, KEYWORD_object) ||
- isKeyword (token, KEYWORD_capital_object) )
- {
- if ( isKeyword (token, KEYWORD_object) ||
- isKeyword (token, KEYWORD_capital_object) )
- is_class = TRUE;
-
- readToken (token);
- if ( isType (token, TOKEN_OPEN_PAREN) )
- skipArgumentList(token);
-
- if (isType (token, TOKEN_SEMICOLON))
- {
- if ( token->nestLevel == 0 )
- {
- if ( is_class )
- {
- makeClassTag (name);
- } else {
- makeFunctionTag (name);
- }
- }
- }
- }
- }
- else if (isKeyword (token, KEYWORD_NONE))
- {
- /*
- * Only create variables for global scope
- */
- if ( token->nestLevel == 0 && is_global )
- {
- /*
- * A pointer can be created to the function.
- * If we recognize the function/class name ignore the variable.
- * This format looks identical to a variable definition.
- * A variable defined outside of a block is considered
- * a global variable:
- * var g_var1 = 1;
- * var g_var2;
- * This is not a global variable:
- * var g_var = function;
- * This is a global variable:
- * var g_var = different_var_name;
- */
- fulltag = vStringNew ();
- if (vStringLength (token->scope) > 0)
- {
- vStringCopy(fulltag, token->scope);
- vStringCatS (fulltag, ".");
- vStringCatS (fulltag, vStringValue(token->string));
- }
- else
- {
- vStringCopy(fulltag, token->string);
- }
- vStringTerminate(fulltag);
- if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
- ! stringListHas(ClassNames, vStringValue (fulltag)) )
- {
- findCmdTerm (token);
- if (isType (token, TOKEN_SEMICOLON))
- makeFlexTag (name, FLEXTAG_VARIABLE);
- }
- vStringDelete (fulltag);
- }
- }
- }
- findCmdTerm (token);
-
- /*
- * Statements can be optionally terminated in the case of
- * statement prior to a close curly brace as in the
- * document.write line below:
- *
- * function checkForUpdate() {
- * if( 1==1 ) {
- * document.write("hello from checkForUpdate<br>")
- * }
- * return 1;
- * }
- */
- if ( ! is_terminated && isType (token, TOKEN_CLOSE_CURLY))
- is_terminated = FALSE;
-
-
-cleanUp:
- vStringCopy(token->scope, saveScope);
- deleteToken (name);
- deleteToken (secondary_name);
- vStringDelete(saveScope);
-
- return is_terminated;
-}
-
-static boolean parseLine (tokenInfo *const token)
-{
- boolean is_terminated = TRUE;
- /*
- * Detect the common statements, if, while, for, do, ...
- * This is necessary since the last statement within a block "{}"
- * can be optionally terminated.
- *
- * If the statement is not terminated, we need to tell
- * the calling routine to prevent reading an additional token
- * looking for the end of the statement.
- */
-
- if (isType(token, TOKEN_KEYWORD))
- {
- switch (token->keyword)
- {
- case KEYWORD_for:
- case KEYWORD_while:
- case KEYWORD_do:
- parseLoop (token);
- break;
- case KEYWORD_if:
- case KEYWORD_else:
- case KEYWORD_try:
- case KEYWORD_catch:
- case KEYWORD_finally:
- /* Common semantics */
- is_terminated = parseIf (token);
- break;
- case KEYWORD_switch:
- parseSwitch (token);
- break;
- default:
- parseStatement (token);
- break;
- }
- }
- else
- {
- /*
- * Special case where single line statements may not be
- * SEMICOLON terminated. parseBlock needs to know this
- * so that it does not read the next token.
- */
- is_terminated = parseStatement (token);
- }
- return is_terminated;
-}
-
-static boolean parseCDATA (tokenInfo *const token)
-{
- if (isType (token, TOKEN_LESS_THAN))
- {
- /*
- * Handle these tags
- * <![CDATA[
- * ...
- * ]]>
- */
- readToken (token);
- if (isType (token, TOKEN_EXCLAMATION))
- {
- /*
- * Not sure why I had to comment these out, but I did.
- * readToken (token);
- * if (isType (token, TOKEN_OPEN_SQUARE))
- * {
- */
- readToken (token);
- if (isKeyword (token, KEYWORD_cdata))
- {
- readToken (token);
- if (isType (token, TOKEN_OPEN_SQUARE))
- {
- parseActionScript (token);
- if (isType (token, TOKEN_CLOSE_SQUARE))
- {
- readToken (token);
- if (isType (token, TOKEN_CLOSE_SQUARE))
- {
- readToken (token);
- }
- }
- }
- }
- /*} Not sure */
- }
- }
- else
- {
- parseActionScript (token);
- }
- return TRUE;
-}
-
-static boolean parseMXML (tokenInfo *const token)
-{
- tokenInfo *const name = newToken ();
- tokenInfo *const type = newToken ();
- /*
- * Detect the common statements, if, while, for, do, ...
- * This is necessary since the last statement within a block "{}"
- * can be optionally terminated.
- *
- * If the statement is not terminated, we need to tell
- * the calling routine to prevent reading an additional token
- * looking for the end of the statement.
- */
-
- readToken (token);
-
- if (isKeyword (token, KEYWORD_script))
- {
- /*
- * These tags can be of this form:
- * <mx:Script src="filename.as" />
- */
- do
- {
- readToken (token);
- } while (! (isType (token, TOKEN_CLOSE_SGML) ||
- isType (token, TOKEN_CLOSE_MXML) ||
- isType (token, TOKEN_GREATER_THAN)) );
-
- if (isType (token, TOKEN_CLOSE_MXML))
- {
- /*
- * We have found a </mx:type> tag
- * Finish reading the "type" and ">"
- */
- readToken (token);
- readToken (token);
- goto cleanUp;
- }
- if (isType (token, TOKEN_CLOSE_SGML))
- {
- /*
- * We have found a <mx:Script src="filename.as" />
- */
- goto cleanUp;
- }
-
- /*
- * This is a beginning of an embedded script.
- * These typically are of this format:
- * <mx:Script>
- * <![CDATA[
- * ... ActionScript ...
- * ]]>
- * </mx:Script>
- */
- readToken (token);
- parseCDATA (token);
-
- readToken (token);
- if (isType (token, TOKEN_CLOSE_MXML))
- {
- /*
- * We have found a </mx:type> tag
- * Finish reading the "type" and ">"
- */
- readToken (token);
- readToken (token);
- }
- goto cleanUp;
- }
-
- copyToken (type, token);
-
- readToken (token);
- do
- {
- if (isType (token, TOKEN_OPEN_MXML))
- {
- parseMXML (token);
- }
- else if (isKeyword (token, KEYWORD_id))
- {
- /* = */
- readToken (token);
- readToken (token);
-
- copyToken (name, token);
- addToScope (name, type->string);
- makeMXTag (name);
- }
- readToken (token);
- } while (! (isType (token, TOKEN_CLOSE_SGML) || isType (token, TOKEN_CLOSE_MXML)) );
-
- if (isType (token, TOKEN_CLOSE_MXML))
- {
- /*
- * We have found a </mx:type> tag
- * Finish reading the "type" and ">"
- */
- readToken (token);
- readToken (token);
- }
-
-cleanUp:
- deleteToken (name);
- deleteToken (type);
- return TRUE;
-}
-
-static boolean parseActionScript (tokenInfo *const token)
-{
- do
- {
- readToken (token);
-
- if (isType (token, TOKEN_LESS_THAN))
- {
- /*
- * Handle these tags
- * <![CDATA[
- * ...
- * ]]>
- */
- readToken (token);
- if (isType (token, TOKEN_EQUAL_SIGN))
- {
- if (isType (token, TOKEN_OPEN_SQUARE))
- {
- readToken (token);
- if (isKeyword (token, KEYWORD_cdata))
- {
- readToken (token);
- }
- }
- }
- }
- if (isType (token, TOKEN_CLOSE_SQUARE))
- {
- /*
- * Handle these tags
- * <![CDATA[
- * ...
- * ]]>
- */
- readToken (token);
- if (isType (token, TOKEN_CLOSE_SQUARE))
- {
- readToken (token);
- if (isType (token, TOKEN_GREATER_THAN))
- {
- return TRUE;
- }
- }
- }
- else if (isType (token, TOKEN_CLOSE_MXML))
- {
- /*
- * Read the Script> tags
- */
- readToken (token);
- readToken (token);
- return TRUE;
- }
- else if (isType (token, TOKEN_OPEN_MXML))
- {
- parseMXML (token);
- }
- else
- {
- if (isType(token, TOKEN_KEYWORD))
- {
- switch (token->keyword)
- {
- case KEYWORD_function: parseFunction (token); break;
- default: parseLine (token); break;
- }
- }
- else
- {
- parseLine (token);
- }
- }
- } while (TRUE);
-}
-
-static void parseFlexFile (tokenInfo *const token)
-{
- do
- {
- readToken (token);
-
- if (isType (token, TOKEN_OPEN_MXML))
- {
- parseMXML (token);
- }
- if (isType (token, TOKEN_LESS_THAN))
- {
- readToken (token);
- if (isType (token, TOKEN_QUESTION_MARK))
- {
- readToken (token);
- while (! isType (token, TOKEN_QUESTION_MARK) )
- {
- readToken (token);
- }
- readToken (token);
- }
- }
- else
- {
- parseActionScript (token);
- }
- } while (TRUE);
-}
-
-static void initialize (const langType language)
-{
- Assert (sizeof (FlexKinds) / sizeof (FlexKinds [0]) == FLEXTAG_COUNT);
- Lang_js = language;
- buildFlexKeywordHash ();
-}
-
-static void findFlexTags (void)
-{
- tokenInfo *const token = newToken ();
- exception_t exception;
-
- ClassNames = stringListNew ();
- FunctionNames = stringListNew ();
-
- exception = (exception_t) (setjmp (Exception));
- while (exception == ExceptionNone)
- parseFlexFile (token);
-
- stringListDelete (ClassNames);
- stringListDelete (FunctionNames);
- ClassNames = NULL;
- FunctionNames = NULL;
- deleteToken (token);
-}
-
-/* Create parser definition stucture */
-extern parserDefinition* FlexParser (void)
-{
- static const char *const extensions [] = { "as", "mxml", NULL };
- parserDefinition *const def = parserNew ("Flex");
- def->extensions = extensions;
- /*
- * New definitions for parsing instead of regex
- */
- def->kinds = FlexKinds;
- def->kindCount = KIND_COUNT (FlexKinds);
- def->parser = findFlexTags;
- def->initialize = initialize;
-
- return def;
-}
-/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
+/*
+ * $Id: flex.c 666 2008-05-15 17:47:31Z dfishburn $
+ *
+ * Copyright (c) 2008, David Fishburn
+ *
+ * This source code is released for free distribution under the terms of the
+ * GNU General Public License.
+ *
+ * This module contains functions for generating tags for Adobe languages.
+ * There are a number of different ones, but this will begin with:
+ * Flex
+ * MXML files (*.mMacromedia XML)
+ * ActionScript files (*.as)
+ *
+ * Flex 3 language reference
+ * http://livedocs.adobe.com/flex/3/langref/index.html
+ */
+
+/*
+ * INCLUDE FILES
+ */
+#include "general.h" /* must always come first */
+#include <ctype.h> /* to define isalpha () */
+#include <setjmp.h>
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include "debug.h"
+#include "entry.h"
+#include "keyword.h"
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+
+/*
+ * MACROS
+ */
+#define isType(token,t) (boolean) ((token)->type == (t))
+#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
+
+/*
+ * DATA DECLARATIONS
+ */
+
+typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
+
+/*
+ * Tracks class and function names already created
+ */
+static stringList *ClassNames;
+static stringList *FunctionNames;
+
+/* Used to specify type of keyword.
+*/
+typedef enum eKeywordId {
+ KEYWORD_NONE = -1,
+ KEYWORD_function,
+ KEYWORD_capital_function,
+ KEYWORD_object,
+ KEYWORD_capital_object,
+ KEYWORD_prototype,
+ KEYWORD_var,
+ KEYWORD_new,
+ KEYWORD_this,
+ KEYWORD_for,
+ KEYWORD_while,
+ KEYWORD_do,
+ KEYWORD_if,
+ KEYWORD_else,
+ KEYWORD_switch,
+ KEYWORD_try,
+ KEYWORD_catch,
+ KEYWORD_finally,
+ KEYWORD_public,
+ KEYWORD_private,
+ KEYWORD_static,
+ KEYWORD_class,
+ KEYWORD_id,
+ KEYWORD_script,
+ KEYWORD_cdata,
+ KEYWORD_mx
+} keywordId;
+
+/* Used to determine whether keyword is valid for the token language and
+ * what its ID is.
+ */
+typedef struct sKeywordDesc {
+ const char *name;
+ keywordId id;
+} keywordDesc;
+
+typedef enum eTokenType {
+ TOKEN_UNDEFINED,
+ TOKEN_CHARACTER,
+ TOKEN_CLOSE_PAREN,
+ TOKEN_SEMICOLON,
+ TOKEN_COLON,
+ TOKEN_COMMA,
+ TOKEN_KEYWORD,
+ TOKEN_OPEN_PAREN,
+ TOKEN_OPERATOR,
+ TOKEN_IDENTIFIER,
+ TOKEN_STRING,
+ TOKEN_PERIOD,
+ TOKEN_OPEN_CURLY,
+ TOKEN_CLOSE_CURLY,
+ TOKEN_EQUAL_SIGN,
+ TOKEN_EXCLAMATION,
+ TOKEN_FORWARD_SLASH,
+ TOKEN_OPEN_SQUARE,
+ TOKEN_CLOSE_SQUARE,
+ TOKEN_OPEN_MXML,
+ TOKEN_CLOSE_MXML,
+ TOKEN_CLOSE_SGML,
+ TOKEN_LESS_THAN,
+ TOKEN_GREATER_THAN,
+ TOKEN_QUESTION_MARK
+} tokenType;
+
+typedef struct sTokenInfo {
+ tokenType type;
+ keywordId keyword;
+ vString * string;
+ vString * scope;
+ unsigned long lineNumber;
+ fpos_t filePosition;
+ int nestLevel;
+ boolean ignoreTag;
+ boolean isClass;
+} tokenInfo;
+
+/*
+ * DATA DEFINITIONS
+ */
+
+static langType Lang_js;
+
+static jmp_buf Exception;
+
+typedef enum {
+ FLEXTAG_FUNCTION,
+ FLEXTAG_CLASS,
+ FLEXTAG_METHOD,
+ FLEXTAG_PROPERTY,
+ FLEXTAG_VARIABLE,
+ FLEXTAG_MXTAG,
+ FLEXTAG_COUNT
+} flexKind;
+
+static kindOption FlexKinds [] = {
+ { TRUE, 'f', "function", "functions" },
+ { TRUE, 'c', "class", "classes" },
+ { TRUE, 'm', "method", "methods" },
+ { TRUE, 'p', "property", "properties" },
+ { TRUE, 'v', "variable", "global variables" },
+ { TRUE, 'x', "mxtag", "mxtags" }
+};
+
+static const keywordDesc FlexKeywordTable [] = {
+ /* keyword keyword ID */
+ { "function", KEYWORD_function },
+ { "Function", KEYWORD_capital_function },
+ { "object", KEYWORD_object },
+ { "Object", KEYWORD_capital_object },
+ { "prototype", KEYWORD_prototype },
+ { "var", KEYWORD_var },
+ { "new", KEYWORD_new },
+ { "this", KEYWORD_this },
+ { "for", KEYWORD_for },
+ { "while", KEYWORD_while },
+ { "do", KEYWORD_do },
+ { "if", KEYWORD_if },
+ { "else", KEYWORD_else },
+ { "switch", KEYWORD_switch },
+ { "try", KEYWORD_try },
+ { "catch", KEYWORD_catch },
+ { "finally", KEYWORD_finally },
+ { "public", KEYWORD_public },
+ { "private", KEYWORD_private },
+ { "static", KEYWORD_static },
+ { "class", KEYWORD_class },
+ { "id", KEYWORD_id },
+ { "script", KEYWORD_script },
+ { "cdata", KEYWORD_cdata },
+ { "mx", KEYWORD_mx }
+};
+
+/*
+ * FUNCTION DEFINITIONS
+ */
+
+/* Recursive functions */
+static void parseFunction (tokenInfo *const token);
+static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
+static boolean parseLine (tokenInfo *const token);
+static boolean parseActionScript (tokenInfo *const token);
+
+static boolean isIdentChar (const int c)
+{
+ return (boolean)
+ (isalpha (c) || isdigit (c) || c == '$' ||
+ c == '@' || c == '_' || c == '#');
+}
+
+static void buildFlexKeywordHash (void)
+{
+ const size_t count = sizeof (FlexKeywordTable) /
+ sizeof (FlexKeywordTable [0]);
+ size_t i;
+ for (i = 0 ; i < count ; ++i)
+ {
+ const keywordDesc* const p = &FlexKeywordTable [i];
+ addKeyword (p->name, Lang_js, (int) p->id);
+ }
+}
+
+static tokenInfo *newToken (void)
+{
+ tokenInfo *const token = xMalloc (1, tokenInfo);
+
+ token->type = TOKEN_UNDEFINED;
+ token->keyword = KEYWORD_NONE;
+ token->string = vStringNew ();
+ token->scope = vStringNew ();
+ token->nestLevel = 0;
+ token->isClass = FALSE;
+ token->ignoreTag = FALSE;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+
+ return token;
+}
+
+static void deleteToken (tokenInfo *const token)
+{
+ vStringDelete (token->string);
+ vStringDelete (token->scope);
+ eFree (token);
+}
+
+/*
+ * Tag generation functions
+ */
+
+static void makeConstTag (tokenInfo *const token, const flexKind kind)
+{
+ if (FlexKinds [kind].enabled && ! token->ignoreTag )
+ {
+ const char *const name = vStringValue (token->string);
+ tagEntryInfo e;
+ initTagEntry (&e, name);
+
+ e.lineNumber = token->lineNumber;
+ e.filePosition = token->filePosition;
+ e.kindName = FlexKinds [kind].name;
+ e.kind = FlexKinds [kind].letter;
+
+ makeTagEntry (&e);
+ }
+}
+
+static void makeFlexTag (tokenInfo *const token, flexKind kind)
+{
+ vString * fulltag;
+
+ if (FlexKinds [kind].enabled && ! token->ignoreTag )
+ {
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n makeFlexTag start: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ if (kind == FLEXTAG_FUNCTION && token->isClass )
+ {
+ kind = FLEXTAG_METHOD;
+ }
+ /*
+ * If a scope has been added to the token, change the token
+ * string to include the scope when making the tag.
+ */
+ if ( vStringLength(token->scope) > 0 )
+ {
+ fulltag = vStringNew ();
+ vStringCopy(fulltag, token->scope);
+ vStringCatS (fulltag, ".");
+ vStringCatS (fulltag, vStringValue(token->string));
+ vStringTerminate(fulltag);
+ vStringCopy(token->string, fulltag);
+ vStringDelete (fulltag);
+ }
+ makeConstTag (token, kind);
+ }
+}
+
+static void makeClassTag (tokenInfo *const token)
+{
+ vString * fulltag;
+
+ if ( ! token->ignoreTag )
+ {
+ fulltag = vStringNew ();
+ if (vStringLength (token->scope) > 0)
+ {
+ vStringCopy(fulltag, token->scope);
+ vStringCatS (fulltag, ".");
+ vStringCatS (fulltag, vStringValue(token->string));
+ }
+ else
+ {
+ vStringCopy(fulltag, token->string);
+ }
+ vStringTerminate(fulltag);
+ if ( ! stringListHas(ClassNames, vStringValue (fulltag)) )
+ {
+ stringListAdd (ClassNames, vStringNewCopy (fulltag));
+ makeFlexTag (token, FLEXTAG_CLASS);
+ }
+ vStringDelete (fulltag);
+ }
+}
+
+static void makeMXTag (tokenInfo *const token)
+{
+ vString * fulltag;
+
+ if ( ! token->ignoreTag )
+ {
+ fulltag = vStringNew ();
+ if (vStringLength (token->scope) > 0)
+ {
+ vStringCopy(fulltag, token->scope);
+ vStringCatS (fulltag, ".");
+ vStringCatS (fulltag, vStringValue(token->string));
+ }
+ else
+ {
+ vStringCopy(fulltag, token->string);
+ }
+ vStringTerminate(fulltag);
+ makeFlexTag (token, FLEXTAG_MXTAG);
+ vStringDelete (fulltag);
+ }
+}
+
+static void makeFunctionTag (tokenInfo *const token)
+{
+ vString * fulltag;
+
+ if ( ! token->ignoreTag )
+ {
+ fulltag = vStringNew ();
+ if (vStringLength (token->scope) > 0)
+ {
+ vStringCopy(fulltag, token->scope);
+ vStringCatS (fulltag, ".");
+ vStringCatS (fulltag, vStringValue(token->string));
+ }
+ else
+ {
+ vStringCopy(fulltag, token->string);
+ }
+ vStringTerminate(fulltag);
+ if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) )
+ {
+ stringListAdd (FunctionNames, vStringNewCopy (fulltag));
+ makeFlexTag (token, FLEXTAG_FUNCTION);
+ }
+ vStringDelete (fulltag);
+ }
+}
+
+/*
+ * Parsing functions
+ */
+
+static void parseString (vString *const string, const int delimiter)
+{
+ boolean end = FALSE;
+ while (! end)
+ {
+ int c = fileGetc ();
+ if (c == EOF)
+ end = TRUE;
+ else if (c == '\\')
+ {
+ c = fileGetc(); /* This maybe a ' or ". */
+ vStringPut(string, c);
+ }
+ else if (c == delimiter)
+ end = TRUE;
+ else
+ vStringPut (string, c);
+ }
+ vStringTerminate (string);
+}
+
+/* Read a C identifier beginning with "firstChar" and places it into
+ * "name".
+ */
+static void parseIdentifier (vString *const string, const int firstChar)
+{
+ int c = firstChar;
+ Assert (isIdentChar (c));
+ do
+ {
+ vStringPut (string, c);
+ c = fileGetc ();
+ } while (isIdentChar (c));
+ vStringTerminate (string);
+ if (!isspace (c))
+ fileUngetc (c); /* unget non-identifier character */
+}
+
+static void readToken (tokenInfo *const token)
+{
+ int c;
+
+ token->type = TOKEN_UNDEFINED;
+ token->keyword = KEYWORD_NONE;
+ vStringClear (token->string);
+
+getNextChar:
+ do
+ {
+ c = fileGetc ();
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ while (c == '\t' || c == ' ' || c == '\n');
+
+ switch (c)
+ {
+ case EOF: longjmp (Exception, (int)ExceptionEOF); break;
+ case '(': token->type = TOKEN_OPEN_PAREN; break;
+ case ')': token->type = TOKEN_CLOSE_PAREN; break;
+ case ';': token->type = TOKEN_SEMICOLON; break;
+ case ',': token->type = TOKEN_COMMA; break;
+ case '.': token->type = TOKEN_PERIOD; break;
+ case ':': token->type = TOKEN_COLON; break;
+ case '{': token->type = TOKEN_OPEN_CURLY; break;
+ case '}': token->type = TOKEN_CLOSE_CURLY; break;
+ case '=': token->type = TOKEN_EQUAL_SIGN; break;
+ case '[': token->type = TOKEN_OPEN_SQUARE; break;
+ case ']': token->type = TOKEN_CLOSE_SQUARE; break;
+ case '?': token->type = TOKEN_QUESTION_MARK; break;
+
+ case '\'':
+ case '"':
+ token->type = TOKEN_STRING;
+ parseString (token->string, c);
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ break;
+
+ case '\\':
+ c = fileGetc ();
+ if (c != '\\' && c != '"' && !isspace (c))
+ fileUngetc (c);
+ token->type = TOKEN_CHARACTER;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ break;
+
+ case '/':
+ {
+ int d = fileGetc ();
+ if ( (d != '*') && /* is this the start of a comment? */
+ (d != '/') && /* is a one line comment? */
+ (d != '>') ) /* is this a close XML tag? */
+ {
+ fileUngetc (d);
+ token->type = TOKEN_FORWARD_SLASH;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (d == '*')
+ {
+ do
+ {
+ fileSkipToCharacter ('*');
+ c = fileGetc ();
+ if (c == '/')
+ break;
+ else
+ fileUngetc (c);
+ } while (c != EOF && c != '\0');
+ goto getNextChar;
+ }
+ else if (d == '/') /* is this the start of a comment? */
+ {
+ fileSkipToCharacter ('\n');
+ goto getNextChar;
+ }
+ else if (d == '>') /* is this the start of a comment? */
+ {
+ token->type = TOKEN_CLOSE_SGML;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ }
+ break;
+ }
+
+ case '<':
+ {
+ /*
+ * An XML comment looks like this
+ * <!-- anything over multiple lines -->
+ */
+ int d = fileGetc ();
+
+ if ( (d != '!' ) && /* is this the start of a comment? */
+ (d != '/' ) && /* is this the start of a closing mx tag */
+ (d != 'm' ) ) /* is this the start of a mx tag */
+ {
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+
+ }
+ else
+ {
+ if (d == '!')
+ {
+ int e = fileGetc ();
+ if ( e != '-' ) /* is this the start of a comment? */
+ {
+ fileUngetc (e);
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (e == '-')
+ {
+ int f = fileGetc ();
+ if ( f != '-' ) /* is this the start of a comment? */
+ {
+ fileUngetc (f);
+ fileUngetc (e);
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (f == '-')
+ {
+ do
+ {
+ fileSkipToCharacter ('-');
+ c = fileGetc ();
+ if (c == '-')
+ {
+ d = fileGetc ();
+ if (d == '>')
+ break;
+ else
+ {
+ fileUngetc (d);
+ fileUngetc (c);
+ }
+ break;
+ }
+ else
+ fileUngetc (c);
+ } while (c != EOF && c != '\0');
+ goto getNextChar;
+ }
+ }
+ }
+ }
+ }
+ else if (d == 'm')
+ {
+ int e = fileGetc ();
+ if ( e != 'x' ) /* continuing an mx tag */
+ {
+ fileUngetc (e);
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (e == 'x')
+ {
+ int f = fileGetc ();
+ if ( f != ':' ) /* is this the start of a comment? */
+ {
+ fileUngetc (f);
+ fileUngetc (e);
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (f == ':')
+ {
+ token->type = TOKEN_OPEN_MXML;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ }
+ }
+ }
+ }
+ else if (d == '/')
+ {
+ int e = fileGetc ();
+ if ( e != 'm' ) /* continuing an mx tag */
+ {
+ fileUngetc (e);
+ fileUngetc (d);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ int f = fileGetc ();
+ if ( f != 'x' ) /* continuing an mx tag */
+ {
+ fileUngetc (f);
+ fileUngetc (e);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (f == 'x')
+ {
+ int g = fileGetc ();
+ if ( g != ':' ) /* is this the start of a comment? */
+ {
+ fileUngetc (g);
+ fileUngetc (f);
+ fileUngetc (e);
+ token->type = TOKEN_LESS_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ else
+ {
+ if (g == ':')
+ {
+ token->type = TOKEN_CLOSE_MXML;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case '>':
+ token->type = TOKEN_GREATER_THAN;
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ break;
+
+ case '!':
+ token->type = TOKEN_EXCLAMATION;
+ /*token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();*/
+ break;
+
+ default:
+ if (! isIdentChar (c))
+ token->type = TOKEN_UNDEFINED;
+ else
+ {
+ parseIdentifier (token->string, c);
+ token->lineNumber = getSourceLineNumber ();
+ token->filePosition = getInputFilePosition ();
+ token->keyword = analyzeToken (token->string, Lang_js);
+ if (isKeyword (token, KEYWORD_NONE))
+ token->type = TOKEN_IDENTIFIER;
+ else
+ token->type = TOKEN_KEYWORD;
+ }
+ break;
+ }
+}
+
+static void copyToken (tokenInfo *const dest, tokenInfo *const src)
+{
+ dest->nestLevel = src->nestLevel;
+ dest->lineNumber = src->lineNumber;
+ dest->filePosition = src->filePosition;
+ dest->type = src->type;
+ dest->keyword = src->keyword;
+ dest->isClass = src->isClass;
+ vStringCopy(dest->string, src->string);
+ vStringCopy(dest->scope, src->scope);
+}
+
+/*
+ * Token parsing functions
+ */
+
+static void skipArgumentList (tokenInfo *const token)
+{
+ int nest_level = 0;
+
+ /*
+ * Other databases can have arguments with fully declared
+ * datatypes:
+ * ( name varchar(30), text binary(10) )
+ * So we must check for nested open and closing parantheses
+ */
+
+ if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
+ {
+ nest_level++;
+ while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ nest_level++;
+ }
+ if (isType (token, TOKEN_CLOSE_PAREN))
+ {
+ if (nest_level > 0)
+ {
+ nest_level--;
+ }
+ }
+ }
+ readToken (token);
+ }
+}
+
+static void skipArrayList (tokenInfo *const token)
+{
+ int nest_level = 0;
+
+ /*
+ * Handle square brackets
+ * var name[1]
+ * So we must check for nested open and closing square brackets
+ */
+
+ if (isType (token, TOKEN_OPEN_SQUARE)) /* arguments? */
+ {
+ nest_level++;
+ while (! (isType (token, TOKEN_CLOSE_SQUARE) && (nest_level == 0)))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_OPEN_SQUARE))
+ {
+ nest_level++;
+ }
+ if (isType (token, TOKEN_CLOSE_SQUARE))
+ {
+ if (nest_level > 0)
+ {
+ nest_level--;
+ }
+ }
+ }
+ readToken (token);
+ }
+}
+
+static void addContext (tokenInfo* const parent, const tokenInfo* const child)
+{
+ if (vStringLength (parent->string) > 0)
+ {
+ vStringCatS (parent->string, ".");
+ }
+ vStringCatS (parent->string, vStringValue(child->string));
+ vStringTerminate(parent->string);
+}
+
+static void addToScope (tokenInfo* const token, vString* const extra)
+{
+ if (vStringLength (token->scope) > 0)
+ {
+ vStringCatS (token->scope, ".");
+ }
+ vStringCatS (token->scope, vStringValue(extra));
+ vStringTerminate(token->scope);
+}
+
+/*
+ * Scanning functions
+ */
+
+static void findCmdTerm (tokenInfo *const token)
+{
+ /*
+ * Read until we find either a semicolon or closing brace.
+ * Any nested braces will be handled within.
+ */
+ while (! ( isType (token, TOKEN_SEMICOLON) ||
+ isType (token, TOKEN_CLOSE_CURLY) ) )
+ {
+ /* Handle nested blocks */
+ if ( isType (token, TOKEN_OPEN_CURLY))
+ {
+ parseBlock (token, token);
+ }
+ else if ( isType (token, TOKEN_OPEN_PAREN) )
+ {
+ skipArgumentList(token);
+ }
+ else
+ {
+ readToken (token);
+ }
+ }
+}
+
+static void parseSwitch (tokenInfo *const token)
+{
+ /*
+ * switch (expression){
+ * case value1:
+ * statement;
+ * break;
+ * case value2:
+ * statement;
+ * break;
+ * default : statement;
+ * }
+ */
+
+ readToken (token);
+
+ if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ skipArgumentList(token);
+ }
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ do
+ {
+ readToken (token);
+ } while (! (isType (token, TOKEN_CLOSE_SGML) ||
+ isType (token, TOKEN_CLOSE_MXML) ||
+ isType (token, TOKEN_CLOSE_CURLY) ||
+ isType (token, TOKEN_GREATER_THAN)) );
+ }
+
+}
+
+static void parseLoop (tokenInfo *const token)
+{
+ /*
+ * Handles these statements
+ * for (x=0; x<3; x++)
+ * document.write("This text is repeated three times<br>");
+ *
+ * for (x=0; x<3; x++)
+ * {
+ * document.write("This text is repeated three times<br>");
+ * }
+ *
+ * while (number<5){
+ * document.write(number+"<br>");
+ * number++;
+ * }
+ *
+ * do{
+ * document.write(number+"<br>");
+ * number++;
+ * }
+ * while (number<5);
+ */
+
+ if (isKeyword (token, KEYWORD_for) || isKeyword (token, KEYWORD_while))
+ {
+ readToken(token);
+
+ if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ /*
+ * Handle nameless functions, these will only
+ * be considered methods.
+ */
+ skipArgumentList(token);
+ }
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * This will be either a function or a class.
+ * We can only determine this by checking the body
+ * of the function. If we find a "this." we know
+ * it is a class, otherwise it is a function.
+ */
+ parseBlock (token, token);
+ }
+ else
+ {
+ parseLine(token);
+ }
+ }
+ else if (isKeyword (token, KEYWORD_do))
+ {
+ readToken(token);
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * This will be either a function or a class.
+ * We can only determine this by checking the body
+ * of the function. If we find a "this." we know
+ * it is a class, otherwise it is a function.
+ */
+ parseBlock (token, token);
+ }
+ else
+ {
+ parseLine(token);
+ }
+
+ readToken(token);
+
+ if (isKeyword (token, KEYWORD_while))
+ {
+ readToken(token);
+
+ if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ /*
+ * Handle nameless functions, these will only
+ * be considered methods.
+ */
+ skipArgumentList(token);
+ }
+ }
+ }
+}
+
+static boolean parseIf (tokenInfo *const token)
+{
+ boolean read_next_token = TRUE;
+ /*
+ * If statements have two forms
+ * if ( ... )
+ * one line;
+ *
+ * if ( ... )
+ * statement;
+ * else
+ * statement
+ *
+ * if ( ... ) {
+ * multiple;
+ * statements;
+ * }
+ *
+ *
+ * if ( ... ) {
+ * return elem
+ * }
+ *
+ * This example if correctly written, but the
+ * else contains only 1 statement without a terminator
+ * since the function finishes with the closing brace.
+ *
+ * function a(flag){
+ * if(flag)
+ * test(1);
+ * else
+ * test(2)
+ * }
+ *
+ * TODO: Deal with statements that can optional end
+ * without a semi-colon. Currently this messes up
+ * the parsing of blocks.
+ * Need to somehow detect this has happened, and either
+ * backup a token, or skip reading the next token if
+ * that is possible from all code locations.
+ *
+ */
+
+ readToken (token);
+
+ if (isKeyword (token, KEYWORD_if))
+ {
+ /*
+ * Check for an "else if" and consume the "if"
+ */
+ readToken (token);
+ }
+
+ if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ /*
+ * Handle nameless functions, these will only
+ * be considered methods.
+ */
+ skipArgumentList(token);
+ }
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * This will be either a function or a class.
+ * We can only determine this by checking the body
+ * of the function. If we find a "this." we know
+ * it is a class, otherwise it is a function.
+ */
+ parseBlock (token, token);
+ }
+ else
+ {
+ findCmdTerm (token);
+
+ /*
+ * The IF could be followed by an ELSE statement.
+ * This too could have two formats, a curly braced
+ * multiline section, or another single line.
+ */
+
+ if (isType (token, TOKEN_CLOSE_CURLY))
+ {
+ /*
+ * This statement did not have a line terminator.
+ */
+ read_next_token = FALSE;
+ }
+ else
+ {
+ readToken (token);
+
+ if (isType (token, TOKEN_CLOSE_CURLY))
+ {
+ /*
+ * This statement did not have a line terminator.
+ */
+ read_next_token = FALSE;
+ }
+ else
+ {
+ if (isKeyword (token, KEYWORD_else))
+ read_next_token = parseIf (token);
+ }
+ }
+ }
+ return read_next_token;
+}
+
+static void parseFunction (tokenInfo *const token)
+{
+ tokenInfo *const name = newToken ();
+
+ /*
+ * This deals with these formats
+ * private static function ioErrorHandler( event:IOErrorEvent ):void {
+ */
+
+ if ( isKeyword(token, KEYWORD_function) )
+ {
+ readToken (token);
+ }
+
+ copyToken (name, token);
+ /* Add scope in case this is an INNER function
+ addToScope(name, token->scope);
+ */
+
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction: name isClass:%d scope:%s name:%s\n"
+ , name->isClass
+ , vStringValue(name->scope)
+ , vStringValue(name->string)
+ );
+ );
+
+ readToken (token);
+
+ if ( isType (token, TOKEN_OPEN_PAREN) )
+ skipArgumentList(token);
+
+ if ( isType (token, TOKEN_COLON) )
+ {
+ /*
+ * function fname ():ReturnType
+ */
+ readToken (token);
+ readToken (token);
+ }
+
+ if ( isType (token, TOKEN_OPEN_CURLY) )
+ {
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction end: name isClass:%d scope:%s name:%s\n"
+ , name->isClass
+ , vStringValue(name->scope)
+ , vStringValue(name->string)
+ );
+ );
+ parseBlock (token, name);
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseFunction end3: name isClass:%d scope:%s name:%s\n"
+ , name->isClass
+ , vStringValue(name->scope)
+ , vStringValue(name->string)
+ );
+ );
+ makeFunctionTag (name);
+ }
+
+ findCmdTerm (token);
+
+ deleteToken (name);
+}
+
+static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
+{
+ boolean read_next_token = TRUE;
+ vString * saveScope = vStringNew ();
+
+ vStringClear(saveScope);
+ vStringCopy (saveScope, token->scope);
+ token->nestLevel++;
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseBlock start: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ /*
+ * Make this routine a bit more forgiving.
+ * If called on an open_curly advance it
+ */
+ if ( isType (token, TOKEN_OPEN_CURLY) &&
+ isKeyword(token, KEYWORD_NONE) )
+ readToken(token);
+
+ if (! isType (token, TOKEN_CLOSE_CURLY))
+ {
+ /*
+ * Read until we find the closing brace,
+ * any nested braces will be handled within
+ */
+ do
+ {
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /* Handle nested blocks */
+ parseBlock (token, parent);
+ }
+ else
+ {
+ /*
+ * It is possible for a line to have no terminator
+ * if the following line is a closing brace.
+ * parseLine will detect this case and indicate
+ * whether we should read an additional token.
+ */
+ read_next_token = parseLine (token);
+ }
+
+ /*
+ * Always read a new token unless we find a statement without
+ * a ending terminator
+ */
+ if( read_next_token )
+ readToken(token);
+
+ /*
+ * If we find a statement without a terminator consider the
+ * block finished, otherwise the stack will be off by one.
+ */
+ } while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token );
+ }
+
+ vStringDelete(saveScope);
+ token->nestLevel--;
+
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseBlock end: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ return FALSE;
+}
+
+static void parseMethods (tokenInfo *const token, tokenInfo *const class)
+{
+ tokenInfo *const name = newToken ();
+
+ /*
+ * This deals with these formats
+ * validProperty : 2,
+ * validMethod : function(a,b) {}
+ * 'validMethod2' : function(a,b) {}
+ * container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false}
+ */
+
+ do
+ {
+ readToken (token);
+ if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE))
+ {
+ copyToken (name, token);
+
+ readToken (token);
+ if ( isType (token, TOKEN_COLON) )
+ {
+ readToken (token);
+ if ( isKeyword (token, KEYWORD_function) )
+ {
+ readToken (token);
+ if ( isType (token, TOKEN_OPEN_PAREN) )
+ {
+ skipArgumentList(token);
+ }
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ addToScope (name, class->string);
+ makeFlexTag (name, FLEXTAG_METHOD);
+ parseBlock (token, name);
+
+ /*
+ * Read to the closing curly, check next
+ * token, if a comma, we must loop again
+ */
+ readToken (token);
+ }
+ }
+ else
+ {
+ addToScope (name, class->string);
+ makeFlexTag (name, FLEXTAG_PROPERTY);
+
+ /*
+ * Read the next token, if a comma
+ * we must loop again
+ */
+ readToken (token);
+ }
+ }
+ }
+ } while ( isType(token, TOKEN_COMMA) );
+
+ findCmdTerm (token);
+
+ deleteToken (name);
+}
+
+static boolean parseVar (tokenInfo *const token, boolean is_public)
+{
+ tokenInfo *const name = newToken ();
+ tokenInfo *const secondary_name = newToken ();
+ vString * saveScope = vStringNew ();
+ boolean is_terminated = TRUE;
+
+ vStringClear(saveScope);
+ vStringCopy (saveScope, token->scope);
+ /*
+ * Variables are defined as:
+ * private static var lastFaultMessage:Date = new Date( 0 );
+ * private static var webRequests:ArrayCollection = new ArrayCollection();
+ */
+
+ if ( isKeyword(token, KEYWORD_var) )
+ {
+ readToken(token);
+ }
+
+ /* Variable name */
+ copyToken (name, token);
+ readToken(token);
+
+ if ( isType (token, TOKEN_COLON) )
+ {
+ /*
+ * var vname ():DataType = new Date();
+ * var vname ():DataType;
+ */
+ readToken (token);
+ readToken (token);
+ }
+
+ while (! isType (token, TOKEN_SEMICOLON) )
+ {
+ readToken (token);
+ }
+
+ if ( isType (token, TOKEN_SEMICOLON) )
+ {
+ /*
+ * Only create variables for global scope
+ */
+ /* if ( token->nestLevel == 0 && is_global ) */
+ if ( is_public )
+ {
+ if (isType (token, TOKEN_SEMICOLON))
+ makeFlexTag (name, FLEXTAG_VARIABLE);
+ }
+ }
+
+ vStringCopy(token->scope, saveScope);
+ deleteToken (name);
+ deleteToken (secondary_name);
+ vStringDelete(saveScope);
+
+ return is_terminated;
+}
+
+static boolean parseClass (tokenInfo *const token)
+{
+ tokenInfo *const name = newToken ();
+ vString * saveScope = vStringNew ();
+ boolean saveIsClass = token->isClass;
+
+ vStringClear(saveScope);
+ vStringCopy (saveScope, token->scope);
+ /*
+ * Variables are defined as:
+ * private static var lastFaultMessage:Date = new Date( 0 );
+ * private static var webRequests:ArrayCollection = new ArrayCollection();
+ */
+
+ if ( isKeyword(token, KEYWORD_class) )
+ {
+ readToken(token);
+ }
+
+ token->isClass = TRUE;
+ /* Add class name to scope */
+ addToScope(token, token->string);
+ /* Class name */
+ copyToken (name, token);
+ readToken(token);
+
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseClass start: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ if ( isType (token, TOKEN_OPEN_CURLY) )
+ {
+ makeClassTag (name);
+ parseBlock (token, name);
+ }
+
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseClass end: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ vStringCopy(token->scope, saveScope);
+ token->isClass = saveIsClass;
+ deleteToken (name);
+ vStringDelete(saveScope);
+
+ return TRUE;
+}
+
+static boolean parseStatement (tokenInfo *const token)
+{
+ tokenInfo *const name = newToken ();
+ tokenInfo *const secondary_name = newToken ();
+ vString * saveScope = vStringNew ();
+ boolean is_public = FALSE;
+ boolean is_class = FALSE;
+ boolean is_terminated = TRUE;
+ boolean is_global = FALSE;
+ boolean is_prototype = FALSE;
+ vString * fulltag;
+
+ vStringClear(saveScope);
+ vStringCopy (saveScope, token->scope);
+ DebugStatement (
+ debugPrintf (DEBUG_PARSE
+ , "\n parseStatement: token isClass:%d scope:%s name:%s\n"
+ , token->isClass
+ , vStringValue(token->scope)
+ , vStringValue(token->string)
+ );
+ );
+ /*
+ * Functions can be named or unnamed.
+ * This deals with these formats:
+ * Function
+ * validFunctionOne = function(a,b) {}
+ * testlib.validFunctionFive = function(a,b) {}
+ * var innerThree = function(a,b) {}
+ * var innerFour = (a,b) {}
+ * var D2 = secondary_fcn_name(a,b) {}
+ * var D3 = new Function("a", "b", "return a+b;");
+ * Class
+ * testlib.extras.ValidClassOne = function(a,b) {
+ * this.a = a;
+ * }
+ * Class Methods
+ * testlib.extras.ValidClassOne.prototype = {
+ * 'validMethodOne' : function(a,b) {},
+ * 'validMethodTwo' : function(a,b) {}
+ * }
+ * ValidClassTwo = function ()
+ * {
+ * this.validMethodThree = function() {}
+ * // unnamed method
+ * this.validMethodFour = () {}
+ * }
+ * Database.prototype.validMethodThree = Database_getTodaysDate;
+ */
+
+ if ( isKeyword(token, KEYWORD_public) )
+ {
+ is_public = TRUE;
+ readToken(token);
+ }
+
+ if ( isKeyword(token, KEYWORD_private) )
+ {
+ readToken(token);
+ }
+
+ if ( isKeyword(token, KEYWORD_static) )
+ {
+ readToken(token);
+ }
+
+ if (isType(token, TOKEN_KEYWORD))
+ {
+ switch (token->keyword)
+ {
+ case KEYWORD_for:
+ case KEYWORD_while:
+ case KEYWORD_do:
+ parseLoop (token);
+ break;
+ case KEYWORD_if:
+ case KEYWORD_else:
+ case KEYWORD_try:
+ case KEYWORD_catch:
+ case KEYWORD_finally:
+ /* Common semantics */
+ is_terminated = parseIf (token);
+ break;
+ case KEYWORD_switch:
+ parseSwitch (token);
+ break;
+ case KEYWORD_class:
+ parseClass (token);
+ return is_terminated;
+ break;
+ case KEYWORD_function:
+ parseFunction (token);
+ return is_terminated;
+ break;
+ case KEYWORD_var:
+ parseVar (token, is_public);
+ return is_terminated;
+ break;
+ default:
+ readToken(token);
+ break;
+ }
+ }
+
+ copyToken (name, token);
+
+ while (! isType (token, TOKEN_CLOSE_CURLY) &&
+ ! isType (token, TOKEN_SEMICOLON) &&
+ ! isType (token, TOKEN_EQUAL_SIGN) )
+ {
+ /* Potentially the name of the function */
+ readToken (token);
+ if (isType (token, TOKEN_PERIOD))
+ {
+ /*
+ * Cannot be a global variable is it has dot references in the name
+ */
+ is_global = FALSE;
+ do
+ {
+ readToken (token);
+ if ( isKeyword(token, KEYWORD_NONE) )
+ {
+ if ( is_class )
+ {
+ vStringCopy(saveScope, token->scope);
+ addToScope(token, name->string);
+ }
+ else
+ addContext (name, token);
+ }
+ else if ( isKeyword(token, KEYWORD_prototype) )
+ {
+ /*
+ * When we reach the "prototype" tag, we infer:
+ * "BindAgent" is a class
+ * "build" is a method
+ *
+ * function BindAgent( repeatableIdName, newParentIdName ) {
+ * }
+ *
+ * CASE 1
+ * Specified function name: "build"
+ * BindAgent.prototype.build = function( mode ) {
+ * ignore everything within this function
+ * }
+ *
+ * CASE 2
+ * Prototype listing
+ * ValidClassOne.prototype = {
+ * 'validMethodOne' : function(a,b) {},
+ * 'validMethodTwo' : function(a,b) {}
+ * }
+ *
+ */
+ makeClassTag (name);
+ is_class = TRUE;
+ is_prototype = TRUE;
+
+ /*
+ * There should a ".function_name" next.
+ */
+ readToken (token);
+ if (isType (token, TOKEN_PERIOD))
+ {
+ /*
+ * Handle CASE 1
+ */
+ readToken (token);
+ if ( isKeyword(token, KEYWORD_NONE) )
+ {
+ vStringCopy(saveScope, token->scope);
+ addToScope(token, name->string);
+
+ makeFlexTag (token, FLEXTAG_METHOD);
+ /*
+ * We can read until the end of the block / statement.
+ * We need to correctly parse any nested blocks, but
+ * we do NOT want to create any tags based on what is
+ * within the blocks.
+ */
+ token->ignoreTag = TRUE;
+ /*
+ * Find to the end of the statement
+ */
+ findCmdTerm (token);
+ token->ignoreTag = FALSE;
+ is_terminated = TRUE;
+ goto cleanUp;
+ }
+ }
+ else if (isType (token, TOKEN_EQUAL_SIGN))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * Handle CASE 2
+ *
+ * Creates tags for each of these class methods
+ * ValidClassOne.prototype = {
+ * 'validMethodOne' : function(a,b) {},
+ * 'validMethodTwo' : function(a,b) {}
+ * }
+ */
+ parseMethods(token, name);
+ /*
+ * Find to the end of the statement
+ */
+ findCmdTerm (token);
+ token->ignoreTag = FALSE;
+ is_terminated = TRUE;
+ goto cleanUp;
+ }
+ }
+ }
+ readToken (token);
+ } while (isType (token, TOKEN_PERIOD));
+ }
+
+ if ( isType (token, TOKEN_OPEN_PAREN) )
+ skipArgumentList(token);
+
+ if ( isType (token, TOKEN_COLON) )
+ {
+ /*
+ * Functions are of this form:
+ * function fname ():ReturnType {
+ */
+ readToken (token);
+ readToken (token);
+ }
+
+ if ( isType (token, TOKEN_OPEN_SQUARE) )
+ skipArrayList(token);
+
+ }
+
+ if ( isType (token, TOKEN_CLOSE_CURLY) )
+ {
+ /*
+ * Reaching this section without having
+ * processed an open curly brace indicates
+ * the statement is most likely not terminated.
+ */
+ is_terminated = FALSE;
+ goto cleanUp;
+ }
+
+ if ( isType (token, TOKEN_SEMICOLON) )
+ {
+ /*
+ * Only create variables for global scope
+ */
+ if ( token->nestLevel == 0 && is_global )
+ {
+ /*
+ * Handles this syntax:
+ * var g_var2;
+ */
+ if (isType (token, TOKEN_SEMICOLON))
+ makeFlexTag (name, FLEXTAG_VARIABLE);
+ }
+ /*
+ * Statement has ended.
+ * This deals with calls to functions, like:
+ * alert(..);
+ */
+ goto cleanUp;
+ }
+
+ if ( isType (token, TOKEN_EQUAL_SIGN) )
+ {
+ readToken (token);
+
+ if ( isKeyword (token, KEYWORD_function) )
+ {
+ readToken (token);
+
+ if ( isKeyword (token, KEYWORD_NONE) &&
+ ! isType (token, TOKEN_OPEN_PAREN) )
+ {
+ /*
+ * Functions of this format:
+ * var D2A = function theAdd(a, b)
+ * {
+ * return a+b;
+ * }
+ * Are really two separate defined functions and
+ * can be referenced in two ways:
+ * alert( D2A(1,2) ); // produces 3
+ * alert( theAdd(1,2) ); // also produces 3
+ * So it must have two tags:
+ * D2A
+ * theAdd
+ * Save the reference to the name for later use, once
+ * we have established this is a valid function we will
+ * create the secondary reference to it.
+ */
+ copyToken (secondary_name, token);
+ readToken (token);
+ }
+
+ if ( isType (token, TOKEN_OPEN_PAREN) )
+ skipArgumentList(token);
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * This will be either a function or a class.
+ * We can only determine this by checking the body
+ * of the function. If we find a "this." we know
+ * it is a class, otherwise it is a function.
+ */
+ if ( token->isClass )
+ {
+ makeFlexTag (name, FLEXTAG_METHOD);
+ if ( vStringLength(secondary_name->string) > 0 )
+ makeFunctionTag (secondary_name);
+ parseBlock (token, name);
+ }
+ else
+ {
+ parseBlock (token, name);
+ makeFunctionTag (name);
+
+ if ( vStringLength(secondary_name->string) > 0 )
+ makeFunctionTag (secondary_name);
+
+ /*
+ * Find to the end of the statement
+ */
+ goto cleanUp;
+ }
+ }
+ }
+ else if (isType (token, TOKEN_OPEN_PAREN))
+ {
+ /*
+ * Handle nameless functions
+ * this.method_name = () {}
+ */
+ skipArgumentList(token);
+
+ if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * Nameless functions are only setup as methods.
+ */
+ makeFlexTag (name, FLEXTAG_METHOD);
+ parseBlock (token, name);
+ }
+ }
+ else if (isType (token, TOKEN_OPEN_CURLY))
+ {
+ /*
+ * Creates tags for each of these class methods
+ * ValidClassOne.prototype = {
+ * 'validMethodOne' : function(a,b) {},
+ * 'validMethodTwo' : function(a,b) {}
+ * }
+ */
+ parseMethods(token, name);
+ if (isType (token, TOKEN_CLOSE_CURLY))
+ {
+ /*
+ * Assume the closing parantheses terminates
+ * this statements.
+ */
+ is_terminated = TRUE;
+ }
+ }
+ else if (isKeyword (token, KEYWORD_new))
+ {
+ readToken (token);
+ if ( isKeyword (token, KEYWORD_function) ||
+ isKeyword (token, KEYWORD_capital_function) ||
+ isKeyword (token, KEYWORD_object) ||
+ isKeyword (token, KEYWORD_capital_object) )
+ {
+ if ( isKeyword (token, KEYWORD_object) ||
+ isKeyword (token, KEYWORD_capital_object) )
+ is_class = TRUE;
+
+ readToken (token);
+ if ( isType (token, TOKEN_OPEN_PAREN) )
+ skipArgumentList(token);
+
+ if (isType (token, TOKEN_SEMICOLON))
+ {
+ if ( token->nestLevel == 0 )
+ {
+ if ( is_class )
+ {
+ makeClassTag (name);
+ } else {
+ makeFunctionTag (name);
+ }
+ }
+ }
+ }
+ }
+ else if (isKeyword (token, KEYWORD_NONE))
+ {
+ /*
+ * Only create variables for global scope
+ */
+ if ( token->nestLevel == 0 && is_global )
+ {
+ /*
+ * A pointer can be created to the function.
+ * If we recognize the function/class name ignore the variable.
+ * This format looks identical to a variable definition.
+ * A variable defined outside of a block is considered
+ * a global variable:
+ * var g_var1 = 1;
+ * var g_var2;
+ * This is not a global variable:
+ * var g_var = function;
+ * This is a global variable:
+ * var g_var = different_var_name;
+ */
+ fulltag = vStringNew ();
+ if (vStringLength (token->scope) > 0)
+ {
+ vStringCopy(fulltag, token->scope);
+ vStringCatS (fulltag, ".");
+ vStringCatS (fulltag, vStringValue(token->string));
+ }
+ else
+ {
+ vStringCopy(fulltag, token->string);
+ }
+ vStringTerminate(fulltag);
+ if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
+ ! stringListHas(ClassNames, vStringValue (fulltag)) )
+ {
+ findCmdTerm (token);
+ if (isType (token, TOKEN_SEMICOLON))
+ makeFlexTag (name, FLEXTAG_VARIABLE);
+ }
+ vStringDelete (fulltag);
+ }
+ }
+ }
+ findCmdTerm (token);
+
+ /*
+ * Statements can be optionally terminated in the case of
+ * statement prior to a close curly brace as in the
+ * document.write line below:
+ *
+ * function checkForUpdate() {
+ * if( 1==1 ) {
+ * document.write("hello from checkForUpdate<br>")
+ * }
+ * return 1;
+ * }
+ */
+ if ( ! is_terminated && isType (token, TOKEN_CLOSE_CURLY))
+ is_terminated = FALSE;
+
+
+cleanUp:
+ vStringCopy(token->scope, saveScope);
+ deleteToken (name);
+ deleteToken (secondary_name);
+ vStringDelete(saveScope);
+
+ return is_terminated;
+}
+
+static boolean parseLine (tokenInfo *const token)
+{
+ boolean is_terminated = TRUE;
+ /*
+ * Detect the common statements, if, while, for, do, ...
+ * This is necessary since the last statement within a block "{}"
+ * can be optionally terminated.
+ *
+ * If the statement is not terminated, we need to tell
+ * the calling routine to prevent reading an additional token
+ * looking for the end of the statement.
+ */
+
+ if (isType(token, TOKEN_KEYWORD))
+ {
+ switch (token->keyword)
+ {
+ case KEYWORD_for:
+ case KEYWORD_while:
+ case KEYWORD_do:
+ parseLoop (token);
+ break;
+ case KEYWORD_if:
+ case KEYWORD_else:
+ case KEYWORD_try:
+ case KEYWORD_catch:
+ case KEYWORD_finally:
+ /* Common semantics */
+ is_terminated = parseIf (token);
+ break;
+ case KEYWORD_switch:
+ parseSwitch (token);
+ break;
+ default:
+ parseStatement (token);
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Special case where single line statements may not be
+ * SEMICOLON terminated. parseBlock needs to know this
+ * so that it does not read the next token.
+ */
+ is_terminated = parseStatement (token);
+ }
+ return is_terminated;
+}
+
+static boolean parseCDATA (tokenInfo *const token)
+{
+ if (isType (token, TOKEN_LESS_THAN))
+ {
+ /*
+ * Handle these tags
+ * <![CDATA[
+ * ...
+ * ]]>
+ */
+ readToken (token);
+ if (isType (token, TOKEN_EXCLAMATION))
+ {
+ /*
+ * Not sure why I had to comment these out, but I did.
+ * readToken (token);
+ * if (isType (token, TOKEN_OPEN_SQUARE))
+ * {
+ */
+ readToken (token);
+ if (isKeyword (token, KEYWORD_cdata))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_OPEN_SQUARE))
+ {
+ parseActionScript (token);
+ if (isType (token, TOKEN_CLOSE_SQUARE))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_CLOSE_SQUARE))
+ {
+ readToken (token);
+ }
+ }
+ }
+ }
+ /*} Not sure */
+ }
+ }
+ else
+ {
+ parseActionScript (token);
+ }
+ return TRUE;
+}
+
+static boolean parseMXML (tokenInfo *const token)
+{
+ tokenInfo *const name = newToken ();
+ tokenInfo *const type = newToken ();
+ /*
+ * Detect the common statements, if, while, for, do, ...
+ * This is necessary since the last statement within a block "{}"
+ * can be optionally terminated.
+ *
+ * If the statement is not terminated, we need to tell
+ * the calling routine to prevent reading an additional token
+ * looking for the end of the statement.
+ */
+
+ readToken (token);
+
+ if (isKeyword (token, KEYWORD_script))
+ {
+ /*
+ * These tags can be of this form:
+ * <mx:Script src="filename.as" />
+ */
+ do
+ {
+ readToken (token);
+ } while (! (isType (token, TOKEN_CLOSE_SGML) ||
+ isType (token, TOKEN_CLOSE_MXML) ||
+ isType (token, TOKEN_GREATER_THAN)) );
+
+ if (isType (token, TOKEN_CLOSE_MXML))
+ {
+ /*
+ * We have found a </mx:type> tag
+ * Finish reading the "type" and ">"
+ */
+ readToken (token);
+ readToken (token);
+ goto cleanUp;
+ }
+ if (isType (token, TOKEN_CLOSE_SGML))
+ {
+ /*
+ * We have found a <mx:Script src="filename.as" />
+ */
+ goto cleanUp;
+ }
+
+ /*
+ * This is a beginning of an embedded script.
+ * These typically are of this format:
+ * <mx:Script>
+ * <![CDATA[
+ * ... ActionScript ...
+ * ]]>
+ * </mx:Script>
+ */
+ readToken (token);
+ parseCDATA (token);
+
+ readToken (token);
+ if (isType (token, TOKEN_CLOSE_MXML))
+ {
+ /*
+ * We have found a </mx:type> tag
+ * Finish reading the "type" and ">"
+ */
+ readToken (token);
+ readToken (token);
+ }
+ goto cleanUp;
+ }
+
+ copyToken (type, token);
+
+ readToken (token);
+ do
+ {
+ if (isType (token, TOKEN_OPEN_MXML))
+ {
+ parseMXML (token);
+ }
+ else if (isKeyword (token, KEYWORD_id))
+ {
+ /* = */
+ readToken (token);
+ readToken (token);
+
+ copyToken (name, token);
+ addToScope (name, type->string);
+ makeMXTag (name);
+ }
+ readToken (token);
+ } while (! (isType (token, TOKEN_CLOSE_SGML) || isType (token, TOKEN_CLOSE_MXML)) );
+
+ if (isType (token, TOKEN_CLOSE_MXML))
+ {
+ /*
+ * We have found a </mx:type> tag
+ * Finish reading the "type" and ">"
+ */
+ readToken (token);
+ readToken (token);
+ }
+
+cleanUp:
+ deleteToken (name);
+ deleteToken (type);
+ return TRUE;
+}
+
+static boolean parseActionScript (tokenInfo *const token)
+{
+ do
+ {
+ readToken (token);
+
+ if (isType (token, TOKEN_LESS_THAN))
+ {
+ /*
+ * Handle these tags
+ * <![CDATA[
+ * ...
+ * ]]>
+ */
+ readToken (token);
+ if (isType (token, TOKEN_EQUAL_SIGN))
+ {
+ if (isType (token, TOKEN_OPEN_SQUARE))
+ {
+ readToken (token);
+ if (isKeyword (token, KEYWORD_cdata))
+ {
+ readToken (token);
+ }
+ }
+ }
+ }
+ if (isType (token, TOKEN_CLOSE_SQUARE))
+ {
+ /*
+ * Handle these tags
+ * <![CDATA[
+ * ...
+ * ]]>
+ */
+ readToken (token);
+ if (isType (token, TOKEN_CLOSE_SQUARE))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_GREATER_THAN))
+ {
+ return TRUE;
+ }
+ }
+ }
+ else if (isType (token, TOKEN_CLOSE_MXML))
+ {
+ /*
+ * Read the Script> tags
+ */
+ readToken (token);
+ readToken (token);
+ return TRUE;
+ }
+ else if (isType (token, TOKEN_OPEN_MXML))
+ {
+ parseMXML (token);
+ }
+ else
+ {
+ if (isType(token, TOKEN_KEYWORD))
+ {
+ switch (token->keyword)
+ {
+ case KEYWORD_function: parseFunction (token); break;
+ default: parseLine (token); break;
+ }
+ }
+ else
+ {
+ parseLine (token);
+ }
+ }
+ } while (TRUE);
+}
+
+static void parseFlexFile (tokenInfo *const token)
+{
+ do
+ {
+ readToken (token);
+
+ if (isType (token, TOKEN_OPEN_MXML))
+ {
+ parseMXML (token);
+ }
+ if (isType (token, TOKEN_LESS_THAN))
+ {
+ readToken (token);
+ if (isType (token, TOKEN_QUESTION_MARK))
+ {
+ readToken (token);
+ while (! isType (token, TOKEN_QUESTION_MARK) )
+ {
+ readToken (token);
+ }
+ readToken (token);
+ }
+ }
+ else
+ {
+ parseActionScript (token);
+ }
+ } while (TRUE);
+}
+
+static void initialize (const langType language)
+{
+ Assert (sizeof (FlexKinds) / sizeof (FlexKinds [0]) == FLEXTAG_COUNT);
+ Lang_js = language;
+ buildFlexKeywordHash ();
+}
+
+static void findFlexTags (void)
+{
+ tokenInfo *const token = newToken ();
+ exception_t exception;
+
+ ClassNames = stringListNew ();
+ FunctionNames = stringListNew ();
+
+ exception = (exception_t) (setjmp (Exception));
+ while (exception == ExceptionNone)
+ parseFlexFile (token);
+
+ stringListDelete (ClassNames);
+ stringListDelete (FunctionNames);
+ ClassNames = NULL;
+ FunctionNames = NULL;
+ deleteToken (token);
+}
+
+/* Create parser definition stucture */
+extern parserDefinition* FlexParser (void)
+{
+ static const char *const extensions [] = { "as", "mxml", NULL };
+ parserDefinition *const def = parserNew ("Flex");
+ def->extensions = extensions;
+ /*
+ * New definitions for parsing instead of regex
+ */
+ def->kinds = FlexKinds;
+ def->kindCount = KIND_COUNT (FlexKinds);
+ def->parser = findFlexTags;
+ def->initialize = initialize;
+
+ return def;
+}
+/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
diff --git a/plugins/symbol-db/anjuta-tags/keyword.c b/plugins/symbol-db/anjuta-tags/keyword.c
index 92dbe0c..2a549d9 100644
--- a/plugins/symbol-db/anjuta-tags/keyword.c
+++ b/plugins/symbol-db/anjuta-tags/keyword.c
@@ -1,5 +1,5 @@
/*
-* $Id: keyword.c 658 2008-04-20 23:21:35Z elliotth $
+* $Id: keyword.c 715 2009-07-06 03:31:00Z dhiebert $
*
* Copyright (c) 1998-2002, Darren Hiebert
*
@@ -123,8 +123,7 @@ static hashEntry *newEntry (
extern void addKeyword (const char *const string, langType language, int value)
{
const unsigned long hashedValue = hashValue (string);
- hashEntry *tableEntry = getHashTableEntry (hashedValue);
- hashEntry *entry = tableEntry;
+ hashEntry *entry = getHashTableEntry (hashedValue);
if (entry == NULL)
{
diff --git a/plugins/symbol-db/anjuta-tags/lisp.c b/plugins/symbol-db/anjuta-tags/lisp.c
index 0e6add5..6fdc4dd 100644
--- a/plugins/symbol-db/anjuta-tags/lisp.c
+++ b/plugins/symbol-db/anjuta-tags/lisp.c
@@ -1,5 +1,5 @@
/*
-* $Id: lisp.c 443 2006-05-30 04:37:13Z darren $
+* $Id: lisp.c 717 2009-07-07 03:40:50Z dhiebert $
*
* Copyright (c) 2000-2002, Darren Hiebert
*
@@ -126,7 +126,7 @@ static void findLispTags (void)
extern parserDefinition* LispParser (void)
{
static const char *const extensions [] = {
- "cl", "clisp", "el", "l", "lisp", "lsp", "ml", NULL
+ "cl", "clisp", "el", "l", "lisp", "lsp", NULL
};
parserDefinition* def = parserNew ("Lisp");
def->kinds = LispKinds;
diff --git a/plugins/symbol-db/anjuta-tags/matlab.c b/plugins/symbol-db/anjuta-tags/matlab.c
index d514b5c..0811457 100644
--- a/plugins/symbol-db/anjuta-tags/matlab.c
+++ b/plugins/symbol-db/anjuta-tags/matlab.c
@@ -1,12 +1,12 @@
/*
-* $Id: yacc.c 443 2006-05-30 04:37:13Z darren $
+* $Id$
*
-* Copyright (c) 2001-2002, Nick Hibma <n_hibma van-laarhoven org>
+* Copyright (c) 2008, David Fishburn
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
-* This module contains functions for generating tags for YACC language files.
+* This module contains functions for generating tags for MATLAB language files.
*/
/*
diff --git a/plugins/symbol-db/anjuta-tags/ocaml.c b/plugins/symbol-db/anjuta-tags/ocaml.c
new file mode 100644
index 0000000..8fd6872
--- /dev/null
+++ b/plugins/symbol-db/anjuta-tags/ocaml.c
@@ -0,0 +1,1842 @@
+/*
+* Copyright (c) 2009, Vincent Berthoux
+*
+* This source code is released for free distribution under the terms of the
+* GNU General Public License.
+*
+* This module contains functions for generating tags for Objective Caml
+* language files.
+*/
+/*
+* INCLUDE FILES
+*/
+#include "general.h" /* must always come first */
+
+#include <string.h>
+
+#include "keyword.h"
+#include "entry.h"
+#include "options.h"
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+
+/* To get rid of unused parameter warning in
+ * -Wextra */
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /* unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+#define OCAML_MAX_STACK_SIZE 256
+
+typedef enum {
+ K_CLASS, /* Ocaml class, relatively rare */
+ K_METHOD, /* class method */
+ K_MODULE, /* Ocaml module OR functor */
+ K_VAR,
+ K_TYPE, /* name of an OCaml type */
+ K_FUNCTION,
+ K_CONSTRUCTOR, /* Constructor of a sum type */
+ K_RECORDFIELD,
+ K_EXCEPTION
+} ocamlKind;
+
+static kindOption OcamlKinds[] = {
+ {TRUE, 'c', "class", "classes"},
+ {TRUE, 'm', "method", "Object's method"},
+ {TRUE, 'M', "module", "Module or functor"},
+ {TRUE, 'v', "var", "Global variable"},
+ {TRUE, 't', "type", "Type name"},
+ {TRUE, 'f', "function", "A function"},
+ {TRUE, 'C', "Constructor", "A constructor"},
+ {TRUE, 'r', "Record field", "A 'structure' field"},
+ {TRUE, 'e', "Exception", "An exception"}
+};
+
+typedef enum {
+ OcaKEYWORD_and,
+ OcaKEYWORD_begin,
+ OcaKEYWORD_class,
+ OcaKEYWORD_do,
+ OcaKEYWORD_done,
+ OcaKEYWORD_else,
+ OcaKEYWORD_end,
+ OcaKEYWORD_exception,
+ OcaKEYWORD_for,
+ OcaKEYWORD_functor,
+ OcaKEYWORD_fun,
+ OcaKEYWORD_if,
+ OcaKEYWORD_in,
+ OcaKEYWORD_let,
+ OcaKEYWORD_match,
+ OcaKEYWORD_method,
+ OcaKEYWORD_module,
+ OcaKEYWORD_mutable,
+ OcaKEYWORD_object,
+ OcaKEYWORD_of,
+ OcaKEYWORD_rec,
+ OcaKEYWORD_sig,
+ OcaKEYWORD_struct,
+ OcaKEYWORD_then,
+ OcaKEYWORD_try,
+ OcaKEYWORD_type,
+ OcaKEYWORD_val,
+ OcaKEYWORD_virtual,
+ OcaKEYWORD_while,
+ OcaKEYWORD_with,
+
+ OcaIDENTIFIER,
+ Tok_PARL, /* '(' */
+ Tok_PARR, /* ')' */
+ Tok_BRL, /* '[' */
+ Tok_BRR, /* ']' */
+ Tok_CurlL, /* '{' */
+ Tok_CurlR, /* '}' */
+ Tok_Prime, /* '\'' */
+ Tok_Pipe, /* '|' */
+ Tok_EQ, /* '=' */
+ Tok_Val, /* string/number/poo */
+ Tok_Op, /* any operator recognized by the language */
+ Tok_semi, /* ';' */
+ Tok_comma, /* ',' */
+ Tok_To, /* '->' */
+ Tok_Sharp, /* '#' */
+ Tok_Backslash, /* '\\' */
+
+ Tok_EOF /* END of file */
+} ocamlKeyword;
+
+typedef struct sOcaKeywordDesc {
+ const char *name;
+ ocamlKeyword id;
+} ocaKeywordDesc;
+
+typedef ocamlKeyword ocaToken;
+
+static const ocaKeywordDesc OcamlKeywordTable[] = {
+ { "and" , OcaKEYWORD_and },
+ { "begin" , OcaKEYWORD_begin },
+ { "class" , OcaKEYWORD_class },
+ { "do" , OcaKEYWORD_do },
+ { "done" , OcaKEYWORD_done },
+ { "else" , OcaKEYWORD_else },
+ { "end" , OcaKEYWORD_end },
+ { "exception" , OcaKEYWORD_exception },
+ { "for" , OcaKEYWORD_for },
+ { "fun" , OcaKEYWORD_fun },
+ { "function" , OcaKEYWORD_fun },
+ { "functor" , OcaKEYWORD_functor },
+ { "in" , OcaKEYWORD_in },
+ { "let" , OcaKEYWORD_let },
+ { "match" , OcaKEYWORD_match },
+ { "method" , OcaKEYWORD_method },
+ { "module" , OcaKEYWORD_module },
+ { "mutable" , OcaKEYWORD_mutable },
+ { "object" , OcaKEYWORD_object },
+ { "of" , OcaKEYWORD_of },
+ { "rec" , OcaKEYWORD_rec },
+ { "sig" , OcaKEYWORD_sig },
+ { "struct" , OcaKEYWORD_struct },
+ { "then" , OcaKEYWORD_then },
+ { "try" , OcaKEYWORD_try },
+ { "type" , OcaKEYWORD_type },
+ { "val" , OcaKEYWORD_val },
+ { "value" , OcaKEYWORD_let }, /* just to handle revised syntax */
+ { "virtual" , OcaKEYWORD_virtual },
+ { "while" , OcaKEYWORD_while },
+ { "with" , OcaKEYWORD_with },
+
+ { "or" , Tok_Op },
+ { "mod " , Tok_Op },
+ { "land " , Tok_Op },
+ { "lor " , Tok_Op },
+ { "lxor " , Tok_Op },
+ { "lsl " , Tok_Op },
+ { "lsr " , Tok_Op },
+ { "asr" , Tok_Op },
+ { "->" , Tok_To },
+ { "true" , Tok_Val },
+ { "false" , Tok_Val }
+};
+
+static langType Lang_Ocaml;
+
+boolean exportLocalInfo = FALSE;
+
+/*//////////////////////////////////////////////////////////////////
+//// lexingInit */
+typedef struct _lexingState {
+ vString *name; /* current parsed identifier/operator */
+ const unsigned char *cp; /* position in stream */
+} lexingState;
+
+/* array of the size of all possible value for a char */
+boolean isOperator[1 << (8 * sizeof (char))] = { FALSE };
+
+static void initKeywordHash ( void )
+{
+ const size_t count = sizeof (OcamlKeywordTable) / sizeof (ocaKeywordDesc);
+ size_t i;
+
+ for (i = 0; i < count; ++i)
+ {
+ addKeyword (OcamlKeywordTable[i].name, Lang_Ocaml,
+ (int) OcamlKeywordTable[i].id);
+ }
+}
+
+/* definition of all the operator in OCaml,
+ * /!\ certain operator get special treatment
+ * in regards of their role in OCaml grammar :
+ * '|' ':' '=' '~' and '?' */
+static void initOperatorTable ( void )
+{
+ isOperator['!'] = TRUE;
+ isOperator['$'] = TRUE;
+ isOperator['%'] = TRUE;
+ isOperator['&'] = TRUE;
+ isOperator['*'] = TRUE;
+ isOperator['+'] = TRUE;
+ isOperator['-'] = TRUE;
+ isOperator['.'] = TRUE;
+ isOperator['/'] = TRUE;
+ isOperator[':'] = TRUE;
+ isOperator['<'] = TRUE;
+ isOperator['='] = TRUE;
+ isOperator['>'] = TRUE;
+ isOperator['?'] = TRUE;
+ isOperator['@'] = TRUE;
+ isOperator['^'] = TRUE;
+ isOperator['~'] = TRUE;
+ isOperator['|'] = TRUE;
+}
+
+/*//////////////////////////////////////////////////////////////////////
+//// Lexing */
+static boolean isNum (char c)
+{
+ return c >= '0' && c <= '9';
+}
+static boolean isLowerAlpha (char c)
+{
+ return c >= 'a' && c <= 'z';
+}
+
+static boolean isUpperAlpha (char c)
+{
+ return c >= 'A' && c <= 'Z';
+}
+
+static boolean isAlpha (char c)
+{
+ return isLowerAlpha (c) || isUpperAlpha (c);
+}
+
+static boolean isIdent (char c)
+{
+ return isNum (c) || isAlpha (c) || c == '_' || c == '\'';
+}
+
+static boolean isSpace (char c)
+{
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
+}
+
+static void eatWhiteSpace (lexingState * st)
+{
+ const unsigned char *cp = st->cp;
+ while (isSpace (*cp))
+ cp++;
+
+ st->cp = cp;
+}
+
+static void eatString (lexingState * st)
+{
+ boolean lastIsBackSlash = FALSE;
+ boolean unfinished = TRUE;
+ const unsigned char *c = st->cp + 1;
+
+ while (unfinished)
+ {
+ /* end of line should never happen.
+ * we tolerate it */
+ if (c == NULL || c[0] == '\0')
+ break;
+ else if (*c == '"' && !lastIsBackSlash)
+ unfinished = FALSE;
+ else
+ lastIsBackSlash = *c == '\\';
+
+ c++;
+ }
+
+ st->cp = c;
+}
+
+static void eatComment (lexingState * st)
+{
+ boolean unfinished = TRUE;
+ boolean lastIsStar = FALSE;
+ const unsigned char *c = st->cp + 2;
+
+ while (unfinished)
+ {
+ /* we've reached the end of the line..
+ * so we have to reload a line... */
+ if (c == NULL || *c == '\0')
+ {
+ st->cp = fileReadLine ();
+ /* WOOPS... no more input...
+ * we return, next lexing read
+ * will be null and ok */
+ if (st->cp == NULL)
+ return;
+ c = st->cp;
+ continue;
+ }
+ /* we've reached the end of the comment */
+ else if (*c == ')' && lastIsStar)
+ unfinished = FALSE;
+ /* here we deal with imbricated comment, which
+ * are allowed in OCaml */
+ else if (c[0] == '(' && c[1] == '*')
+ {
+ st->cp = c;
+ eatComment (st);
+ c = st->cp;
+ lastIsStar = FALSE;
+ }
+ else
+ lastIsStar = '*' == *c;
+
+ c++;
+ }
+
+ st->cp = c;
+}
+
+static void readIdentifier (lexingState * st)
+{
+ const unsigned char *p;
+ vStringClear (st->name);
+
+ /* first char is a simple letter */
+ if (isAlpha (*st->cp) || *st->cp == '_')
+ vStringPut (st->name, (int) *st->cp);
+
+ /* Go till you get identifier chars */
+ for (p = st->cp + 1; isIdent (*p); p++)
+ vStringPut (st->name, (int) *p);
+
+ st->cp = p;
+
+ vStringTerminate (st->name);
+}
+
+static ocamlKeyword eatNumber (lexingState * st)
+{
+ while (isNum (*st->cp))
+ st->cp++;
+ return Tok_Val;
+}
+
+/* Operator can be defined in OCaml as a function
+ * so we must be ample enough to parse them normally */
+static ocamlKeyword eatOperator (lexingState * st)
+{
+ int count = 0;
+ const unsigned char *root = st->cp;
+
+ vStringClear (st->name);
+
+ while (isOperator[st->cp[count]])
+ {
+ vStringPut (st->name, st->cp[count]);
+ count++;
+ }
+
+ vStringTerminate (st->name);
+
+ st->cp += count;
+ if (count <= 1)
+ {
+ switch (root[0])
+ {
+ case '|':
+ return Tok_Pipe;
+ case '=':
+ return Tok_EQ;
+ default:
+ return Tok_Op;
+ }
+ }
+ else if (count == 2 && root[0] == '-' && root[1] == '>')
+ return Tok_To;
+ else
+ return Tok_Op;
+}
+
+/* The lexer is in charge of reading the file.
+ * Some of sub-lexer (like eatComment) also read file.
+ * lexing is finished when the lexer return Tok_EOF */
+static ocamlKeyword lex (lexingState * st)
+{
+ int retType;
+ /* handling data input here */
+ while (st->cp == NULL || st->cp[0] == '\0')
+ {
+ st->cp = fileReadLine ();
+ if (st->cp == NULL)
+ return Tok_EOF;
+ }
+
+ if (isAlpha (*st->cp))
+ {
+ readIdentifier (st);
+ retType = lookupKeyword (vStringValue (st->name), Lang_Ocaml);
+
+ if (retType == -1) /* If it's not a keyword */
+ {
+ return OcaIDENTIFIER;
+ }
+ else
+ {
+ return retType;
+ }
+ }
+ else if (isNum (*st->cp))
+ return eatNumber (st);
+ else if (isSpace (*st->cp))
+ {
+ eatWhiteSpace (st);
+ return lex (st);
+ }
+ /* OCaml permit the definition of our own operators
+ * so here we check all the consecuting chars which
+ * are operators to discard them. */
+ else if (isOperator[*st->cp])
+ return eatOperator (st);
+ else
+ switch (*st->cp)
+ {
+ case '(':
+ if (st->cp[1] == '*') /* ergl, a comment */
+ {
+ eatComment (st);
+ return lex (st);
+ }
+ else
+ {
+ st->cp++;
+ return Tok_PARL;
+ }
+
+ case ')':
+ st->cp++;
+ return Tok_PARR;
+ case '[':
+ st->cp++;
+ return Tok_BRL;
+ case ']':
+ st->cp++;
+ return Tok_BRR;
+ case '{':
+ st->cp++;
+ return Tok_CurlL;
+ case '}':
+ st->cp++;
+ return Tok_CurlR;
+ case '\'':
+ st->cp++;
+ return Tok_Prime;
+ case ',':
+ st->cp++;
+ return Tok_comma;
+ case '=':
+ st->cp++;
+ return Tok_EQ;
+ case ';':
+ st->cp++;
+ return Tok_semi;
+ case '"':
+ eatString (st);
+ return Tok_Val;
+ case '_':
+ st->cp++;
+ return Tok_Val;
+ case '#':
+ st->cp++;
+ return Tok_Sharp;
+ case '\\':
+ st->cp++;
+ return Tok_Backslash;
+
+ default:
+ st->cp++;
+ break;
+ }
+
+ /* default return if nothing is recognized,
+ * shouldn't happen, but at least, it will
+ * be handled without destroying the parsing. */
+ return Tok_Val;
+}
+
+/*//////////////////////////////////////////////////////////////////////
+//// Parsing */
+typedef void (*parseNext) (vString * const ident, ocaToken what);
+
+/********** Helpers */
+/* This variable hold the 'parser' which is going to
+ * handle the next token */
+parseNext toDoNext;
+
+/* Special variable used by parser eater to
+ * determine which action to put after their
+ * job is finished. */
+parseNext comeAfter;
+
+/* If a token put an end to current delcaration/
+ * statement */
+ocaToken terminatingToken;
+
+/* Token to be searched by the different
+ * parser eater. */
+ocaToken waitedToken;
+
+/* name of the last class, used for
+ * context stacking. */
+vString *lastClass;
+
+vString *voidName;
+
+typedef enum _sContextKind {
+ ContextStrong,
+ ContextSoft
+} contextKind;
+
+typedef enum _sContextType {
+ ContextType,
+ ContextModule,
+ ContextClass,
+ ContextValue,
+ ContextFunction,
+ ContextMethod,
+ ContextBlock
+} contextType;
+
+typedef struct _sOcamlContext {
+ contextKind kind; /* well if the context is strong or not */
+ contextType type;
+ parseNext callback; /* what to do when a context is pop'd */
+ vString *contextName; /* name, if any, of the surrounding context */
+} ocamlContext;
+
+/* context stack, can be used to output scope information
+ * into the tag file. */
+ocamlContext stack[OCAML_MAX_STACK_SIZE];
+/* current position in the tag */
+int stackIndex;
+
+/* special function, often recalled, so putting it here */
+static void globalScope (vString * const ident, ocaToken what);
+
+/* Return : index of the last named context if one
+ * is found, -1 otherwise */
+static int getLastNamedIndex ( void )
+{
+ int i;
+
+ for (i = stackIndex - 1; i >= 0; --i)
+ {
+ if (stack[i].contextName->buffer &&
+ strlen (stack[i].contextName->buffer) > 0)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static const char *contextDescription (contextType t)
+{
+ switch (t)
+ {
+ case ContextFunction:
+ return "function";
+ case ContextMethod:
+ return "method";
+ case ContextValue:
+ return "value";
+ case ContextModule:
+ return "Module";
+ case ContextType:
+ return "type";
+ case ContextClass:
+ return "class";
+ case ContextBlock:
+ return "begin/end";
+ }
+
+ return NULL;
+}
+
+static char contextTypeSuffix (contextType t)
+{
+ switch (t)
+ {
+ case ContextFunction:
+ case ContextMethod:
+ case ContextValue:
+ case ContextModule:
+ return '/';
+ case ContextType:
+ return '.';
+ case ContextClass:
+ return '#';
+ case ContextBlock:
+ return ' ';
+ }
+
+ return '$';
+}
+
+/* Push a new context, handle null string */
+static void pushContext (contextKind kind, contextType type, parseNext after,
+ vString const *contextName)
+{
+ int parentIndex;
+
+ if (stackIndex >= OCAML_MAX_STACK_SIZE)
+ {
+ verbose ("OCaml Maximum depth reached");
+ return;
+ }
+
+
+ stack[stackIndex].kind = kind;
+ stack[stackIndex].type = type;
+ stack[stackIndex].callback = after;
+
+ parentIndex = getLastNamedIndex ();
+ if (contextName == NULL)
+ {
+ vStringClear (stack[stackIndex++].contextName);
+ return;
+ }
+
+ if (parentIndex >= 0)
+ {
+ vStringCopy (stack[stackIndex].contextName,
+ stack[parentIndex].contextName);
+ vStringPut (stack[stackIndex].contextName,
+ contextTypeSuffix (stack[parentIndex].type));
+
+ vStringCat (stack[stackIndex].contextName, contextName);
+ }
+ else
+ vStringCopy (stack[stackIndex].contextName, contextName);
+
+ stackIndex++;
+}
+
+static void pushStrongContext (vString * name, contextType type)
+{
+ pushContext (ContextStrong, type, &globalScope, name);
+}
+
+static void pushSoftContext (parseNext continuation,
+ vString * name, contextType type)
+{
+ pushContext (ContextSoft, type, continuation, name);
+}
+
+static void pushEmptyContext (parseNext continuation)
+{
+ pushContext (ContextSoft, ContextValue, continuation, NULL);
+}
+
+/* unroll the stack until the last named context.
+ * then discard it. Used to handle the :
+ * let f x y = ...
+ * in ...
+ * where the context is reseted after the in. Context may have
+ * been really nested before that. */
+static void popLastNamed ( void )
+{
+ int i = getLastNamedIndex ();
+
+ if (i >= 0)
+ {
+ stackIndex = i;
+ toDoNext = stack[i].callback;
+ vStringClear (stack[i].contextName);
+ }
+ else
+ {
+ /* ok, no named context found...
+ * (should not happen). */
+ stackIndex = 0;
+ toDoNext = &globalScope;
+ }
+}
+
+/* pop a context without regarding it's content
+ * (beside handling empty stack case) */
+static void popSoftContext ( void )
+{
+ if (stackIndex <= 0)
+ {
+ toDoNext = &globalScope;
+ }
+ else
+ {
+ stackIndex--;
+ toDoNext = stack[stackIndex].callback;
+ vStringClear (stack[stackIndex].contextName);
+ }
+}
+
+/* Reset everything until the last global space.
+ * a strong context can be :
+ * - module
+ * - class definition
+ * - the initial global space
+ * - a _global_ delcaration (let at global scope or in a module).
+ * Created to exit quickly deeply nested context */
+static contextType popStrongContext ( void )
+{
+ int i;
+
+ for (i = stackIndex - 1; i >= 0; --i)
+ {
+ if (stack[i].kind == ContextStrong)
+ {
+ stackIndex = i;
+ toDoNext = stack[i].callback;
+ vStringClear (stack[i].contextName);
+ return stack[i].type;
+ }
+ }
+ /* ok, no strong context found... */
+ stackIndex = 0;
+ toDoNext = &globalScope;
+ return -1;
+}
+
+/* Ignore everything till waitedToken and jump to comeAfter.
+ * If the "end" keyword is encountered break, doesn't remember
+ * why though. */
+static void tillToken (vString * const UNUSED (ident), ocaToken what)
+{
+ if (what == waitedToken)
+ toDoNext = comeAfter;
+ else if (what == OcaKEYWORD_end)
+ {
+ popStrongContext ();
+ toDoNext = &globalScope;
+ }
+}
+
+/* Ignore everything till a waitedToken is seen, but
+ * take care of balanced parentheses/bracket use */
+static void contextualTillToken (vString * const UNUSED (ident), ocaToken what)
+{
+ static int parentheses = 0;
+ static int bracket = 0;
+ static int curly = 0;
+
+ switch (what)
+ {
+ case Tok_PARL:
+ parentheses--;
+ break;
+ case Tok_PARR:
+ parentheses++;
+ break;
+ case Tok_CurlL:
+ curly--;
+ break;
+ case Tok_CurlR:
+ curly++;
+ break;
+ case Tok_BRL:
+ bracket--;
+ break;
+ case Tok_BRR:
+ bracket++;
+ break;
+
+ default: /* other token are ignored */
+ break;
+ }
+
+ if (what == waitedToken && parentheses == 0 && bracket == 0 && curly == 0)
+ toDoNext = comeAfter;
+
+ else if (what == OcaKEYWORD_end)
+ {
+ popStrongContext ();
+ toDoNext = &globalScope;
+ }
+}
+
+/* Wait for waitedToken and jump to comeAfter or let
+ * the globalScope handle declarations */
+static void tillTokenOrFallback (vString * const ident, ocaToken what)
+{
+ if (what == waitedToken)
+ toDoNext = comeAfter;
+ else
+ globalScope (ident, what);
+}
+
+/* ignore token till waitedToken, or give up if find
+ * terminatingToken. Use globalScope to handle new
+ * declarations. */
+static void tillTokenOrTerminatingOrFallback (vString * const ident,
+ ocaToken what)
+{
+ if (what == waitedToken)
+ toDoNext = comeAfter;
+ else if (what == terminatingToken)
+ toDoNext = globalScope;
+ else
+ globalScope (ident, what);
+}
+
+/* ignore the next token in the stream and jump to the
+ * given comeAfter state */
+static void ignoreToken (vString * const UNUSED (ident), ocaToken UNUSED (what))
+{
+ toDoNext = comeAfter;
+}
+
+/********** Grammar */
+/* the purpose of each function is detailled near their
+ * implementation */
+
+static void killCurrentState ( void )
+{
+
+ /* Tracking the kind of previous strong
+ * context, if it doesn't match with a
+ * really strong entity, repop */
+ switch (popStrongContext ())
+ {
+
+ case ContextValue:
+ popStrongContext ();
+ break;
+ case ContextFunction:
+ popStrongContext ();
+ break;
+ case ContextMethod:
+ popStrongContext ();
+ break;
+
+ case ContextType:
+ popStrongContext();
+ break;
+ case ContextBlock:
+ break;
+ case ContextModule:
+ break;
+ case ContextClass:
+ break;
+ default:
+ /* nothing more */
+ break;
+ }
+}
+
+/* used to prepare tag for OCaml, just in case their is a need to
+ * add additional information to the tag. */
+static void prepareTag (tagEntryInfo * tag, vString const *name, ocamlKind kind)
+{
+ int parentIndex;
+
+ initTagEntry (tag, vStringValue (name));
+ tag->kindName = OcamlKinds[kind].name;
+ tag->kind = OcamlKinds[kind].letter;
+
+ parentIndex = getLastNamedIndex ();
+ if (parentIndex >= 0)
+ {
+ tag->extensionFields.scope[0] =
+ contextDescription (stack[parentIndex].type);
+ tag->extensionFields.scope[1] =
+ vStringValue (stack[parentIndex].contextName);
+ }
+}
+
+/* Used to centralise tag creation, and be able to add
+ * more information to it in the future */
+static void addTag (vString * const ident, int kind)
+{
+ tagEntryInfo toCreate;
+ prepareTag (&toCreate, ident, kind);
+ makeTagEntry (&toCreate);
+}
+
+boolean needStrongPoping = FALSE;
+static void requestStrongPoping ( void )
+{
+ needStrongPoping = TRUE;
+}
+
+static void cleanupPreviousParser ( void )
+{
+ if (needStrongPoping)
+ {
+ needStrongPoping = FALSE;
+ popStrongContext ();
+ }
+}
+
+/* Due to some circular dependencies, the following functions
+ * must be forward-declared. */
+static void letParam (vString * const ident, ocaToken what);
+static void localScope (vString * const ident, ocaToken what);
+static void mayRedeclare (vString * const ident, ocaToken what);
+static void typeSpecification (vString * const ident, ocaToken what);
+
+/*
+ * Parse a record type
+ * type ident = // parsed previously
+ * {
+ * ident1: type1;
+ * ident2: type2;
+ * }
+ */
+static void typeRecord (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case OcaIDENTIFIER:
+ addTag (ident, K_RECORDFIELD);
+ terminatingToken = Tok_CurlR;
+ waitedToken = Tok_semi;
+ comeAfter = &typeRecord;
+ toDoNext = &tillTokenOrTerminatingOrFallback;
+ break;
+
+ case OcaKEYWORD_mutable:
+ /* ignore it */
+ break;
+
+ case Tok_CurlR:
+ popStrongContext ();
+ toDoNext = &globalScope;
+ break;
+
+ default: /* don't care */
+ break;
+ }
+}
+
+/* handle :
+ * exception ExceptionName ... */
+static void exceptionDecl (vString * const ident, ocaToken what)
+{
+ if (what == OcaIDENTIFIER)
+ {
+ addTag (ident, K_EXCEPTION);
+ }
+ /* don't know what to do on else... */
+
+ toDoNext = &globalScope;
+}
+
+tagEntryInfo tempTag;
+vString *tempIdent;
+
+/* Ensure a constructor is not a type path beginning
+ * with a module */
+static void constructorValidation (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_Op: /* if we got a '.' which is an operator */
+ toDoNext = &globalScope;
+ popStrongContext ();
+ needStrongPoping = FALSE;
+ break;
+
+ case OcaKEYWORD_of: /* OK, it must be a constructor :) */
+ makeTagEntry (&tempTag);
+ vStringClear (tempIdent);
+ toDoNext = &tillTokenOrFallback;
+ comeAfter = &typeSpecification;
+ waitedToken = Tok_Pipe;
+ break;
+
+ case Tok_Pipe: /* OK, it was a constructor :) */
+ makeTagEntry (&tempTag);
+ vStringClear (tempIdent);
+ toDoNext = &typeSpecification;
+ break;
+
+ default: /* and mean that we're not facing a module name */
+ makeTagEntry (&tempTag);
+ vStringClear (tempIdent);
+ toDoNext = &tillTokenOrFallback;
+ comeAfter = &typeSpecification;
+ waitedToken = Tok_Pipe;
+
+ /* nothing in the context, discard it */
+ popStrongContext ();
+
+ /* to be sure we use this token */
+ globalScope (ident, what);
+ }
+}
+
+
+/* Parse beginning of type definition
+ * type 'avar ident =
+ * or
+ * type ('var1, 'var2) ident =
+ */
+static void typeDecl (vString * const ident, ocaToken what)
+{
+
+ switch (what)
+ {
+ /* parameterized */
+ case Tok_Prime:
+ comeAfter = &typeDecl;
+ toDoNext = &ignoreToken;
+ break;
+ /* LOTS of parameters */
+ case Tok_PARL:
+ comeAfter = &typeDecl;
+ waitedToken = Tok_PARR;
+ toDoNext = &tillToken;
+ break;
+
+ case OcaIDENTIFIER:
+ addTag (ident, K_TYPE);
+ pushStrongContext (ident, ContextType);
+ requestStrongPoping ();
+ waitedToken = Tok_EQ;
+ comeAfter = &typeSpecification;
+ toDoNext = &tillTokenOrFallback;
+ break;
+
+ default:
+ globalScope (ident, what);
+ }
+}
+
+/* Parse type of kind
+ * type bidule = Ctor1 of ...
+ * | Ctor2
+ * | Ctor3 of ...
+ * or
+ * type bidule = | Ctor1 of ... | Ctor2
+ *
+ * when type bidule = { ... } is detected,
+ * let typeRecord handle it. */
+static void typeSpecification (vString * const ident, ocaToken what)
+{
+
+ switch (what)
+ {
+ case OcaIDENTIFIER:
+ if (isUpperAlpha (ident->buffer[0]))
+ {
+ /* here we handle type aliases of type
+ * type foo = AnotherModule.bar
+ * AnotherModule can mistakenly be took
+ * for a constructor. */
+ vStringCopy (tempIdent, ident);
+ prepareTag (&tempTag, tempIdent, K_CONSTRUCTOR);
+ toDoNext = &constructorValidation;
+ }
+ else
+ {
+ toDoNext = &tillTokenOrFallback;
+ comeAfter = &typeSpecification;
+ waitedToken = Tok_Pipe;
+ }
+ break;
+
+ case OcaKEYWORD_and:
+ toDoNext = &typeDecl;
+ break;
+
+ case Tok_BRL: /* the '[' & ']' are ignored to accommodate */
+ case Tok_BRR: /* with the revised syntax */
+ case Tok_Pipe:
+ /* just ignore it */
+ break;
+
+ case Tok_CurlL:
+ toDoNext = &typeRecord;
+ break;
+
+ default: /* don't care */
+ break;
+ }
+}
+
+
+static boolean dirtySpecialParam = FALSE;
+
+
+/* parse the ~label and ~label:type parameter */
+static void parseLabel (vString * const ident, ocaToken what)
+{
+ static int parCount = 0;
+
+ switch (what)
+ {
+ case OcaIDENTIFIER:
+ if (!dirtySpecialParam)
+ {
+
+ if (exportLocalInfo)
+ addTag (ident, K_VAR);
+
+ dirtySpecialParam = TRUE;
+ }
+ break;
+
+ case Tok_PARL:
+ parCount++;
+ break;
+
+ case Tok_PARR:
+ parCount--;
+ if (parCount == 0)
+ toDoNext = &letParam;
+ break;
+
+ case Tok_Op:
+ if (ident->buffer[0] == ':')
+ {
+ toDoNext = &ignoreToken;
+ comeAfter = &letParam;
+ }
+ else if (parCount == 0 && dirtySpecialParam)
+ {
+ toDoNext = &letParam;
+ letParam (ident, what);
+ }
+ break;
+
+ default:
+ if (parCount == 0 && dirtySpecialParam)
+ {
+ toDoNext = &letParam;
+ letParam (ident, what);
+ }
+ break;
+ }
+}
+
+
+/* Optional argument with syntax like this :
+ * ?(foo = value) */
+static void parseOptionnal (vString * const ident, ocaToken what)
+{
+ static int parCount = 0;
+
+
+ switch (what)
+ {
+ case OcaIDENTIFIER:
+ if (!dirtySpecialParam)
+ {
+ if (exportLocalInfo)
+ addTag (ident, K_VAR);
+
+ dirtySpecialParam = TRUE;
+
+ if (parCount == 0)
+ toDoNext = &letParam;
+ }
+ break;
+
+ case Tok_PARL:
+ parCount++;
+ break;
+
+ case Tok_PARR:
+ parCount--;
+ if (parCount == 0)
+ toDoNext = &letParam;
+ break;
+
+ default: /* don't care */
+ break;
+ }
+}
+
+
+/** handle let inside functions (so like it's name
+ * say : local let */
+static void localLet (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_PARL:
+ /* We ignore this token to be able to parse such
+ * declarations :
+ * let (ident : type) = ...
+ */
+ break;
+
+ case OcaKEYWORD_rec:
+ /* just ignore to be able to parse such declarations:
+ * let rec ident = ... */
+ break;
+
+ case Tok_Op:
+ /* we are defining a new operator, it's a
+ * function definition */
+ if (exportLocalInfo)
+ addTag (ident, K_FUNCTION);
+
+ pushSoftContext (mayRedeclare, ident, ContextFunction);
+ toDoNext = &letParam;
+ break;
+
+ /* Can be a weiiird binding, or an '_' */
+ case Tok_Val:
+ if (exportLocalInfo)
+ addTag (ident, K_VAR);
+ pushSoftContext (mayRedeclare, ident, ContextValue);
+ toDoNext = &letParam;
+ break;
+
+ case OcaIDENTIFIER:
+ if (exportLocalInfo)
+ addTag (ident, K_VAR);
+ pushSoftContext (mayRedeclare, ident, ContextValue);
+ toDoNext = &letParam;
+ break;
+
+ case OcaKEYWORD_end:
+ popStrongContext ();
+ break;
+
+ default:
+ toDoNext = &localScope;
+ break;
+ }
+}
+
+/* parse :
+ * | pattern pattern -> ...
+ * or
+ * pattern apttern apttern -> ...
+ * we ignore all identifiers declared in the pattern,
+ * because their scope is likely to be even more limited
+ * than the let definitions.
+ * Used after a match ... with, or a function ... or fun ...
+ * because their syntax is similar. */
+static void matchPattern (vString * const UNUSED (ident), ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_To:
+ pushEmptyContext (&matchPattern);
+ toDoNext = &mayRedeclare;
+ break;
+
+
+ case OcaKEYWORD_in:
+ popLastNamed ();
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Used at the beginning of a new scope (begin of a
+ * definition, parenthesis...) to catch inner let
+ * definition that may be in. */
+static void mayRedeclare (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case OcaKEYWORD_let:
+ case OcaKEYWORD_val:
+ toDoNext = localLet;
+ break;
+
+ case OcaKEYWORD_object:
+ vStringClear (lastClass);
+ pushContext (ContextStrong, ContextClass,
+ &localScope, NULL /*voidName */ );
+ needStrongPoping = FALSE;
+ toDoNext = &globalScope;
+ break;
+
+ case OcaKEYWORD_for:
+ case OcaKEYWORD_while:
+ toDoNext = &tillToken;
+ waitedToken = OcaKEYWORD_do;
+ comeAfter = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_try:
+ toDoNext = &mayRedeclare;
+ pushSoftContext (matchPattern, ident, ContextFunction);
+ break;
+
+ case OcaKEYWORD_fun:
+ toDoNext = &matchPattern;
+ break;
+
+ /* Handle the special ;; from the OCaml
+ * Top level */
+ case Tok_semi:
+ default:
+ toDoNext = &localScope;
+ localScope (ident, what);
+ }
+}
+
+/* parse :
+ * p1 p2 ... pn = ...
+ * or
+ * ?(p1=v) p2 ~p3 ~pn:ja ... = ... */
+static void letParam (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_EQ:
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaIDENTIFIER:
+ if (exportLocalInfo)
+ addTag (ident, K_VAR);
+ break;
+
+ case Tok_Op:
+ switch (ident->buffer[0])
+ {
+ case ':':
+ /*popSoftContext(); */
+ /* we got a type signature */
+ comeAfter = &mayRedeclare;
+ toDoNext = &tillTokenOrFallback;
+ waitedToken = Tok_EQ;
+ break;
+
+ /* parse something like
+ * ~varname:type
+ * or
+ * ~varname
+ * or
+ * ~(varname: long type) */
+ case '~':
+ toDoNext = &parseLabel;
+ dirtySpecialParam = FALSE;
+ break;
+
+ /* Optional argument with syntax like this :
+ * ?(bla = value)
+ * or
+ * ?bla */
+ case '?':
+ toDoNext = &parseOptionnal;
+ dirtySpecialParam = FALSE;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default: /* don't care */
+ break;
+ }
+}
+
+
+/* parse object ...
+ * used to be sure the class definition is not a type
+ * alias */
+static void classSpecif (vString * const UNUSED (ident), ocaToken what)
+{
+ switch (what)
+ {
+ case OcaKEYWORD_object:
+ pushStrongContext (lastClass, ContextClass);
+ toDoNext = &globalScope;
+ break;
+
+ default:
+ vStringClear (lastClass);
+ toDoNext = &globalScope;
+ }
+}
+
+/* Handle a method ... class declaration.
+ * nearly a copy/paste of globalLet. */
+static void methodDecl (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_PARL:
+ /* We ignore this token to be able to parse such
+ * declarations :
+ * let (ident : type) = ... */
+ break;
+
+ case OcaKEYWORD_mutable:
+ case OcaKEYWORD_virtual:
+ case OcaKEYWORD_rec:
+ /* just ignore to be able to parse such declarations:
+ * let rec ident = ... */
+ break;
+
+ case OcaIDENTIFIER:
+ addTag (ident, K_METHOD);
+ /* Normal pushing to get good subs */
+ pushStrongContext (ident, ContextMethod);
+ /*pushSoftContext( globalScope, ident, ContextMethod ); */
+ toDoNext = &letParam;
+ break;
+
+ case OcaKEYWORD_end:
+ popStrongContext ();
+ break;
+
+ default:
+ toDoNext = &globalScope;
+ break;
+ }
+}
+
+/* name of the last module, used for
+ * context stacking. */
+vString *lastModule;
+
+
+/* parse
+ * ... struct (* new global scope *) end
+ * or
+ * ... sig (* new global scope *) end
+ * or
+ * functor ... -> moduleSpecif
+ */
+static void moduleSpecif (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case OcaKEYWORD_functor:
+ toDoNext = &contextualTillToken;
+ waitedToken = Tok_To;
+ comeAfter = &moduleSpecif;
+ break;
+
+ case OcaKEYWORD_struct:
+ case OcaKEYWORD_sig:
+ pushStrongContext (lastModule, ContextModule);
+ toDoNext = &globalScope;
+ break;
+
+ case Tok_PARL: /* ( */
+ toDoNext = &contextualTillToken;
+ comeAfter = &globalScope;
+ waitedToken = Tok_PARR;
+ contextualTillToken (ident, what);
+ break;
+
+ default:
+ vStringClear (lastModule);
+ toDoNext = &globalScope;
+ }
+}
+
+/* parse :
+ * module name = ...
+ * then pass the token stream to moduleSpecif */
+static void moduleDecl (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case OcaKEYWORD_type:
+ /* just ignore it, name come after */
+ break;
+
+ case OcaIDENTIFIER:
+ addTag (ident, K_MODULE);
+ vStringCopy (lastModule, ident);
+ waitedToken = Tok_EQ;
+ comeAfter = &moduleSpecif;
+ toDoNext = &contextualTillToken;
+ break;
+
+ default: /* don't care */
+ break;
+ }
+}
+
+/* parse :
+ * class name = ...
+ * or
+ * class virtual ['a,'b] classname = ... */
+static void classDecl (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case OcaIDENTIFIER:
+ addTag (ident, K_CLASS);
+ vStringCopy (lastClass, ident);
+ toDoNext = &contextualTillToken;
+ waitedToken = Tok_EQ;
+ comeAfter = &classSpecif;
+ break;
+
+ case Tok_BRL:
+ toDoNext = &tillToken;
+ waitedToken = Tok_BRR;
+ comeAfter = &classDecl;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Handle a global
+ * let ident ...
+ * or
+ * let rec ident ... */
+static void globalLet (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_PARL:
+ /* We ignore this token to be able to parse such
+ * declarations :
+ * let (ident : type) = ...
+ */
+ break;
+
+ case OcaKEYWORD_mutable:
+ case OcaKEYWORD_virtual:
+ case OcaKEYWORD_rec:
+ /* just ignore to be able to parse such declarations:
+ * let rec ident = ... */
+ break;
+
+ case Tok_Op:
+ /* we are defining a new operator, it's a
+ * function definition */
+ addTag (ident, K_FUNCTION);
+ pushStrongContext (ident, ContextFunction);
+ toDoNext = &letParam;
+ break;
+
+ case OcaIDENTIFIER:
+ addTag (ident, K_VAR);
+ pushStrongContext (ident, ContextValue);
+ requestStrongPoping ();
+ toDoNext = &letParam;
+ break;
+
+ case OcaKEYWORD_end:
+ popStrongContext ();
+ break;
+
+ default:
+ toDoNext = &globalScope;
+ break;
+ }
+}
+
+/* Handle the "strong" top levels, all 'big' declarations
+ * happen here */
+static void globalScope (vString * const UNUSED (ident), ocaToken what)
+{
+ /* Do not touch, this is used only by the global scope
+ * to handle an 'and' */
+ static parseNext previousParser = NULL;
+
+ switch (what)
+ {
+ case OcaKEYWORD_and:
+ cleanupPreviousParser ();
+ toDoNext = previousParser;
+ break;
+
+ case OcaKEYWORD_type:
+ cleanupPreviousParser ();
+ toDoNext = &typeDecl;
+ previousParser = &typeDecl;
+ break;
+
+ case OcaKEYWORD_class:
+ cleanupPreviousParser ();
+ toDoNext = &classDecl;
+ previousParser = &classDecl;
+ break;
+
+ case OcaKEYWORD_module:
+ cleanupPreviousParser ();
+ toDoNext = &moduleDecl;
+ previousParser = &moduleDecl;
+ break;
+
+ case OcaKEYWORD_end:
+ needStrongPoping = FALSE;
+ killCurrentState ();
+ /*popStrongContext(); */
+ break;
+
+ case OcaKEYWORD_method:
+ cleanupPreviousParser ();
+ toDoNext = &methodDecl;
+ /* and is not allowed in methods */
+ break;
+
+ /* val is mixed with let as global
+ * to be able to handle mli & new syntax */
+ case OcaKEYWORD_val:
+ case OcaKEYWORD_let:
+ cleanupPreviousParser ();
+ toDoNext = &globalLet;
+ previousParser = &globalLet;
+ break;
+
+ case OcaKEYWORD_exception:
+ cleanupPreviousParser ();
+ toDoNext = &exceptionDecl;
+ previousParser = NULL;
+ break;
+
+ /* must be a #line directive, discard the
+ * whole line. */
+ case Tok_Sharp:
+ /* ignore */
+ break;
+
+ default:
+ /* we don't care */
+ break;
+ }
+}
+
+/* Parse expression. Well ignore it is more the case,
+ * ignore all tokens except "shocking" keywords */
+static void localScope (vString * const ident, ocaToken what)
+{
+ switch (what)
+ {
+ case Tok_Pipe:
+ case Tok_PARR:
+ case Tok_BRR:
+ case Tok_CurlR:
+ popSoftContext ();
+ break;
+
+ /* Everything that `begin` has an `end`
+ * as end is overloaded and signal many end
+ * of things, we add an empty strong context to
+ * avoid problem with the end.
+ */
+ case OcaKEYWORD_begin:
+ pushContext (ContextStrong, ContextBlock, &mayRedeclare, NULL);
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_in:
+ popLastNamed ();
+ break;
+
+ /* Ok, we got a '{', which is much likely to create
+ * a record. We cannot treat it like other [ && (,
+ * because it may contain the 'with' keyword and screw
+ * everything else. */
+ case Tok_CurlL:
+ toDoNext = &contextualTillToken;
+ waitedToken = Tok_CurlR;
+ comeAfter = &localScope;
+ contextualTillToken (ident, what);
+ break;
+
+ /* Yeah imperative feature of OCaml,
+ * a ';' like in C */
+ case Tok_semi:
+ toDoNext = &mayRedeclare;
+ break;
+
+ case Tok_PARL:
+ case Tok_BRL:
+ pushEmptyContext (&localScope);
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_and:
+ popLastNamed ();
+ toDoNext = &localLet;
+ break;
+
+ case OcaKEYWORD_else:
+ case OcaKEYWORD_then:
+ popSoftContext ();
+ pushEmptyContext (&localScope);
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_if:
+ pushEmptyContext (&localScope);
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_match:
+ pushEmptyContext (&localScope);
+ toDoNext = &mayRedeclare;
+ break;
+
+ case OcaKEYWORD_with:
+ popSoftContext ();
+ toDoNext = &matchPattern;
+ pushEmptyContext (&matchPattern);
+ break;
+
+ case OcaKEYWORD_end:
+ killCurrentState ();
+ break;
+
+
+ case OcaKEYWORD_fun:
+ comeAfter = &mayRedeclare;
+ toDoNext = &tillToken;
+ waitedToken = Tok_To;
+ break;
+
+ case OcaKEYWORD_done:
+ case OcaKEYWORD_val:
+ /* doesn't care */
+ break;
+
+ default:
+ requestStrongPoping ();
+ globalScope (ident, what);
+ break;
+ }
+}
+
+/*////////////////////////////////////////////////////////////////
+//// Deal with the system */
+/* in OCaml the file name is the module name used in the language
+ * with it first letter put in upper case */
+static void computeModuleName ( void )
+{
+ /* in Ocaml the file name define a module.
+ * so we define a module =)
+ */
+ const char *filename = getSourceFileName ();
+ int beginIndex = 0;
+ int endIndex = strlen (filename) - 1;
+ vString *moduleName = vStringNew ();
+
+ while (filename[endIndex] != '.' && endIndex > 0)
+ endIndex--;
+
+ /* avoid problem with path in front of filename */
+ beginIndex = endIndex;
+ while (beginIndex > 0)
+ {
+ if (filename[beginIndex] == '\\' || filename[beginIndex] == '/')
+ {
+ beginIndex++;
+ break;
+ }
+
+ beginIndex--;
+ }
+
+ vStringNCopyS (moduleName, &filename[beginIndex], endIndex - beginIndex);
+ vStringTerminate (moduleName);
+
+ if (isLowerAlpha (moduleName->buffer[0]))
+ moduleName->buffer[0] += ('A' - 'a');
+
+ makeSimpleTag (moduleName, OcamlKinds, K_MODULE);
+ vStringDelete (moduleName);
+}
+
+/* Allocate all string of the context stack */
+static void initStack ( void )
+{
+ int i;
+ for (i = 0; i < OCAML_MAX_STACK_SIZE; ++i)
+ stack[i].contextName = vStringNew ();
+}
+
+static void clearStack ( void )
+{
+ int i;
+ for (i = 0; i < OCAML_MAX_STACK_SIZE; ++i)
+ vStringDelete (stack[i].contextName);
+}
+
+static void findOcamlTags (void)
+{
+ vString *name = vStringNew ();
+ lexingState st;
+ ocaToken tok;
+
+ computeModuleName ();
+ initStack ();
+ tempIdent = vStringNew ();
+ lastModule = vStringNew ();
+ lastClass = vStringNew ();
+ voidName = vStringNew ();
+ vStringCopyS (voidName, "_");
+
+ st.name = vStringNew ();
+ st.cp = fileReadLine ();
+ toDoNext = &globalScope;
+ tok = lex (&st);
+ while (tok != Tok_EOF)
+ {
+ (*toDoNext) (st.name, tok);
+ tok = lex (&st);
+ }
+
+ vStringDelete (name);
+ vStringDelete (voidName);
+ vStringDelete (tempIdent);
+ vStringDelete (lastModule);
+ vStringDelete (lastClass);
+ clearStack ();
+}
+
+static void ocamlInitialize (const langType language)
+{
+ Lang_Ocaml = language;
+
+ initOperatorTable ();
+ initKeywordHash ();
+}
+
+extern parserDefinition *OcamlParser (void)
+{
+ static const char *const extensions[] = { "ml", "mli", NULL };
+ parserDefinition *def = parserNew ("OCaml");
+ def->kinds = OcamlKinds;
+ def->kindCount = KIND_COUNT (OcamlKinds);
+ def->extensions = extensions;
+ def->parser = findOcamlTags;
+ def->initialize = ocamlInitialize;
+
+ return def;
+}
diff --git a/plugins/symbol-db/anjuta-tags/parsers.h b/plugins/symbol-db/anjuta-tags/parsers.h
index bddff58..3dcc8ae 100644
--- a/plugins/symbol-db/anjuta-tags/parsers.h
+++ b/plugins/symbol-db/anjuta-tags/parsers.h
@@ -1,5 +1,5 @@
/*
-* $Id: parsers.h 693 2008-12-14 13:19:48Z dfishburn $
+* $Id: parsers.h 717 2009-07-07 03:40:50Z dhiebert $
*
* Copyright (c) 2000-2003, Darren Hiebert
*
@@ -38,6 +38,7 @@
LuaParser, \
MakefileParser, \
MatLabParser, \
+ OcamlParser, \
PascalParser, \
PerlParser, \
PhpParser, \
diff --git a/plugins/symbol-db/anjuta-tags/php.c b/plugins/symbol-db/anjuta-tags/php.c
index 0dd60c5..f7b91f8 100644
--- a/plugins/symbol-db/anjuta-tags/php.c
+++ b/plugins/symbol-db/anjuta-tags/php.c
@@ -1,5 +1,5 @@
/*
-* $Id: php.c 624 2007-09-15 22:53:31Z jafl $
+* $Id: php.c 729 2009-07-10 23:34:21Z jafl $
*
* Copyright (c) 2000, Jesus Castagnetto <jmcastagnetto zkey com>
*
@@ -64,17 +64,17 @@ static kindOption PhpKinds [] = {
static void installPHPRegex (const langType language)
{
- addTagRegex(language, "(^|[ \t])class[ \t]+([" ALPHA "_][" ALNUM "_]*)",
+ addTagRegex(language, "^[ \t]*(abstract[ \t]+)?class[ \t]+([" ALPHA "_][" ALNUM "_]*)",
"\\2", "c,class,classes", NULL);
- addTagRegex(language, "(^|[ \t])interface[ \t]+([" ALPHA "_][" ALNUM "_]*)",
- "\\2", "i,interface,interfaces", NULL);
- addTagRegex(language, "(^|[ \t])define[ \t]*\\([ \t]*['\"]?([" ALPHA "_][" ALNUM "_]*)",
- "\\2", "d,define,constant definitions", NULL);
- addTagRegex(language, "(^|[ \t])function[ \t]+&?[ \t]*([" ALPHA "_][" ALNUM "_]*)",
- "\\2", "f,function,functions", NULL);
- addTagRegex(language, "(^|[ \t])(\\$|::\\$|\\$this->)([" ALPHA "_][" ALNUM "_]*)[ \t]*=",
- "\\3", "v,variable,variables", NULL);
- addTagRegex(language, "(^|[ \t])(var|public|protected|private|static)[ \t]+\\$([" ALPHA "_][" ALNUM "_]*)[ \t]*[=;]",
+ addTagRegex(language, "^[ \t]*interface[ \t]+([" ALPHA "_][" ALNUM "_]*)",
+ "\\1", "i,interface,interfaces", NULL);
+ addTagRegex(language, "^[ \t]*define[ \t]*\\([ \t]*['\"]?([" ALPHA "_][" ALNUM "_]*)",
+ "\\1", "d,define,constant definitions", NULL);
+ addTagRegex(language, "^[ \t]*((static|public|protected|private)[ \t]+)*function[ \t]+&?[ \t]*([" ALPHA "_][" ALNUM "_]*)",
+ "\\3", "f,function,functions", NULL);
+ addTagRegex(language, "^[ \t]*(\\$|::\\$|\\$this->)([" ALPHA "_][" ALNUM "_]*)[ \t]*=",
+ "\\2", "v,variable,variables", NULL);
+ addTagRegex(language, "^[ \t]*((var|public|protected|private|static)[ \t]+)+\\$([" ALPHA "_][" ALNUM "_]*)[ \t]*[=;]",
"\\3", "v,variable,variables", NULL);
/* function regex is covered by PHP regex */
diff --git a/plugins/symbol-db/anjuta-tags/python.c b/plugins/symbol-db/anjuta-tags/python.c
index f565200..5fdf31b 100644
--- a/plugins/symbol-db/anjuta-tags/python.c
+++ b/plugins/symbol-db/anjuta-tags/python.c
@@ -1,5 +1,5 @@
/*
-* $Id: python.c 704 2009-05-17 16:17:25Z elias $
+* $Id: python.c 720 2009-07-07 03:55:23Z dhiebert $
*
* Copyright (c) 2000-2003, Darren Hiebert
*
@@ -19,23 +19,14 @@
#include "entry.h"
#include "options.h"
#include "read.h"
-#include "routines.h"
+#include "main.h"
#include "vstring.h"
+#include "routines.h"
+#include "debug.h"
/*
-* DATA DEFINITIONS
+* DATA DECLARATIONS
*/
-typedef enum {
- K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE
-} pythonKind;
-
-static kindOption PythonKinds[] = {
- {TRUE, 'c', "class", "classes"},
- {TRUE, 'f', "function", "functions"},
- {TRUE, 'm', "member", "class members"},
- {TRUE, 'v', "variable", "variables"}
-};
-
typedef struct NestingLevel NestingLevel;
typedef struct NestingLevels NestingLevels;
@@ -43,16 +34,31 @@ struct NestingLevel
{
int indentation;
vString *name;
- boolean is_class;
+ int type;
};
struct NestingLevels
{
NestingLevel *levels;
- int n;
+ int n; /* number of levels in use */
int allocated;
};
+typedef enum {
+ K_CLASS, K_FUNCTION, K_MEMBER, K_VARIABLE, K_IMPORT
+} pythonKind;
+
+/*
+* DATA DEFINITIONS
+*/
+static kindOption PythonKinds[] = {
+ {TRUE, 'c', "class", "classes"},
+ {TRUE, 'f', "function", "functions"},
+ {TRUE, 'm', "member", "class members"},
+ {TRUE, 'v', "variable", "variables"},
+ {TRUE, 'i', "namespace", "imports"}
+};
+
static char const * const singletriple = "'''";
static char const * const doubletriple = "\"\"\"";
@@ -60,7 +66,60 @@ static char const * const doubletriple = "\"\"\"";
* FUNCTION DEFINITIONS
*/
-#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
+static NestingLevels *nestingLevelsNew (void)
+{
+ NestingLevels *nls = xCalloc (1, NestingLevels);
+ return nls;
+}
+
+static void nestingLevelsFree (NestingLevels *nls)
+{
+ int i;
+ for (i = 0; i < nls->allocated; i++)
+ vStringDelete(nls->levels[i].name);
+ if (nls->levels) eFree(nls->levels);
+ eFree(nls);
+}
+
+static void nestingLevelsPush (NestingLevels *nls,
+ const vString *name, int type)
+{
+ NestingLevel *nl = NULL;
+
+ if (nls->n >= nls->allocated)
+ {
+ nls->allocated++;
+ nls->levels = xRealloc(nls->levels,
+ nls->allocated, NestingLevel);
+ nls->levels[nls->n].name = vStringNew();
+ }
+ nl = &nls->levels[nls->n];
+ nls->n++;
+
+ vStringCopy(nl->name, name);
+ nl->type = type;
+}
+
+#if 0
+static NestingLevel *nestingLevelsGetCurrent (NestingLevels *nls)
+{
+ Assert (nls != NULL);
+
+ if (nls->n < 1)
+ return NULL;
+
+ return &nls->levels[nls->n - 1];
+}
+
+static void nestingLevelsPop (NestingLevels *nls)
+{
+ const NestingLevel *nl = nestingLevelsGetCurrent(nls);
+
+ Assert (nl != NULL);
+ vStringClear(nl->name);
+ nls->n--;
+}
+#endif
static boolean isIdentifierFirstCharacter (int c)
{
@@ -76,13 +135,14 @@ static boolean isIdentifierCharacter (int c)
* extract all relevant information and create a tag.
*/
static void makeFunctionTag (vString *const function,
- vString *const parent, int is_class_parent)
+ vString *const parent, int is_class_parent, const char *arglist __unused__)
{
tagEntryInfo tag;
initTagEntry (&tag, vStringValue (function));
-
+
tag.kindName = "function";
tag.kind = 'f';
+ /* tag.extensionFields.arglist = arglist; */
if (vStringLength (parent) > 0)
{
@@ -263,11 +323,82 @@ static void parseClass (const char *cp, vString *const class,
vStringDelete (inheritance);
}
+static void parseImports (const char *cp)
+{
+ const char *pos;
+ vString *name, *name_next;
+
+ cp = skipEverything (cp);
+
+ if ((pos = strstr (cp, "import")) == NULL)
+ return;
+
+ cp = pos + 6;
+
+ /* continue only if there is some space between the keyword and the identifier */
+ if (! isspace (*cp))
+ return;
+
+ cp++;
+ cp = skipSpace (cp);
+
+ name = vStringNew ();
+ name_next = vStringNew ();
+
+ cp = skipEverything (cp);
+ while (*cp)
+ {
+ cp = parseIdentifier (cp, name);
+
+ cp = skipEverything (cp);
+ /* we parse the next possible import statement as well to be able to ignore 'foo' in
+ * 'import foo as bar' */
+ parseIdentifier (cp, name_next);
+
+ /* take the current tag only if the next one is not "as" */
+ if (strcmp (vStringValue (name_next), "as") != 0 &&
+ strcmp (vStringValue (name), "as") != 0)
+ {
+ makeSimpleTag (name, PythonKinds, K_IMPORT);
+ }
+ }
+ vStringDelete (name);
+ vStringDelete (name_next);
+}
+
+/* modified from get.c getArglistFromStr().
+ * warning: terminates rest of string past arglist!
+ * note: does not ignore brackets inside strings! */
+static char *parseArglist(const char *buf)
+{
+ char *start, *end;
+ int level;
+ if (NULL == buf)
+ return NULL;
+ if (NULL == (start = strchr(buf, '(')))
+ return NULL;
+ for (level = 1, end = start + 1; level > 0; ++end)
+ {
+ if ('\0' == *end)
+ break;
+ else if ('(' == *end)
+ ++ level;
+ else if (')' == *end)
+ -- level;
+ }
+ *end = '\0';
+ return strdup(start);
+}
+
static void parseFunction (const char *cp, vString *const def,
vString *const parent, int is_class_parent)
{
+ char *arglist;
+
cp = parseIdentifier (cp, def);
- makeFunctionTag (def, parent, is_class_parent);
+ arglist = parseArglist (cp);
+ makeFunctionTag (def, parent, is_class_parent, arglist);
+ eFree (arglist);
}
/* Get the combined name of a nested symbol. Classes are separated with ".",
@@ -295,13 +426,16 @@ static boolean constructParentString(NestingLevels *nls, int indent,
break;
if (prev)
{
- if (prev->is_class)
+ vStringCatS(result, "."); /* make Geany symbol list grouping work properly */
+/*
+ if (prev->type == K_CLASS)
vStringCatS(result, ".");
else
vStringCatS(result, "/");
+*/
}
vStringCat(result, nl->name);
- is_class = nl->is_class;
+ is_class = (nl->type == K_CLASS);
prev = nl;
}
return is_class;
@@ -331,27 +465,8 @@ static void checkParent(NestingLevels *nls, int indent, vString *parent)
}
}
-static NestingLevels *newNestingLevels(void)
-{
- NestingLevels *nls = xCalloc (1, NestingLevels);
- return nls;
-}
-
-static void freeNestingLevels(NestingLevels *nls)
-{
- int i;
- for (i = 0; i < nls->allocated; i++)
- vStringDelete(nls->levels[i].name);
- if (nls->levels) eFree(nls->levels);
- eFree(nls);
-}
-
-/* TODO: This is totally out of place in python.c, but strlist.h is not usable.
- * Maybe should just move these three functions to a separate file, even if no
- * other parser uses them.
- */
static void addNestingLevel(NestingLevels *nls, int indentation,
- vString *name, boolean is_class)
+ const vString *name, boolean is_class)
{
int i;
NestingLevel *nl = NULL;
@@ -363,20 +478,16 @@ static void addNestingLevel(NestingLevels *nls, int indentation,
}
if (i == nls->n)
{
- if (i >= nls->allocated)
- {
- nls->allocated++;
- nls->levels = xRealloc(nls->levels,
- nls->allocated, NestingLevel);
- nls->levels[i].name = vStringNew();
- }
+ nestingLevelsPush(nls, name, 0);
nl = nls->levels + i;
}
- nls->n = i + 1;
-
- vStringCopy(nl->name, name);
+ else
+ { /* reuse existing slot */
+ nls->n = i + 1;
+ vStringCopy(nl->name, name);
+ }
nl->indentation = indentation;
- nl->is_class = is_class;
+ nl->type = is_class ? K_CLASS : !K_CLASS;
}
/* Return a pointer to the start of the next triple string, or NULL. Store
@@ -467,18 +578,18 @@ static const char *findVariable(const char *line)
}
/* Skip type declaration that optionally follows a cdef/cpdef */
-static const char *skipTypeDecl (const char *cp, boolean *is_class)
-{
+static const char *skipTypeDecl (const char *cp, boolean *is_class)
+{
const char *lastStart = cp, *ptr = cp;
int loopCount = 0;
ptr = skipSpace(cp);
- if (!strncmp("extern", ptr, 6)) {
- ptr += 6;
- ptr = skipSpace(ptr);
+ if (!strncmp("extern", ptr, 6)) {
+ ptr += 6;
+ ptr = skipSpace(ptr);
if (!strncmp("from", ptr, 4)) { return NULL; }
}
if (!strncmp("class", ptr, 5)) {
- ptr += 5 ;
+ ptr += 5 ;
*is_class = TRUE;
ptr = skipSpace(ptr);
return ptr;
@@ -489,7 +600,7 @@ static const char *skipTypeDecl (const char *cp, boolean *is_class)
if (!*ptr || *ptr == '=') return NULL;
if (*ptr == '(') {
return lastStart; /* if we stopped on a '(' we are done */
- }
+ }
ptr = skipSpace(ptr);
lastStart = ptr;
while (*lastStart == '*') lastStart++; /* cdef int *identifier */
@@ -503,7 +614,7 @@ static void findPythonTags (void)
vString *const name = vStringNew ();
vString *const parent = vStringNew();
- NestingLevels *const nesting_levels = newNestingLevels();
+ NestingLevels *const nesting_levels = nestingLevelsNew();
const char *line;
int line_skip = 0;
@@ -540,7 +651,7 @@ static void findPythonTags (void)
cp = skipSpace (cp);
indent = cp - line;
line_skip = 0;
-
+
checkParent(nesting_levels, indent, parent);
/* Deal with multiline string ending. */
@@ -549,7 +660,7 @@ static void findPythonTags (void)
find_triple_end(cp, &longStringLiteral);
continue;
}
-
+
/* Deal with multiline string start. */
longstring = find_triple_start(cp, &longStringLiteral);
if (longstring)
@@ -578,7 +689,7 @@ static void findPythonTags (void)
is_class = TRUE;
}
else if (!strncmp (keyword, "cdef ", 5))
- {
+ {
cp = skipSpace(keyword + 4);
candidate = skipTypeDecl (cp, &is_class);
if (candidate)
@@ -589,7 +700,7 @@ static void findPythonTags (void)
}
else if (!strncmp (keyword, "cpdef ", 6))
- {
+ {
cp = skipSpace(keyword + 5);
candidate = skipTypeDecl (cp, &is_class);
if (candidate)
@@ -636,12 +747,14 @@ static void findPythonTags (void)
makeVariableTag (name, parent);
}
+ /* Find and parse imports */
+ parseImports(line);
}
/* Clean up all memory we allocated. */
vStringDelete (parent);
vStringDelete (name);
vStringDelete (continuation);
- freeNestingLevels (nesting_levels);
+ nestingLevelsFree (nesting_levels);
}
extern parserDefinition *PythonParser (void)
diff --git a/plugins/symbol-db/anjuta-tags/read.c b/plugins/symbol-db/anjuta-tags/read.c
index ff42bf1..7940c86 100644
--- a/plugins/symbol-db/anjuta-tags/read.c
+++ b/plugins/symbol-db/anjuta-tags/read.c
@@ -1,5 +1,5 @@
/*
-* $Id: read.c 659 2008-04-20 23:27:48Z elliotth $
+* $Id: read.c 708 2009-07-04 05:29:02Z dhiebert $
*
* Copyright (c) 1996-2002, Darren Hiebert
*
@@ -449,7 +449,7 @@ extern int fileGetc (void)
return c;
}
-extern int fileSkipToCharacter (const int c)
+extern int fileSkipToCharacter (int c)
{
int d;
do
diff --git a/plugins/symbol-db/anjuta-tags/tex.c b/plugins/symbol-db/anjuta-tags/tex.c
index b467e06..a285797 100644
--- a/plugins/symbol-db/anjuta-tags/tex.c
+++ b/plugins/symbol-db/anjuta-tags/tex.c
@@ -1,12 +1,12 @@
/*
* $Id: tex.c 666 2008-05-15 17:47:31Z dfishburn $
*
- * Copyright (c) 2003, Darren Hiebert
+ * Copyright (c) 2008, David Fishburn
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
- * This module contains functions for generating tags for Tex languages.
+ * This module contains functions for generating tags for TeX language files.
*
* Tex language reference:
* http://en.wikibooks.org/wiki/TeX#The_Structure_of_TeX
diff --git a/plugins/symbol-db/anjuta-tags/vstring.h b/plugins/symbol-db/anjuta-tags/vstring.h
index 8088150..611d3a9 100644
--- a/plugins/symbol-db/anjuta-tags/vstring.h
+++ b/plugins/symbol-db/anjuta-tags/vstring.h
@@ -1,5 +1,5 @@
/*
-* $Id: vstring.h 550 2007-06-07 05:50:32Z dhiebert $
+* $Id: vstring.h 719 2009-07-07 03:46:59Z dhiebert $
*
* Copyright (c) 1998-2002, Darren Hiebert
*
@@ -35,6 +35,7 @@
#define vStringValue(vs) ((vs)->buffer)
#define vStringItem(vs,i) ((vs)->buffer[i])
+#define vStringLast(vs) ((vs)->buffer[(vs)->length - 1])
#define vStringLength(vs) ((vs)->length)
#define vStringSize(vs) ((vs)->size)
#define vStringCat(vs,s) vStringCatS((vs), vStringValue((s)))
diff --git a/plugins/symbol-db/symbol-db-engine-core.c b/plugins/symbol-db/symbol-db-engine-core.c
index 02cee54..779017d 100644
--- a/plugins/symbol-db/symbol-db-engine-core.c
+++ b/plugins/symbol-db/symbol-db-engine-core.c
@@ -5707,7 +5707,7 @@ symbol_db_engine_update_project_symbols (SymbolDBEngine *dbe,
filetm.tm_min = timestamp->minute;
filetm.tm_sec = timestamp->second;
- /* add one hour to the db_file_time. */
+ /* remove one hour to the db_file_time. */
db_time = mktime (&filetm) - 3600;
/*
DEBUG_PRINT ("%s %d ## %d", file_abs_path, db_time,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]