From 1593689d09cfb3f837c29991fd8836fac9edca81 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 2 Apr 2019 16:28:21 +0900 Subject: [PATCH 1/4] main: fix a bug not calling inputStart() method of a subparser If a subparser uses addLanguageCallbackRegex, ctags didn't call a inputStart() subparser method. This is a bug. ctags should not call inputStart() of a subparser if METHOD_NOT_CRAFTED is set to the method field of the subparser. There is a critical typo to extract the it for METHOD_NOT_CRAFTED from a flag. Signed-off-by: Masatake YAMATO --- main/parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/parse.c b/main/parse.c index f4672a436a..b2c6037346 100644 --- a/main/parse.c +++ b/main/parse.c @@ -4146,7 +4146,7 @@ extern subparser *getNextSubparser(subparser *last, t = getSubparserLanguage(r); if (isLanguageEnabled (t) && (includingNoneCraftedParser - || ((((LanguageTable + t)->def->method) && METHOD_NOT_CRAFTED) == 0))) + || ((((LanguageTable + t)->def->method) & METHOD_NOT_CRAFTED) == 0))) return r; else return getNextSubparser (r, includingNoneCraftedParser); From e3ee5398c7c7d65678f5422a325c57510c8f3bf3 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 2 Apr 2019 05:24:29 +0900 Subject: [PATCH 2/4] TclOO: make tcloo subparser instance file local Signed-off-by: Masatake YAMATO --- parsers/tcloo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parsers/tcloo.c b/parsers/tcloo.c index fd3225ca95..648cbe24ea 100644 --- a/parsers/tcloo.c +++ b/parsers/tcloo.c @@ -144,7 +144,7 @@ static void inputStart (subparser *s) tcloo->foundTclOONamespaceImported = false; } -struct tclooSubparser tclooSubparser = { +static struct tclooSubparser tclooSubparser = { .tcl = { .subparser = { .direction = SUBPARSER_BI_DIRECTION, From cd677fe636bc07ff6d6d8ec1725f09f858b5f59a Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 2 Apr 2019 05:28:15 +0900 Subject: [PATCH 3/4] Perl: export subparser interface Signed-off-by: Masatake YAMATO --- parsers/perl.c | 84 +++++++++++++++++++++--------- parsers/perl.h | 35 +++++++++++++ source.mak | 1 + win32/ctags_vs2013.vcxproj | 1 + win32/ctags_vs2013.vcxproj.filters | 3 ++ 5 files changed, 99 insertions(+), 25 deletions(-) create mode 100644 parsers/perl.h diff --git a/parsers/perl.c b/parsers/perl.c index 87969ba27c..7ac02b72f2 100644 --- a/parsers/perl.c +++ b/parsers/perl.c @@ -16,10 +16,12 @@ #include #include "entry.h" +#include "perl.h" #include "promise.h" #include "read.h" #include "routines.h" #include "selectors.h" +#include "subparser.h" #include "vstring.h" #include "xtag.h" @@ -29,15 +31,7 @@ /* * DATA DEFINITIONS */ -typedef enum { - K_NONE = -1, - K_CONSTANT, - K_FORMAT, - K_LABEL, - K_PACKAGE, - K_SUBROUTINE, - K_SUBROUTINE_DECLARATION -} perlKind; +typedef enum PerlKindType perlKind; static kindDefinition PerlKinds [] = { { true, 'c', "constant", "constants" }, @@ -52,6 +46,38 @@ static kindDefinition PerlKinds [] = { * FUNCTION DEFINITIONS */ +static void notifyEntringPod () +{ + subparser *sub; + + foreachSubparser (sub, false) + { + perlSubparser *perlsub = (perlSubparser *)sub; + if (perlsub->enteringPodNotify) + { + enterSubparser (sub); + perlsub->enteringPodNotify (perlsub); + leaveSubparser (); + } + } +} + +static void notifyLeavingPod () +{ + subparser *sub; + + foreachSubparser (sub, false) + { + perlSubparser *perlsub = (perlSubparser *)sub; + if (perlsub->leavingPodNotify) + { + enterSubparser (sub); + perlsub->leavingPodNotify (perlsub); + leaveSubparser (); + } + } +} + static bool isIdentifier1 (int c) { return (bool) (isalpha (c) || c == '_'); @@ -178,7 +204,7 @@ static void makeTagFromLeftSide (const char *begin, const char *end, { tagEntryInfo entry; const char *b, *e; - if (! PerlKinds[K_CONSTANT].enabled) + if (! PerlKinds[KIND_PERL_CONSTANT].enabled) return; for (e = end - 1; e > begin && isspace(*e); --e) ; @@ -197,13 +223,13 @@ static void makeTagFromLeftSide (const char *begin, const char *end, return; /* Left side of => has an invalid identifier. */ vStringClear(name); vStringNCatS(name, b, e - b + 1); - initTagEntry(&entry, vStringValue(name), K_CONSTANT); + initTagEntry(&entry, vStringValue(name), KIND_PERL_CONSTANT); makeTagEntry(&entry); if (isXtagEnabled (XTAG_QUALIFIED_TAGS) && package && vStringLength(package)) { vStringClear(name); vStringCopy(name, package); vStringNCatS(name, b, e - b + 1); - initTagEntry(&entry, vStringValue(name), K_CONSTANT); + initTagEntry(&entry, vStringValue(name), KIND_PERL_CONSTANT); markTagExtraBit (&entry, XTAG_QUALIFIED_TAGS); makeTagEntry(&entry); } @@ -291,7 +317,7 @@ static void findPerlTags (void) bool spaceRequired = false; bool qualified = false; const unsigned char *cp = line; - perlKind kind = K_NONE; + perlKind kind = KIND_PERL_NONE; tagEntryInfo e; if (skipPodDoc) @@ -301,6 +327,7 @@ static void findPerlTags (void) skipPodDoc = false; if (podStart != 0UL) { + notifyLeavingPod (); makePromise ("Pod", podStart, 0, getInputLineNumber(), 0, @@ -314,7 +341,10 @@ static void findPerlTags (void) { skipPodDoc = isPodWord ((const char*)line + 1); if (skipPodDoc) + { podStart = getSourceLineNumber (); + notifyEntringPod (); + } continue; } else if (strcmp ((const char*) line, "__DATA__") == 0) @@ -341,7 +371,7 @@ static void findPerlTags (void) { TRACE("this looks like a sub\n"); cp += 3; - kind = K_SUBROUTINE; + kind = KIND_PERL_SUBROUTINE; spaceRequired = true; qualified = true; } @@ -383,7 +413,7 @@ static void findPerlTags (void) } else goto END_MAIN_WHILE; } - kind = K_CONSTANT; + kind = KIND_PERL_CONSTANT; spaceRequired = false; qualified = true; } @@ -413,14 +443,14 @@ static void findPerlTags (void) vStringCatS (package, "::"); cp = first; /* Rewind */ - kind = K_PACKAGE; + kind = KIND_PERL_PACKAGE; spaceRequired = false; qualified = true; } else if (strncmp((const char*) cp, "format", (size_t) 6) == 0) { cp += 6; - kind = K_FORMAT; + kind = KIND_PERL_FORMAT; spaceRequired = true; qualified = true; } @@ -434,10 +464,10 @@ static void findPerlTags (void) while (isspace (*p)) ++p; if ((int) *p == ':' && (int) *(p + 1) != ':') - kind = K_LABEL; + kind = KIND_PERL_LABEL; } } - if (kind != K_NONE) + if (kind != KIND_PERL_NONE) { TRACE("cp0: %s\n", (const char *) cp); if (spaceRequired && *cp && !isspace (*cp)) @@ -456,13 +486,13 @@ static void findPerlTags (void) cp++; } - while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp)) + while (isIdentifier (*cp) || (KIND_PERL_PACKAGE == kind && ':' == *cp)) { vStringPut (name, (int) *cp); cp++; } - if (K_FORMAT == kind && + if (KIND_PERL_FORMAT == kind && vStringLength (name) == 0 && /* cp did not advance */ '=' == *cp) { @@ -478,7 +508,7 @@ static void findPerlTags (void) continue; } - if (K_SUBROUTINE == kind) + if (KIND_PERL_SUBROUTINE == kind) { /* * isSubroutineDeclaration() may consume several lines. So @@ -487,8 +517,8 @@ static void findPerlTags (void) initTagEntry(&e, vStringValue(name), KIND_GHOST_INDEX); if (true == isSubroutineDeclaration(cp)) { - if (true == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) { - kind = K_SUBROUTINE_DECLARATION; + if (true == PerlKinds[KIND_PERL_SUBROUTINE_DECLARATION].enabled) { + kind = KIND_PERL_SUBROUTINE_DECLARATION; } else { vStringClear (name); continue; @@ -516,7 +546,7 @@ static void findPerlTags (void) { makeSimpleTag (name, kind); if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified && - K_PACKAGE != kind && + KIND_PERL_PACKAGE != kind && package != NULL && vStringLength (package) > 0) { tagEntryInfo fqe; @@ -550,5 +580,9 @@ extern parserDefinition* PerlParser (void) def->extensions = extensions; def->parser = findPerlTags; def->selectLanguage = selectors; + + /* Subparsers need this */ + def->useCork = true; + return def; } diff --git a/parsers/perl.h b/parsers/perl.h new file mode 100644 index 0000000000..47b9c58a6b --- /dev/null +++ b/parsers/perl.h @@ -0,0 +1,35 @@ +/* +* Copyright (c) 2019, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +*/ +#ifndef CTAGS_PARSER_PERL_H +#define CTAGS_PARSER_PERL_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "subparser.h" + +typedef struct sPerlSubparser perlSubparser; + +enum PerlKindType { + KIND_PERL_NONE = -1, + KIND_PERL_CONSTANT, + KIND_PERL_FORMAT, + KIND_PERL_LABEL, + KIND_PERL_PACKAGE, + KIND_PERL_SUBROUTINE, + KIND_PERL_SUBROUTINE_DECLARATION +}; + +struct sPerlSubparser { + subparser subparser; + void (* enteringPodNotify) (perlSubparser *); + void (* leavingPodNotify) (perlSubparser *); +}; + +#endif /* CTAGS_PARSER_PERL_H */ diff --git a/source.mak b/source.mak index 1073f3cc3d..0601eb3734 100644 --- a/source.mak +++ b/source.mak @@ -162,6 +162,7 @@ PARSER_HEADS = \ parsers/iniconf.h \ parsers/m4.h \ parsers/make.h \ + parsers/perl.h \ parsers/tcl.h \ \ $(NULL) diff --git a/win32/ctags_vs2013.vcxproj b/win32/ctags_vs2013.vcxproj index 9884852206..d4e8e00e9d 100644 --- a/win32/ctags_vs2013.vcxproj +++ b/win32/ctags_vs2013.vcxproj @@ -326,6 +326,7 @@ + diff --git a/win32/ctags_vs2013.vcxproj.filters b/win32/ctags_vs2013.vcxproj.filters index fda10a27e6..f3f8e690e6 100644 --- a/win32/ctags_vs2013.vcxproj.filters +++ b/win32/ctags_vs2013.vcxproj.filters @@ -731,6 +731,9 @@ Header Files + + Header Files + Header Files From dbbfa047fdf460c27c5c3ae244a0b8907e2faf01 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 2 Apr 2019 05:29:19 +0900 Subject: [PATCH 4/4] Moose: new subparser running on Perl parser @EvanCarroll suggested me various Moose notation. Signed-off-by: Masatake YAMATO --- .../stdout-expected.txt | 1 + Tmain/list-fields.d/stdout-expected.txt | 1 + .../list-subparsers-all.d/stdout-expected.txt | 1 + Units/simple-moose.d/args.ctags | 3 + Units/simple-moose.d/expected.tags | 32 ++ Units/simple-moose.d/input.pl | 58 ++ docs/news.rst | 1 + main/parsers_p.h | 1 + parsers/moose.c | 510 ++++++++++++++++++ source.mak | 1 + win32/ctags_vs2013.vcxproj | 1 + win32/ctags_vs2013.vcxproj.filters | 3 + 12 files changed, 613 insertions(+) create mode 100644 Units/simple-moose.d/args.ctags create mode 100644 Units/simple-moose.d/expected.tags create mode 100644 Units/simple-moose.d/input.pl create mode 100644 parsers/moose.c diff --git a/Tmain/list-fields-with-prefix.d/stdout-expected.txt b/Tmain/list-fields-with-prefix.d/stdout-expected.txt index d49412244d..7ad25c7910 100644 --- a/Tmain/list-fields-with-prefix.d/stdout-expected.txt +++ b/Tmain/list-fields-with-prefix.d/stdout-expected.txt @@ -17,6 +17,7 @@ x UCTAGSxpath no NONE s-- no xpath for the - UCTAGSpackageName yes Go s-- no the name for referring the package - UCTAGSassignment yes LdScript s-- no how a value is assigned to the symbol - UCTAGSsectionMarker no Markdown s-- no character used for declaring section(#, ##, =, or -) +- UCTAGSwrapping yes Moose s-- no how a wrapper wrapping the method (around, after, or before) - UCTAGScategory yes ObjectiveC s-- no category attached to the class - UCTAGSprotocols yes ObjectiveC s-- no protocols that the class (or category) confirms to - UCTAGShome yes Passwd s-- no home directory diff --git a/Tmain/list-fields.d/stdout-expected.txt b/Tmain/list-fields.d/stdout-expected.txt index 4e393171b1..c35cd0efdb 100644 --- a/Tmain/list-fields.d/stdout-expected.txt +++ b/Tmain/list-fields.d/stdout-expected.txt @@ -35,6 +35,7 @@ z kind no NONE s-- no Include the "kind:" key in kind field (use k or K) in tags - packageName yes Go s-- no the name for referring the package - assignment yes LdScript s-- no how a value is assigned to the symbol - sectionMarker no Markdown s-- no character used for declaring section(#, ##, =, or -) +- wrapping yes Moose s-- no how a wrapper wrapping the method (around, after, or before) - category yes ObjectiveC s-- no category attached to the class - protocols yes ObjectiveC s-- no protocols that the class (or category) confirms to - home yes Passwd s-- no home directory diff --git a/Tmain/list-subparsers-all.d/stdout-expected.txt b/Tmain/list-subparsers-all.d/stdout-expected.txt index bc0bd17e32..300beae6e9 100644 --- a/Tmain/list-subparsers-all.d/stdout-expected.txt +++ b/Tmain/list-subparsers-all.d/stdout-expected.txt @@ -2,6 +2,7 @@ Autoconf M4 base <> sub {bidirectional} Automake Make base <= sub {dedicated} ITcl Tcl base <> sub {bidirectional} +Moose Perl base <> sub {bidirectional} PythonLoggingConfig Iniconf base <> sub {bidirectional} QtMoc C++ base <> sub {bidirectional} RSpec Ruby base => sub {shared} diff --git a/Units/simple-moose.d/args.ctags b/Units/simple-moose.d/args.ctags new file mode 100644 index 0000000000..b3b2465123 --- /dev/null +++ b/Units/simple-moose.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--fields=+lineK +--extras=+s diff --git a/Units/simple-moose.d/expected.tags b/Units/simple-moose.d/expected.tags new file mode 100644 index 0000000000..e8c54dc378 --- /dev/null +++ b/Units/simple-moose.d/expected.tags @@ -0,0 +1,32 @@ +Point input.pl /^package Point;$/;" package line:2 language:Perl +Point input.pl /^package Point;$/;" class line:2 language:Moose +x input.pl /^has 'x' => (is => 'rw', isa => 'Int');$/;" attribute line:5 language:Moose class:Point +y input.pl /^has 'y' => (is => 'rw', isa => 'Int');$/;" attribute line:6 language:Moose class:Point +z0 input.pl /^has z0 => (is => 'rw');$/;" attribute line:8 language:Moose class:Point +z1 input.pl /^has 'z1' => (is => 'rw');$/;" attribute line:9 language:Moose class:Point +z2 input.pl /^has "z2" => (is => 'rw');$/;" attribute line:10 language:Moose class:Point +z0p input.pl /^has (z0p => (is => 'rw'));$/;" attribute line:12 language:Moose class:Point +z1p input.pl /^has ('z1p' => (is => 'rw'));$/;" attribute line:13 language:Moose class:Point +z2p input.pl /^has ("z2p" => (is => 'rw'));$/;" attribute line:14 language:Moose class:Point +z0ps input.pl /^has ( z0ps => (is => 'rw'));$/;" attribute line:16 language:Moose class:Point +z1ps input.pl /^has ( 'z1ps' => (is => 'rw'));$/;" attribute line:17 language:Moose class:Point +z2ps input.pl /^has ( "z2ps" => (is => 'rw'));$/;" attribute line:18 language:Moose class:Point +z3 input.pl /^has [qw#z3 z4 z5#] => ((is => 'rw'),(is => 'rw'),(is => 'rw'));$/;" attribute line:20 language:Moose class:Point +z4 input.pl /^has [qw#z3 z4 z5#] => ((is => 'rw'),(is => 'rw'),(is => 'rw'));$/;" attribute line:20 language:Moose class:Point +z5 input.pl /^has [qw#z3 z4 z5#] => ((is => 'rw'),(is => 'rw'),(is => 'rw'));$/;" attribute line:20 language:Moose class:Point +z6 input.pl /^has ([qw|z6 z7|] => (is => 'rw'), (is => 'rw'));$/;" attribute line:21 language:Moose class:Point +z7 input.pl /^has ([qw|z6 z7|] => (is => 'rw'), (is => 'rw'));$/;" attribute line:21 language:Moose class:Point +clear input.pl /^sub clear {$/;" subroutine line:23 language:Perl +clear input.pl /^sub clear {$/;" method line:23 language:Moose class:Point +Point3D input.pl /^package Point3D;$/;" package line:29 language:Perl +Point3D input.pl /^package Point3D;$/;" class line:29 language:Moose inherits:Point +z input.pl /^has 'z' => (is => 'rw', isa => 'Int');$/;" attribute line:34 language:Moose class:Point3D +clear input.pl /^before 'clear' => sub {$/;" wrapper line:36 language:Moose class:Point3D wrapping:before +clear input.pl /^after 'clear' => sub {$/;" wrapper line:39 language:Moose class:Point3D wrapping:after +clear input.pl /^around 'clear' => sub {$/;" wrapper line:42 language:Moose class:Point3D wrapping:around +TimeAxis input.pl /^package TimeAxis;$/;" package line:45 language:Perl +TimeAxis input.pl /^package TimeAxis;$/;" class line:45 language:Moose +Point4D input.pl /^package Point4D;$/;" package line:48 language:Perl +Point4D input.pl /^package Point4D;$/;" class line:48 language:Moose inherits:Point3D,TimeAxis end:57 +clear input.pl /^override "clear" => sub {$/;" method line:54 language:Moose class:Point4D +Line input.pl /^package Line;$/;" package line:58 language:Perl diff --git a/Units/simple-moose.d/input.pl b/Units/simple-moose.d/input.pl new file mode 100644 index 0000000000..8fbd842ecc --- /dev/null +++ b/Units/simple-moose.d/input.pl @@ -0,0 +1,58 @@ +# Taken from https://metacpan.org/pod/Moose +package Point; +use Moose; # automatically turns on strict and warnings + +has 'x' => (is => 'rw', isa => 'Int'); +has 'y' => (is => 'rw', isa => 'Int'); + +has z0 => (is => 'rw'); +has 'z1' => (is => 'rw'); +has "z2" => (is => 'rw'); + +has (z0p => (is => 'rw')); +has ('z1p' => (is => 'rw')); +has ("z2p" => (is => 'rw')); + +has ( z0ps => (is => 'rw')); +has ( 'z1ps' => (is => 'rw')); +has ( "z2ps" => (is => 'rw')); + +has [qw#z3 z4 z5#] => ((is => 'rw'),(is => 'rw'),(is => 'rw')); +has ([qw|z6 z7|] => (is => 'rw'), (is => 'rw')); + +sub clear { + my $self = shift; + $self->x(0); + $self->y(0); +} + +package Point3D; +use Moose; + +extends 'Point'; + +has 'z' => (is => 'rw', isa => 'Int'); + +before 'clear' => sub { +}; + +after 'clear' => sub { +}; + +around 'clear' => sub { +}; + +package TimeAxis; +use Moose; + +package Point4D; +use Moose; + +extends 'Point3D', + 'TimeAxis' ; + +override "clear" => sub { +}; + +no Moose; +package Line; diff --git a/docs/news.rst b/docs/news.rst index 480c9964a9..dad1e70047 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -64,6 +64,7 @@ The following parsers have been added: * Man page *optlib* * Markdown *optlib* * Maven2 *libxml* +* Moose *perl basesd subperser* * M4 * ObjectiveC * Passwd *optlib* diff --git a/main/parsers_p.h b/main/parsers_p.h index 7e86b25c2c..f2276927b8 100644 --- a/main/parsers_p.h +++ b/main/parsers_p.h @@ -94,6 +94,7 @@ MakefileParser, \ MarkdownParser, \ MatLabParser, \ + MooseParser, \ MyrddinParser, \ ObjcParser, \ OldCppParser, \ diff --git a/parsers/moose.c b/parsers/moose.c new file mode 100644 index 0000000000..1d7aaf100f --- /dev/null +++ b/parsers/moose.c @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2019, Masatake YAMATO + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * This module contains functions for generating tags for Moose perl extension. + * https://metacpan.org/pod/Moose + */ + +/* NOTE about kind/role design: + * + * sub foo { ... } + * after 'foo' => sub { ...} + * + * There were two ideas to capture 'foo': + * + * A: capturing 'foo' as a reference tag with 'method' kind and 'wrapped' role, and + * B: capturing 'foo' as a definition tag with 'wrapper' kind. + * + * This implementation takes idea B. */ + +/* + * INCLUDE FILES + */ +#include "general.h" /* must always come first */ + +#include "debug.h" +#include "entry.h" +#include "kind.h" +#include "parse.h" +#include "perl.h" +#include "read.h" +#include "routines.h" +#include "trace.h" + +/* + * DATA DECLARATIONS + */ + +enum MooseKind { + K_CLASS, + K_METHOD, + K_ATTR, + K_WRAPPER, +}; + +static kindDefinition MooseKinds[] = { + { true, 'c', "class", "classes" }, + { true, 'm', "method", "methods" }, + { true, 'a', "attribute", "attributes" }, + { true, 'w', "wrapper", "wrappers" }, +}; + +typedef enum { + F_WRAPPING, +} MooseField; + +static fieldDefinition MooseFields [] = { + { + .name = "wrapping", + .description = "how a wrapper wrapping the method (around, after, or before)", + .enabled = true, + }, +}; + +enum Wrapping { + W_UNKNOWN, + W_AROUND, + W_BEFORE, + W_AFTER, +}; + +static char * WrappingStrings[] = { + "unknown", + "around", + "before", + "after", +}; + +struct mooseSubparser { + perlSubparser perl; + bool notInMoose; + bool inPod; + int packageCork; + int classCork; + bool notContinuousExtendsLines; + const char *superClass; +}; + +/* + * FUNCTION PROTOTYPES + */ +static void inputStart (subparser *s); +static void inputEnd (subparser *s); +static void makeTagEntryNotify (subparser *s, const tagEntryInfo *tag, int corkIndex); +static void enteringPodNotify (perlSubparser *perl); +static void leavingPodNotify (perlSubparser *perl); + +/* + * DATA DEFINITIONS + */ + +static struct mooseSubparser mooseSubparser = { + .perl = { + .subparser = { + .direction = SUBPARSER_BI_DIRECTION, + .inputStart = inputStart, + .inputEnd = inputEnd, + .makeTagEntryNotify = makeTagEntryNotify, + }, + .enteringPodNotify = enteringPodNotify, + .leavingPodNotify = leavingPodNotify, + }, +}; + + +/* + * FUNCTION DEFINITIONS + */ + +static void inputStart (subparser *s) +{ + struct mooseSubparser *moose = (struct mooseSubparser *)s; + + moose->notInMoose = true; + moose->packageCork = CORK_NIL; + moose->classCork = CORK_NIL; + moose->inPod = false; + moose->superClass = NULL; + moose->notContinuousExtendsLines = true; +} + +static void inputEnd (subparser *s) +{ + struct mooseSubparser *moose = (struct mooseSubparser *)s; + + if (moose->classCork != CORK_NIL) + { + tagEntryInfo *e = getEntryInCorkQueue (moose->classCork); + Assert (e); + e->extensionFields.endLine = getInputLineNumber (); + } + + if (moose->superClass) + eFree ((char *)moose->superClass); +} + +static void makeTagEntryNotify (subparser *s, const tagEntryInfo *tag, int corkIndex) +{ + perlSubparser *perl = (perlSubparser *)s; + struct mooseSubparser *moose = (struct mooseSubparser *)perl; + + if (isTagExtraBitMarked(tag, XTAG_QUALIFIED_TAGS)) + return; + + if (tag->kindIndex == KIND_PERL_PACKAGE) + moose->packageCork = corkIndex; + else if ((!moose->notInMoose) && tag->kindIndex == KIND_PERL_SUBROUTINE) + { + tagEntryInfo moose_e; + initTagEntry (&moose_e, tag->name, K_METHOD); + moose_e.extensionFields.scopeIndex = moose->classCork; + makeTagEntry (&moose_e); + } +} + +static void enteringPodNotify (perlSubparser *perl) +{ + struct mooseSubparser *moose = (struct mooseSubparser *)perl; + moose->inPod = true; +} + +static void leavingPodNotify (perlSubparser *perl) +{ + struct mooseSubparser *moose = (struct mooseSubparser *)perl; + moose->inPod = false; +} + +static bool leaveMoose (const char *line CTAGS_ATTR_UNUSED, + const regexMatch *matches CTAGS_ATTR_UNUSED, + unsigned int count CTAGS_ATTR_UNUSED, + void *data) +{ + struct mooseSubparser *moose = data; + + if (moose->inPod) + return true; + + moose->notContinuousExtendsLines = true; + + tagEntryInfo *e = getEntryInCorkQueue (moose->classCork); + Assert (e); + e->extensionFields.endLine = getInputLineNumber (); + + moose->classCork = CORK_NIL; + moose->notInMoose = true; + moose->packageCork = CORK_NIL; + return true; +} + +static bool enterMoose (const char *line CTAGS_ATTR_UNUSED, + const regexMatch *matches CTAGS_ATTR_UNUSED, + unsigned int count CTAGS_ATTR_UNUSED, + void *data) +{ + struct mooseSubparser *moose = data; + + if (moose->inPod) + return true; + + moose->notContinuousExtendsLines = true; + + if (moose->packageCork == CORK_NIL) + return true; + + moose->notInMoose = false; + tagEntryInfo *perl_e = getEntryInCorkQueue (moose->packageCork); + Assert (perl_e); + + tagEntryInfo moose_e; + initTagEntry (&moose_e, perl_e->name, K_CLASS); + moose_e.lineNumber = perl_e->lineNumber; + moose_e.filePosition = perl_e->filePosition; + moose->classCork = makeTagEntry (&moose_e); + + return true; +} + +static void parseExtendsClass (const char *input, + size_t input_len, + vString *inherits, + bool *notContinuousLine) +{ + int i = 0; + do + { + if (input [i] == ',') + i++; + + for (; (i < input_len) && input[i] != '\n' && input[i] != '\0'; i++) + { + if (input[i] == '\'' || input[i] == ' ' || input[i] == '\t') + continue; + else if (input[i] == ',') + { + vStringPut (inherits, ','); + *notContinuousLine = false; + break; + } + else if (input[i] == ';') + { + *notContinuousLine = true; + break; + } + else + vStringPut (inherits, input[i]); + } + } + while (input[i] == ','); +} + +static bool findExtendsClass (const char *line, + const regexMatch *matches, + unsigned int count, + void *data) +{ + struct mooseSubparser *moose = data; + + if (moose->inPod) + return true; + + moose->notContinuousExtendsLines = true; + + if (moose->classCork == CORK_NIL) + return true; + + const char *input = line + matches[1].start; + vString *str = vStringNew (); + + parseExtendsClass (input, matches[1].length, str, + &moose->notContinuousExtendsLines); + + if (vStringLength(str) == 0) + { + vStringDelete (str); + return true; + } + + tagEntryInfo *e = getEntryInCorkQueue (moose->classCork); + Assert (e); + if (e->extensionFields.inheritance == NULL) + e->extensionFields.inheritance = vStringDeleteUnwrap (str); + + return true; +} + +static bool findExtendsClassContinuation (const char *line, + const regexMatch *matches, + unsigned int count, + void *data) +{ + struct mooseSubparser *moose = data; + moose->notContinuousExtendsLines = true; + + tagEntryInfo *e = getEntryInCorkQueue (moose->classCork); + Assert (e); + + const char *input = line + matches[1].start; + vString *str; + + if (e->extensionFields.inheritance) + str = vStringNewInit (e->extensionFields.inheritance); + else + str = vStringNew (); + + parseExtendsClass (input, matches[1].length, str, + &moose->notContinuousExtendsLines); + + if (e->extensionFields.inheritance != NULL) + eFree ((char *)e->extensionFields.inheritance); + + e->extensionFields.inheritance = vStringDeleteUnwrap (str); + + return true; +} + +static const char *parseAttributeOrWrapper (const char *str, int parentCorkIndex, int extraTerminator, + int kind, enum Wrapping wrapping) +{ + int i; + + for (i = 0; + str[i] + && str[i] != extraTerminator + && (isalnum ((unsigned char)str[i]) || str[i] == '_'); + i++) + ; /* Just advancing `i' */ + + if (i == 0) + return NULL; + + tagEntryInfo e; + char *buf = eStrndup (str, i); + + initTagEntry (&e, buf, kind); + if (parentCorkIndex != CORK_NIL) + e.extensionFields.scopeIndex = parentCorkIndex; + + int corkIndex = makeTagEntry (&e); + if (kind == K_WRAPPER) + attachParserFieldToCorkEntry (corkIndex, MooseFields[F_WRAPPING].ftype, + WrappingStrings [wrapping]); + eFree (buf); + + return str[i] == '\0'? NULL: str + i; +} + +static void solveKindAndWrappng (const char *str, int *kind, enum Wrapping *wrapping) +{ + *kind = K_WRAPPER; + *wrapping = W_UNKNOWN; + switch (str[0]) + { + case 'h': /* has */ + *kind = K_ATTR; + break; + case 'a': /* around or after */ + if (str[1] == 'r') + *wrapping = W_AROUND; + else if (str[1] == 'f') + *wrapping = W_AFTER; + break; + case 'b': /* before */ + *wrapping = W_BEFORE; + break; + case 'o': /* override */ + *kind = K_METHOD; + } +} + +static bool findAttributeOrWrapperOne (const char *line, + const regexMatch *matches, + unsigned int count, + void *data) +{ + struct mooseSubparser *moose = data; + int kind; + enum Wrapping wrapping; + + if (moose->inPod) + return true; + + moose->notContinuousExtendsLines = true; + + if (count < 3) + return true; + + solveKindAndWrappng (line + matches[1].start, &kind, &wrapping); + parseAttributeOrWrapper (line + matches[2].start, moose->classCork, -1, + kind, wrapping); + return true; +} + +static bool findAttributeOrWrapperMulit (const char *line, + const regexMatch *matches, + unsigned int count, + void *data) +{ + + struct mooseSubparser *moose = data; + int kind; + enum Wrapping wrapping; + int terminator; + + if (moose->inPod) + return true; + + moose->notContinuousExtendsLines = true; + + if (count < 4) + return true; + + solveKindAndWrappng (line + matches[1].start, &kind, &wrapping); + terminator = line[matches[2].start]; + + + const char *str = line + matches[3].start; + while ((str = parseAttributeOrWrapper (str, moose->classCork, terminator, + kind, wrapping))) + { + int i; + for (i = 0; (str[i] == ' ') || (str[i] == '\t'); i++) + ; + if (str[i] == '\0' || str[i] == terminator) + break; + str = str + i; + } + + return true; +} + +static void findMooseTags (void) +{ + scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS); +} + +static void initializeMooseParser (langType language) +{ + addLanguageCallbackRegex (language, "^[ \t]*use +Moose *;", + "{exclusive}", + enterMoose, NULL, + &mooseSubparser); + addLanguageCallbackRegex (language, "^[ \t]*no +Moose *;", + "{exclusive}", + leaveMoose, &mooseSubparser.notInMoose, + &mooseSubparser); + addLanguageCallbackRegex (language, "^[ \t]*extends *(.+)", + "{exclusive}", + findExtendsClass, &mooseSubparser.notInMoose, + &mooseSubparser); + addLanguageCallbackRegex (language, "^[ \t]*(has|after|before|around|override) +" + /* foo => () + * 'foo' => () + * "foo" => () + * ( foo => ()) + * ( "foo" => ()) + * ( 'foo' => ()) */ + "\\(?[ \t]*[\"']?" + "" + "([a-zA-Z_][a-zA-Z0-9_]*([ \t][a-zA-Z_][a-zA-Z0-9_]*)*).*=>", + "{exclusive}", + findAttributeOrWrapperOne, &mooseSubparser.notInMoose, + &mooseSubparser); + addLanguageCallbackRegex (language, "^[ \t]*(has|after|before|around) +\\(?\\[qw([^ \t])[ \t]*" + /* [qw/foo bar/] => () + * ([qw/foo bar/] => ()) */ + "([a-zA-Z_][a-zA-Z0-9_]*([ \t][a-zA-Z_][a-zA-Z0-9_]*)*).*=>", + "{exclusive}", + findAttributeOrWrapperMulit, &mooseSubparser.notInMoose, + &mooseSubparser); + addLanguageCallbackRegex (language, "^[ \t]*(.+)", + "{exclusive}", + findExtendsClassContinuation, &mooseSubparser.notContinuousExtendsLines, + &mooseSubparser); +} + +extern parserDefinition* MooseParser (void) +{ + parserDefinition* const def = parserNew("Moose"); + + static parserDependency dependencies [] = { + [0] = { DEPTYPE_SUBPARSER, "Perl", &mooseSubparser }, + }; + + def->dependencies = dependencies; + def->dependencyCount = ARRAY_SIZE (dependencies); + + def->kindTable = MooseKinds; + def->kindCount = ARRAY_SIZE(MooseKinds); + + def->fieldTable = MooseFields; + def->fieldCount = ARRAY_SIZE (MooseFields); + + def->initialize = initializeMooseParser; + def->parser = findMooseTags; + def->useCork = true; + + return def; +} diff --git a/source.mak b/source.mak index 0601eb3734..febf10f1fe 100644 --- a/source.mak +++ b/source.mak @@ -228,6 +228,7 @@ PARSER_SRCS = \ parsers/m4.c \ parsers/make.c \ parsers/matlab.c \ + parsers/moose.c \ parsers/myrddin.c \ parsers/objc.c \ parsers/ocaml.c \ diff --git a/win32/ctags_vs2013.vcxproj b/win32/ctags_vs2013.vcxproj index d4e8e00e9d..cd8ffd88c5 100644 --- a/win32/ctags_vs2013.vcxproj +++ b/win32/ctags_vs2013.vcxproj @@ -212,6 +212,7 @@ + diff --git a/win32/ctags_vs2013.vcxproj.filters b/win32/ctags_vs2013.vcxproj.filters index f3f8e690e6..d0ff808468 100644 --- a/win32/ctags_vs2013.vcxproj.filters +++ b/win32/ctags_vs2013.vcxproj.filters @@ -390,6 +390,9 @@ Source Files\Parsers + + Source Files\Parsers + Source Files\Parsers