Make parser includes closer to uctags and sync parser license header
[geany-mirror.git] / ctags / parsers / erlang.c
blobbe147bad959df6b0b626d04fbe115cef8ba408ce
1 /*
2 * Copyright (c) 2003, Brent Fulgham <bfulgham@debian.org>
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
7 * This module contains functions for generating tags for Erlang language
8 * files. Some of the parsing constructs are based on the Emacs 'etags'
9 * program by Francesco Potori <pot@gnu.org>
12 * INCLUDE FILES
14 #include "general.h" /* must always come first */
16 #include <string.h>
18 #include "entry.h"
19 #include "options.h"
20 #include "read.h"
21 #include "routines.h"
22 #include "vstring.h"
25 * DATA DEFINITIONS
27 typedef enum {
28 K_MACRO, K_FUNCTION, K_MODULE, K_RECORD, K_TYPE
29 } erlangKind;
31 static kindOption ErlangKinds[] = {
32 {TRUE, 'd', "macro", "macro definitions"},
33 {TRUE, 'f', "function", "functions"},
34 {TRUE, 'm', "module", "modules"},
35 {TRUE, 'r', "record", "record definitions"},
36 {TRUE, 't', "type", "type definitions"},
40 * FUNCTION DEFINITIONS
42 /* tagEntryInfo and vString should be preinitialized/preallocated but not
43 * necessary. If successful you will find class name in vString
46 static boolean isIdentifierFirstCharacter (int c)
48 return (boolean) (isalpha (c));
51 static boolean isIdentifierCharacter (int c)
53 return (boolean) (isalnum (c) || c == '_' || c == ':');
56 static const unsigned char *skipSpace (const unsigned char *cp)
58 while (isspace ((int) *cp))
59 ++cp;
60 return cp;
63 static const unsigned char *parseIdentifier (
64 const unsigned char *cp, vString *const identifier)
66 vStringClear (identifier);
67 while (isIdentifierCharacter ((int) *cp))
69 vStringPut (identifier, (int) *cp);
70 ++cp;
72 vStringTerminate (identifier);
73 return cp;
76 static void makeMemberTag (
77 vString *const identifier, erlangKind kind, vString *const module)
79 if (ErlangKinds [kind].enabled && vStringLength (identifier) > 0)
81 tagEntryInfo tag;
82 initTagEntry (&tag, vStringValue (identifier));
83 tag.kindName = ErlangKinds[kind].name;
84 tag.kind = ErlangKinds[kind].letter;
86 if (module != NULL && vStringLength (module) > 0)
88 tag.extensionFields.scope [0] = "module";
89 tag.extensionFields.scope [1] = vStringValue (module);
91 makeTagEntry (&tag);
95 static void parseModuleTag (const unsigned char *cp, vString *const module)
97 vString *const identifier = vStringNew ();
98 parseIdentifier (cp, identifier);
99 makeSimpleTag (identifier, ErlangKinds, K_MODULE);
101 /* All further entries go in the new module */
102 vStringCopy (module, identifier);
103 vStringDelete (identifier);
106 static void parseSimpleTag (const unsigned char *cp, erlangKind kind)
108 vString *const identifier = vStringNew ();
109 parseIdentifier (cp, identifier);
110 makeSimpleTag (identifier, ErlangKinds, kind);
111 vStringDelete (identifier);
114 static void parseFunctionTag (const unsigned char *cp, vString *const module)
116 vString *const identifier = vStringNew ();
117 parseIdentifier (cp, identifier);
118 makeMemberTag (identifier, K_FUNCTION, module);
119 vStringDelete (identifier);
123 * Directives are of the form:
124 * -module(foo)
125 * -define(foo, bar)
126 * -record(graph, {vtab = notable, cyclic = true}).
127 * -type some_type() :: any().
128 * -opaque some_opaque_type() :: any().
130 static void parseDirective (const unsigned char *cp, vString *const module)
133 * A directive will be either a record definition or a directive.
134 * Record definitions are handled separately
136 vString *const directive = vStringNew ();
137 const char *const drtv = vStringValue (directive);
138 cp = parseIdentifier (cp, directive);
139 cp = skipSpace (cp);
140 if (*cp == '(')
141 ++cp;
143 if (strcmp (drtv, "record") == 0)
144 parseSimpleTag (cp, K_RECORD);
145 else if (strcmp (drtv, "define") == 0)
146 parseSimpleTag (cp, K_MACRO);
147 else if (strcmp (drtv, "type") == 0)
148 parseSimpleTag (cp, K_TYPE);
149 else if (strcmp (drtv, "opaque") == 0)
150 parseSimpleTag (cp, K_TYPE);
151 else if (strcmp (drtv, "module") == 0)
152 parseModuleTag (cp, module);
153 /* Otherwise, it was an import, export, etc. */
155 vStringDelete (directive);
158 static void findErlangTags (void)
160 vString *const module = vStringNew ();
161 const unsigned char *line;
163 while ((line = fileReadLine ()) != NULL)
165 const unsigned char *cp = line;
167 if (*cp == '%') /* skip initial comment */
168 continue;
169 if (*cp == '"') /* strings sometimes start in column one */
170 continue;
172 if ( *cp == '-')
174 ++cp; /* Move off of the '-' */
175 parseDirective(cp, module);
177 else if (isIdentifierFirstCharacter ((int) *cp))
178 parseFunctionTag (cp, module);
180 vStringDelete (module);
183 extern parserDefinition *ErlangParser (void)
185 static const char *const extensions[] = { "erl", "ERL", "hrl", "HRL", NULL };
186 parserDefinition *def = parserNew ("Erlang");
187 def->kinds = ErlangKinds;
188 def->kindCount = KIND_COUNT (ErlangKinds);
189 def->extensions = extensions;
190 def->parser = findErlangTags;
191 return def;
194 /* vi:set tabstop=4 shiftwidth=4: */