1 /*
2 * Copyright (c) 2022 Nik Silver
3 * based on the Kotlin code by Jan Dolinár
4 *
5 * This source code is released for free distribution under the terms of the
6 * GNU General Public License version 2 or (at your option) any later version.
7 *
8 * This module contains functions to generate tags for Elm.
9 * See https://elm-lang.org/docs/syntax for language reference.
10 */
11
12 /*
13 * INCLUDE FILES
14 */
15 #include "parse.h"
16 #include "trace.h"
17
18 /*
19 * FUNCTION DEFINITIONS
20 */
21
22 /* kind - The kind of this tag.
23 * role - The role this tag plays. But def(ined) is expressed by setting
24 * this to ROLE_DEFINITION_INDEX.
25 * pushScope - If true, also update the scope to be this tag.
26 */
makeElmTag(struct parserCtx * auxil,const char * name,long offset,int kind,int role)27 static int makeElmTag (struct parserCtx *auxil, const char *name, long offset, int kind, int role)
28 {
29 tagEntryInfo e;
30
31 if (role == ROLE_DEFINITION_INDEX)
32 {
33 initTagEntry (&e, name, kind);
34 }
35 else
36 {
37 initRefTagEntry (&e, name, kind, role);
38 }
39
40 e.lineNumber = getInputLineNumberForFileOffset (offset);
41 e.filePosition = getInputFilePositionForLine (e.lineNumber);
42 e.extensionFields.scopeIndex = BASE_SCOPE (auxil);
43 int scope_index = makeTagEntry (&e);
44 return scope_index;
45 }
46
47 /* Like makeElmTag, but have this tag be the latest scope.
48 */
makeElmTagSettingScope(struct parserCtx * auxil,const char * name,long offset,int kind,int role)49 static int makeElmTagSettingScope (struct parserCtx *auxil, const char *name, long offset, int kind, int role)
50 {
51 // Here is a useless line just to use a function and so
52 // get rid of a compiler warning.
53 if (1 < 0)
54 {
55 PEEK_KIND (auxil);
56 }
57 // The real function starts here...
58
59 int scope_index = makeElmTag (auxil, name, offset, kind, role);
60 SET_SCOPE (auxil, scope_index);
61 return scope_index;
62 }
63
addElmSignature(int scope_index,const char * sig)64 static void addElmSignature(int scope_index, const char *sig)
65 {
66 tagEntryInfo *e = getEntryInCorkQueue (scope_index);
67
68 if (e)
69 {
70 vString *vsig = collapseWhitespace (sig);
71
72 e->extensionFields.signature = vStringDeleteUnwrap (vsig);
73 }
74 }
75
addElmTypeRef(int scope_index,const char * sig)76 static void addElmTypeRef(int scope_index, const char *sig)
77 {
78 tagEntryInfo *e = getEntryInCorkQueue (scope_index);
79
80 if (e)
81 {
82 vString *vsig = collapseWhitespace (sig);
83
84 e->extensionFields.typeRef [0] = eStrdup ("typename");
85 e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vsig);
86 }
87 }
88
89 /* There are several steps to making the type of constructors within
90 * a custom type:
91 * 1. Initialise the fields when we encounter the custom type declaration.
92 * 2. Initialise the subtype field (do this for each new constructor).
93 * 3. Add each subtype as we find it.
94 * 4. Make the typeref field.
95 * 5. Tidy up
96 */
initElmConstructorFields(struct parserCtx * auxil,const char * name)97 static void initElmConstructorFields (struct parserCtx *auxil, const char *name)
98 {
99 auxil->customType = vStringNewInit (name);
100 auxil->consSubtype = vStringNew ();
101 }
102
initElmConstructorSubtypeFields(struct parserCtx * auxil)103 static void initElmConstructorSubtypeFields (struct parserCtx *auxil)
104 {
105 vStringClear (auxil->consSubtype);
106 }
107
addElmConstructorSubtype(struct parserCtx * auxil,const char * name)108 static void addElmConstructorSubtype (struct parserCtx *auxil, const char *name)
109 {
110 vStringCatS (auxil->consSubtype, name);
111 vStringCatS (auxil->consSubtype, " -> ");
112 }
113
addElmConstructorTypeRef(struct parserCtx * auxil,int tag_index)114 static void addElmConstructorTypeRef (struct parserCtx *auxil, int tag_index)
115 {
116 vStringCat (auxil->consSubtype, auxil->customType);
117 addElmTypeRef (tag_index, vStringValue (auxil->consSubtype));
118 }
119
tidyElmConstructorFields(struct parserCtx * auxil)120 static void tidyElmConstructorFields (struct parserCtx *auxil)
121 {
122 vStringDelete (auxil->consSubtype);
123 vStringDelete (auxil->customType);
124 }
125
126 /* For a signature such as "a1 b2 c3" we want to transform it
127 * to "a1 b2 c3" for the signature field.
128 */
collapseWhitespace(const char * sig)129 static vString *collapseWhitespace (const char *sig)
130 {
131 vString *vstr = vStringNew ();
132
133 const char *c = sig;
134 char c2;
135 int found_ws = 0;
136
137 for ( ; *c != '\0'; c++)
138 {
139 // The character, in case we need to change it
140 c2 = *c;
141
142 if (isspace ((unsigned char) c2))
143 {
144 // It's whitespace. Make it plain space
145 c2 = ' ';
146 if (found_ws)
147 {
148 // We found whitespace just before, so ignore this
149 continue;
150 }
151
152 found_ws = 1;
153 }
154 else
155 {
156 found_ws = 0;
157 }
158 vStringPut (vstr, c2);
159 }
160
161 return vstr;
162 }
163
ctxInit(struct parserCtx * auxil)164 static void ctxInit (struct parserCtx *auxil)
165 {
166 BASE_INIT (auxil, K_MODULE);
167 }
168
ctxFini(struct parserCtx * auxil)169 static void ctxFini (struct parserCtx *auxil)
170 {
171 BASE_FINI (auxil);
172 }
173
findElmTags(void)174 static void findElmTags (void)
175 {
176 struct parserCtx auxil;
177
178 ctxInit (&auxil);
179 pelm_context_t *pctx = pelm_create (&auxil);
180
181 while (pelm_parse (pctx, NULL) && (! BASE_ERROR (&auxil)))
182 ;
183
184 pelm_destroy (pctx);
185 ctxFini (&auxil);
186 }
187
ElmParser(void)188 extern parserDefinition *ElmParser (void)
189 {
190 static const char *const extensions [] = { "elm", NULL };
191 parserDefinition *def = parserNew ("Elm");
192 def->kindTable = ElmKinds;
193 def->kindCount = ARRAY_SIZE (ElmKinds);
194 def->extensions = extensions;
195 def->parser = findElmTags;
196 def->fieldTable = ElmFields;
197 def->fieldCount = ARRAY_SIZE (ElmFields);
198 def->useCork = true;
199 def->enabled = true;
200 //def->requestAutomaticFQTag = true;
201 def->defaultScopeSeparator = ".";
202 return def;
203 }
204