Sync go parser with fishman-ctags

New features include:
* struct/interface detection
* struct member parsing
* function prototype parsing
This commit is contained in:
Jiří Techet 2015-05-27 16:15:01 +02:00
parent b5702f03dc
commit e433490672
3 changed files with 220 additions and 59 deletions

View File

@ -13,6 +13,7 @@
/* /*
* MACROS * MACROS
*/ */
#define MAX_SIGNATURE_LENGTH 512
#define isType(token,t) (boolean) ((token)->type == (t)) #define isType(token,t) (boolean) ((token)->type == (t))
#define isKeyword(token,k) (boolean) ((token)->keyword == (k)) #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
@ -77,6 +78,7 @@ typedef struct sTokenInfo {
static int Lang_go; static int Lang_go;
static vString *scope; static vString *scope;
static vString *signature = NULL;
typedef enum { typedef enum {
GOTAG_UNDEFINED = -1, GOTAG_UNDEFINED = -1,
@ -85,6 +87,9 @@ typedef enum {
GOTAG_CONST, GOTAG_CONST,
GOTAG_TYPE, GOTAG_TYPE,
GOTAG_VAR, GOTAG_VAR,
GOTAG_STRUCT,
GOTAG_INTERFACE,
GOTAG_MEMBER
} goKind; } goKind;
static kindOption GoKinds[] = { static kindOption GoKinds[] = {
@ -92,7 +97,10 @@ static kindOption GoKinds[] = {
{TRUE, 'f', "function", "functions"}, {TRUE, 'f', "function", "functions"},
{TRUE, 'c', "macro", "constants"}, {TRUE, 'c', "macro", "constants"},
{TRUE, 't', "typedef", "types"}, {TRUE, 't', "typedef", "types"},
{TRUE, 'v', "variable", "variables"} {TRUE, 'v', "variable", "variables"},
{TRUE, 's', "struct", "structs"},
{TRUE, 'i', "interface", "interfaces"},
{TRUE, 'm', "member", "struct members"}
}; };
static keywordDesc GoKeywordTable[] = { static keywordDesc GoKeywordTable[] = {
@ -149,6 +157,17 @@ static tokenInfo *newToken (void)
return token; return token;
} }
static tokenInfo *copyToken (tokenInfo *other)
{
tokenInfo *const token = xMalloc (1, tokenInfo);
token->type = other->type;
token->keyword = other->keyword;
token->string = vStringNewCopy (other->string);
token->lineNumber = other->lineNumber;
token->filePosition = other->filePosition;
return token;
}
static void deleteToken (tokenInfo * const token) static void deleteToken (tokenInfo * const token)
{ {
if (token != NULL) if (token != NULL)
@ -201,6 +220,8 @@ static void readToken (tokenInfo *const token)
{ {
int c; int c;
static tokenType lastTokenType = TOKEN_NONE; static tokenType lastTokenType = TOKEN_NONE;
boolean firstWhitespace = TRUE;
boolean whitespace;
token->type = TOKEN_NONE; token->type = TOKEN_NONE;
token->keyword = KEYWORD_NONE; token->keyword = KEYWORD_NONE;
@ -219,11 +240,16 @@ getNextChar:
lastTokenType == TOKEN_CLOSE_CURLY || lastTokenType == TOKEN_CLOSE_CURLY ||
lastTokenType == TOKEN_CLOSE_SQUARE)) lastTokenType == TOKEN_CLOSE_SQUARE))
{ {
token->type = TOKEN_SEMICOLON; c = ';'; // semicolon injection
goto done; }
whitespace = c == '\t' || c == ' ' || c == '\r' || c == '\n';
if (signature && whitespace && firstWhitespace && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
{
firstWhitespace = FALSE;
vStringPut(signature, ' ');
} }
} }
while (c == '\t' || c == ' ' || c == '\r' || c == '\n'); while (whitespace);
switch (c) switch (c)
{ {
@ -354,71 +380,79 @@ getNextChar:
break; break;
} }
done: if (signature && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
{
if (token->type == TOKEN_LEFT_ARROW)
vStringCatS(signature, "<-");
else if (token->type == TOKEN_STRING)
{
// only struct member annotations can appear in function prototypes
// so only `` type strings are possible
vStringPut(signature, '`');
vStringCat(signature, token->string);
vStringPut(signature, '`');
}
else if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD)
vStringCat(signature, token->string);
else if (c != EOF)
vStringPut(signature, c);
}
lastTokenType = token->type; lastTokenType = token->type;
} }
static void skipToMatched (tokenInfo *const token) static boolean skipToMatchedNoRead (tokenInfo *const token)
{ {
int nest_level = 0; int nest_level = 0;
tokenType open_token; tokenType open_token = token->type;
tokenType close_token; tokenType close_token;
switch (token->type) switch (open_token)
{ {
case TOKEN_OPEN_PAREN: case TOKEN_OPEN_PAREN:
open_token = TOKEN_OPEN_PAREN;
close_token = TOKEN_CLOSE_PAREN; close_token = TOKEN_CLOSE_PAREN;
break; break;
case TOKEN_OPEN_CURLY: case TOKEN_OPEN_CURLY:
open_token = TOKEN_OPEN_CURLY;
close_token = TOKEN_CLOSE_CURLY; close_token = TOKEN_CLOSE_CURLY;
break; break;
case TOKEN_OPEN_SQUARE: case TOKEN_OPEN_SQUARE:
open_token = TOKEN_OPEN_SQUARE;
close_token = TOKEN_CLOSE_SQUARE; close_token = TOKEN_CLOSE_SQUARE;
break; break;
default: default:
return; return FALSE;
} }
/* /*
* This routine will skip to a matching closing token. * This routine will skip to a matching closing token.
* It will also handle nested tokens like the (, ) below. * It will also handle nested tokens.
* ( name varchar(30), text binary(10) )
*/ */
if (isType (token, open_token)) nest_level++;
while (nest_level > 0 && !isType (token, TOKEN_EOF))
{ {
nest_level++;
while (!(isType (token, close_token) && (nest_level == 0)) &&
!isType (token, TOKEN_EOF))
{
readToken (token);
if (isType (token, open_token))
{
nest_level++;
}
if (isType (token, close_token))
{
if (nest_level > 0)
{
nest_level--;
}
}
}
readToken (token); readToken (token);
if (isType (token, open_token))
nest_level++;
else if (isType (token, close_token))
nest_level--;
} }
return TRUE;
} }
static void skipType (tokenInfo *const token) static void skipToMatched (tokenInfo *const token)
{
if (skipToMatchedNoRead (token))
readToken (token);
}
static boolean skipType (tokenInfo *const token)
{ {
again:
// Type = TypeName | TypeLit | "(" Type ")" . // Type = TypeName | TypeLit | "(" Type ")" .
// Skips also function multiple return values "(" Type {"," Type} ")" // Skips also function multiple return values "(" Type {"," Type} ")"
if (isType (token, TOKEN_OPEN_PAREN)) if (isType (token, TOKEN_OPEN_PAREN))
{ {
skipToMatched (token); skipToMatched (token);
return; return TRUE;
} }
// TypeName = QualifiedIdent. // TypeName = QualifiedIdent.
@ -433,7 +467,7 @@ again:
if (isType (token, TOKEN_IDENTIFIER)) if (isType (token, TOKEN_IDENTIFIER))
readToken (token); readToken (token);
} }
return; return TRUE;
} }
// StructType = "struct" "{" { FieldDecl ";" } "}" // StructType = "struct" "{" { FieldDecl ";" } "}"
@ -443,7 +477,7 @@ again:
readToken (token); readToken (token);
// skip over "{}" // skip over "{}"
skipToMatched (token); skipToMatched (token);
return; return TRUE;
} }
// ArrayType = "[" ArrayLength "]" ElementType . // ArrayType = "[" ArrayLength "]" ElementType .
@ -452,7 +486,7 @@ again:
if (isType (token, TOKEN_OPEN_SQUARE)) if (isType (token, TOKEN_OPEN_SQUARE))
{ {
skipToMatched (token); skipToMatched (token);
goto again; return skipType (token);
} }
// PointerType = "*" BaseType . // PointerType = "*" BaseType .
@ -461,7 +495,7 @@ again:
if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW)) if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
{ {
readToken (token); readToken (token);
goto again; return skipType (token);
} }
// MapType = "map" "[" KeyType "]" ElementType . // MapType = "map" "[" KeyType "]" ElementType .
@ -471,7 +505,7 @@ again:
readToken (token); readToken (token);
// skip over "[]" // skip over "[]"
skipToMatched (token); skipToMatched (token);
goto again; return skipType (token);
} }
// FunctionType = "func" Signature . // FunctionType = "func" Signature .
@ -486,11 +520,15 @@ again:
// Result is parameters or type or nothing. skipType treats anything // Result is parameters or type or nothing. skipType treats anything
// surrounded by parentheses as a type, and does nothing if what // surrounded by parentheses as a type, and does nothing if what
// follows is not a type. // follows is not a type.
goto again; return skipType (token);
} }
return FALSE;
} }
static void makeTag (tokenInfo *const token, const goKind kind) static void makeTag (tokenInfo *const token, const goKind kind,
tokenInfo *const parent_token, const goKind parent_kind,
const char *argList)
{ {
const char *const name = vStringValue (token->string); const char *const name = vStringValue (token->string);
@ -504,7 +542,14 @@ static void makeTag (tokenInfo *const token, const goKind kind)
e.filePosition = token->filePosition; e.filePosition = token->filePosition;
e.kindName = GoKinds [kind].name; e.kindName = GoKinds [kind].name;
e.kind = GoKinds [kind].letter; e.kind = GoKinds [kind].letter;
if (argList)
e.extensionFields.arglist = argList;
if (parent_kind != GOTAG_UNDEFINED && parent_token != NULL)
{
e.extensionFields.scope[0] = GoKinds[parent_kind].name;
e.extensionFields.scope[1] = vStringValue (parent_token->string);
}
makeTagEntry (&e); makeTagEntry (&e);
if (scope && Option.include.qualifiedTags) if (scope && Option.include.qualifiedTags)
@ -524,7 +569,7 @@ static void parsePackage (tokenInfo *const token)
readToken (token); readToken (token);
if (isType (token, TOKEN_IDENTIFIER)) if (isType (token, TOKEN_IDENTIFIER))
{ {
makeTag (token, GOTAG_PACKAGE); makeTag (token, GOTAG_PACKAGE, NULL, GOTAG_UNDEFINED, NULL);
if (!scope && Option.include.qualifiedTags) if (!scope && Option.include.qualifiedTags)
{ {
scope = vStringNew (); scope = vStringNew ();
@ -549,11 +594,25 @@ static void parseFunctionOrMethod (tokenInfo *const token)
if (isType (token, TOKEN_IDENTIFIER)) if (isType (token, TOKEN_IDENTIFIER))
{ {
makeTag (token, GOTAG_FUNCTION); tokenInfo *functionToken = copyToken (token);
// Start recording signature
signature = vStringNew ();
// Skip over parameters. // Skip over parameters.
readToken (token); readToken (token);
skipToMatched (token); skipToMatchedNoRead (token);
vStringStripLeading (signature);
vStringStripTrailing (signature);
makeTag (functionToken, GOTAG_FUNCTION, NULL, GOTAG_UNDEFINED, signature->buffer);
deleteToken (functionToken);
vStringDelete(signature);
// Stop recording signature
signature = NULL;
readToken (token);
// Skip over result. // Skip over result.
skipType (token); skipType (token);
@ -564,6 +623,75 @@ static void parseFunctionOrMethod (tokenInfo *const token)
} }
} }
static void parseStructMembers (tokenInfo *const token, tokenInfo *const parent_token)
{
// StructType = "struct" "{" { FieldDecl ";" } "}" .
// FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
// AnonymousField = [ "*" ] TypeName .
// Tag = string_lit .
readToken (token);
if (!isType (token, TOKEN_OPEN_CURLY))
return;
readToken (token);
while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
{
tokenInfo *memberCandidate = NULL;
boolean first = TRUE;
while (!isType (token, TOKEN_EOF))
{
if (isType (token, TOKEN_IDENTIFIER))
{
if (first)
{
// could be anonymous field like in 'struct {int}' - we don't know yet
memberCandidate = copyToken (token);
first = FALSE;
}
else
{
if (memberCandidate)
{
// if we are here, there was a comma and memberCandidate isn't an anonymous field
makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
deleteToken (memberCandidate);
memberCandidate = NULL;
}
makeTag (token, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
}
readToken (token);
}
if (!isType (token, TOKEN_COMMA))
break;
readToken (token);
}
// in the case of an anonymous field, we already read part of the
// type into memberCandidate and skipType() should return FALSE so no tag should
// be generated in this case.
if (skipType (token) && memberCandidate)
makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
if (memberCandidate)
deleteToken (memberCandidate);
while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_CURLY)
&& !isType (token, TOKEN_EOF))
{
readToken (token);
skipToMatched (token);
}
if (!isType (token, TOKEN_CLOSE_CURLY))
{
// we are at TOKEN_SEMICOLON
readToken (token);
}
}
}
static void parseConstTypeVar (tokenInfo *const token, goKind kind) static void parseConstTypeVar (tokenInfo *const token, goKind kind)
{ {
// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) . // ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
@ -586,11 +714,26 @@ static void parseConstTypeVar (tokenInfo *const token, goKind kind)
do do
{ {
tokenInfo *typeToken = NULL;
while (!isType (token, TOKEN_EOF)) while (!isType (token, TOKEN_EOF))
{ {
if (isType (token, TOKEN_IDENTIFIER)) if (isType (token, TOKEN_IDENTIFIER))
{ {
makeTag (token, kind); if (kind == GOTAG_TYPE)
{
typeToken = copyToken (token);
readToken (token);
if (isKeyword (token, KEYWORD_struct))
makeTag (typeToken, GOTAG_STRUCT, NULL, GOTAG_UNDEFINED, NULL);
else if (isKeyword (token, KEYWORD_interface))
makeTag (typeToken, GOTAG_INTERFACE, NULL, GOTAG_UNDEFINED, NULL);
else
makeTag (typeToken, kind, NULL, GOTAG_UNDEFINED, NULL);
break;
}
else
makeTag (token, kind, NULL, GOTAG_UNDEFINED, NULL);
readToken (token); readToken (token);
} }
if (!isType (token, TOKEN_COMMA)) if (!isType (token, TOKEN_COMMA))
@ -598,7 +741,17 @@ static void parseConstTypeVar (tokenInfo *const token, goKind kind)
readToken (token); readToken (token);
} }
skipType (token); if (typeToken)
{
if (isKeyword (token, KEYWORD_struct))
parseStructMembers (token, typeToken);
else
skipType (token);
deleteToken (typeToken);
}
else
skipType (token);
while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN) while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN)
&& !isType (token, TOKEN_EOF)) && !isType (token, TOKEN_EOF))
{ {

View File

@ -15,8 +15,11 @@ type (
) )
type T6 struct { type T6 struct {
a, b, c, d int _a, _b, _c, _d int
e float32 int
T1 `annotation`
*T2
_e float32
//ignored int //ignored int
} }
@ -44,4 +47,4 @@ func (tt * T7) f4(a func () func ()) (func (), int) {return func (){}, 1};func f
func main() { func main() {
go func (){}() go func (){}()
fmt.Println("Hello, 世界") fmt.Println("Hello, 世界")
} }

View File

@ -12,23 +12,28 @@ T1
T2フ4096ヨ0 T2フ4096ヨ0
T3フ4096ヨ0 T3フ4096ヨ0
T4フ4096ヨ0 T4フ4096ヨ0
T5フ4096ヨ0 T5フ32ヨ0
T6フ4096ヨ0 T6フ2048ヨ0
T7フ4096ヨ0 T7フ4096ヨ0
T8フ4096ヨ0 T8フ4096ヨ0
T9フ4096ヨ0 T9フ4096ヨ0
_aフ64ホT6ヨ0
_bフ64ホT6ヨ0
_cフ64ホT6ヨ0
_dフ64ホT6ヨ0
_eフ64ホT6ヨ0
aフ16384ヨ0 aフ16384ヨ0
bフ16384ヨ0 bフ16384ヨ0
cフ16384ヨ0 cフ16384ヨ0
dフ16384ヨ0 dフ16384ヨ0
eフ16384ヨ0 eフ16384ヨ0
fフ16384ヨ0 fフ16384ヨ0
f1フ16ヨ0 f1フ16ヘ()ヨ0
f2フ16ヨ0 f2フ16ヘ()ヨ0
f3フ16ヨ0 f3フ16ヘ()ヨ0
f4フ16ヨ0 f4フ16ヘ(a func () func ())ヨ0
f5フ16ヨ0 f5フ16ヘ()ヨ0
gフ16384ヨ0 gフ16384ヨ0
hフ16384ヨ0 hフ16384ヨ0
mainフ16ヨ0 mainフ16ヘ()ヨ0
mainフ256ヨ0 mainフ256ヨ0