Line data Source code
1 : /* do not edit automatically generated by mc from M2StackSpell. */
2 : /* M2StackSpell.mod maintain a stack of scopes used in spell checks.
3 :
4 : Copyright (C) 2025-2026 Free Software Foundation, Inc.
5 : Contributed by Gaius Mulley <gaiusmod2@gmail.com>.
6 :
7 : This file is part of GNU Modula-2.
8 :
9 : GNU Modula-2 is free software; you can redistribute it and/or modify
10 : it under the terms of the GNU General Public License as published by
11 : the Free Software Foundation; either version 3, or (at your option)
12 : any later version.
13 :
14 : GNU Modula-2 is distributed in the hope that it will be useful, but
15 : WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : General Public License for more details.
18 :
19 : You should have received a copy of the GNU General Public License
20 : along with GNU Modula-2; see the file COPYING3. If not see
21 : <http://www.gnu.org/licenses/>. */
22 :
23 : #include "config.h"
24 : #include "system.h"
25 : #include "gcc-consolidation.h"
26 :
27 : #include <stdbool.h>
28 : # if !defined (PROC_D)
29 : # define PROC_D
30 : typedef void (*PROC_t) (void);
31 : typedef struct { PROC_t proc; } PROC;
32 : # endif
33 :
34 : #if defined(__cplusplus)
35 : # undef NULL
36 : # define NULL 0
37 : #endif
38 : #define _M2StackSpell_C
39 :
40 : #include "GM2StackSpell.h"
41 : # include "GSymbolTable.h"
42 : # include "GSymbolKey.h"
43 : # include "GDynamicStrings.h"
44 : # include "GFormatStrings.h"
45 : # include "GNameKey.h"
46 : # include "GM2MetaError.h"
47 : # include "GM2StackWord.h"
48 : # include "GCDataTypes.h"
49 : # include "GM2Batch.h"
50 : # include "Gm2spellcheck.h"
51 :
52 : static M2StackWord_StackOfWord DefaultStack;
53 : static unsigned int PushCount;
54 : static m2spellcheck_Candidates PushCandidate;
55 :
56 : /*
57 : GetSpellHint - return a string describing a spelling hint.
58 : */
59 :
60 : extern "C" DynamicStrings_String M2StackSpell_GetSpellHint (unsigned int unknown);
61 :
62 : /*
63 : Push - push a scope onto the spelling stack.
64 : sym might be a ModSym, DefImpSym or a varsym
65 : of a record type denoting a with statement.
66 : */
67 :
68 : extern "C" void M2StackSpell_Push (unsigned int sym);
69 :
70 : /*
71 : Pop - remove the top scope from the spelling stack.
72 : */
73 :
74 : extern "C" void M2StackSpell_Pop (void);
75 :
76 : /*
77 : GetRecordField - return the record field containing fieldName.
78 : An error is generated if the fieldName is not
79 : found in record.
80 : */
81 :
82 : extern "C" unsigned int M2StackSpell_GetRecordField (unsigned int tokno, unsigned int record, NameKey_Name fieldName);
83 :
84 : /*
85 : GetDefModuleSpellHint - return a string describing a spelling
86 : hint for the definition module name
87 : similiar to defimp. The premise is that
88 : defimp has been misspelt. NIL is returned
89 : if no hint can be given.
90 : */
91 :
92 : extern "C" DynamicStrings_String M2StackSpell_GetDefModuleSpellHint (unsigned int defimp);
93 :
94 : /*
95 : CandidatePushName - push a symbol name to the candidate list.
96 : */
97 :
98 : static void CandidatePushName (m2spellcheck_Candidates cand, unsigned int sym);
99 :
100 : /*
101 : PushName - push a name to the candidate vec.
102 : */
103 :
104 : static void PushName (unsigned int sym);
105 :
106 : /*
107 : ForeachRecordFieldDo -
108 : */
109 :
110 : static void ForeachRecordFieldDo (unsigned int record, SymbolKey_PerformOperation op);
111 :
112 : /*
113 : PushCandidates -
114 : */
115 :
116 : static unsigned int PushCandidates (m2spellcheck_Candidates cand, unsigned int sym);
117 :
118 : /*
119 : BuildHintStr - create the did you mean hint and return it
120 : if HintStr is NIL. Otherwise append a hint
121 : to HintStr. If content is NIL then return NIL.
122 : */
123 :
124 : static DynamicStrings_String BuildHintStr (DynamicStrings_String HintStr, const char * content);
125 :
126 : /*
127 : CheckForHintStr - lookup a spell hint matching misspelt. If one exists
128 : then append it to HintStr. Return HintStr.
129 : */
130 :
131 : static DynamicStrings_String CheckForHintStr (unsigned int sym, DynamicStrings_String HintStr, DynamicStrings_String misspelt);
132 :
133 : /*
134 : AddPunctuation - adds punct to the end of str providing that str is non NIL.
135 : */
136 :
137 : static DynamicStrings_String AddPunctuation (DynamicStrings_String str, const char *punct_, unsigned int _punct_high);
138 :
139 : /*
140 : GetExportedSpellHint - return a string describing a spelling hint
141 : using the module exported identifiers.
142 : */
143 :
144 : static DynamicStrings_String GetExportedSpellHint (unsigned int unknown, unsigned int module);
145 :
146 : /*
147 : GetScopeSpellHint - return a string describing a spelling hint
148 : using the visible scopes.
149 : */
150 :
151 : static DynamicStrings_String GetScopeSpellHint (unsigned int unknown);
152 :
153 : /*
154 : Init -
155 : */
156 :
157 : static void Init (void);
158 :
159 :
160 : /*
161 : CandidatePushName - push a symbol name to the candidate list.
162 : */
163 :
164 83 : static void CandidatePushName (m2spellcheck_Candidates cand, unsigned int sym)
165 : {
166 83 : DynamicStrings_String str;
167 :
168 83 : str = DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (SymbolTable_GetSymName (sym)));
169 83 : m2spellcheck_Push (cand, const_cast <const char * > (static_cast <char * > (DynamicStrings_string (str))));
170 83 : PushCount += 1;
171 83 : }
172 :
173 :
174 : /*
175 : PushName - push a name to the candidate vec.
176 : */
177 :
178 3044 : static void PushName (unsigned int sym)
179 : {
180 3044 : DynamicStrings_String str;
181 :
182 3044 : str = DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (SymbolTable_GetSymName (sym)));
183 3044 : m2spellcheck_Push (PushCandidate, const_cast <const char * > (static_cast <char * > (DynamicStrings_string (str))));
184 : /* str := KillString (str) */
185 3044 : PushCount += 1;
186 3044 : }
187 :
188 :
189 : /*
190 : ForeachRecordFieldDo -
191 : */
192 :
193 12 : static void ForeachRecordFieldDo (unsigned int record, SymbolKey_PerformOperation op)
194 : {
195 12 : unsigned int i;
196 12 : unsigned int field;
197 :
198 12 : i = 1;
199 36 : do {
200 36 : field = SymbolTable_GetNth (record, i);
201 36 : if (field != SymbolTable_NulSym)
202 : {
203 24 : (*op.proc) (field);
204 : }
205 36 : i += 1;
206 36 : } while (! (field == SymbolTable_NulSym));
207 12 : }
208 :
209 :
210 : /*
211 : PushCandidates -
212 : */
213 :
214 210 : static unsigned int PushCandidates (m2spellcheck_Candidates cand, unsigned int sym)
215 : {
216 210 : PushCount = 0;
217 210 : PushCandidate = cand;
218 210 : if ((SymbolTable_IsModule (sym)) || (SymbolTable_IsDefImp (sym)))
219 : {
220 198 : SymbolTable_ForeachProcedureDo (sym, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
221 198 : SymbolTable_ForeachLocalSymDo (sym, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
222 : }
223 12 : else if (SymbolTable_IsEnumeration (sym))
224 : {
225 : /* avoid dangling else. */
226 0 : SymbolTable_ForeachFieldEnumerationDo (sym, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
227 : }
228 12 : else if (SymbolTable_IsRecord (sym))
229 : {
230 : /* avoid dangling else. */
231 12 : ForeachRecordFieldDo (sym, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
232 : }
233 210 : return PushCount;
234 : /* static analysis guarentees a RETURN statement will be used before here. */
235 : __builtin_unreachable ();
236 : }
237 :
238 :
239 : /*
240 : BuildHintStr - create the did you mean hint and return it
241 : if HintStr is NIL. Otherwise append a hint
242 : to HintStr. If content is NIL then return NIL.
243 : */
244 :
245 224 : static DynamicStrings_String BuildHintStr (DynamicStrings_String HintStr, const char * content)
246 : {
247 224 : DynamicStrings_String str;
248 :
249 224 : if (content != NULL)
250 : {
251 64 : str = DynamicStrings_InitStringCharStar (static_cast <void *> (const_cast <char * > (content)));
252 64 : if (HintStr == NULL)
253 : {
254 64 : return FormatStrings_Sprintf1 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) ", did you mean %s", 17)), (const unsigned char *) &str, (sizeof (str)-1));
255 : }
256 : else
257 : {
258 0 : return FormatStrings_Sprintf2 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) "%s or %s", 8)), (const unsigned char *) &HintStr, (sizeof (HintStr)-1), (const unsigned char *) &str, (sizeof (str)-1));
259 : }
260 : }
261 : return static_cast<DynamicStrings_String> (NULL);
262 : /* static analysis guarentees a RETURN statement will be used before here. */
263 : __builtin_unreachable ();
264 : }
265 :
266 :
267 : /*
268 : CheckForHintStr - lookup a spell hint matching misspelt. If one exists
269 : then append it to HintStr. Return HintStr.
270 : */
271 :
272 204 : static DynamicStrings_String CheckForHintStr (unsigned int sym, DynamicStrings_String HintStr, DynamicStrings_String misspelt)
273 : {
274 204 : m2spellcheck_Candidates cand;
275 204 : const char * content;
276 :
277 204 : if (((((SymbolTable_IsModule (sym)) || (SymbolTable_IsDefImp (sym))) || (SymbolTable_IsProcedure (sym))) || (SymbolTable_IsRecord (sym))) || (SymbolTable_IsEnumeration (sym)))
278 : {
279 204 : cand = m2spellcheck_InitCandidates ();
280 204 : if ((PushCandidates (cand, sym)) > 1)
281 : {
282 204 : content = m2spellcheck_FindClosestCharStar (cand, const_cast <const char * > (static_cast <char * > (DynamicStrings_string (misspelt))));
283 : }
284 : else
285 : {
286 : content = static_cast<const char *> (NULL);
287 : }
288 204 : m2spellcheck_KillCandidates (&cand);
289 204 : HintStr = BuildHintStr (HintStr, content);
290 : }
291 204 : return HintStr;
292 : /* static analysis guarentees a RETURN statement will be used before here. */
293 : __builtin_unreachable ();
294 : }
295 :
296 :
297 : /*
298 : AddPunctuation - adds punct to the end of str providing that str is non NIL.
299 : */
300 :
301 266 : static DynamicStrings_String AddPunctuation (DynamicStrings_String str, const char *punct_, unsigned int _punct_high)
302 : {
303 266 : char punct[_punct_high+1];
304 :
305 : /* make a local copy of each unbounded array. */
306 266 : memcpy (punct, punct_, _punct_high+1);
307 :
308 266 : if (str == NULL)
309 : {
310 : return static_cast<DynamicStrings_String> (NULL);
311 : }
312 : else
313 : {
314 64 : return DynamicStrings_ConCat (str, DynamicStrings_Mark (DynamicStrings_InitString ((const char *) punct, _punct_high)));
315 : }
316 : /* static analysis guarentees a RETURN statement will be used before here. */
317 : __builtin_unreachable ();
318 266 : }
319 :
320 :
321 : /*
322 : GetExportedSpellHint - return a string describing a spelling hint
323 : using the module exported identifiers.
324 : */
325 :
326 3 : static DynamicStrings_String GetExportedSpellHint (unsigned int unknown, unsigned int module)
327 : {
328 3 : const char * content;
329 3 : DynamicStrings_String misspell;
330 3 : DynamicStrings_String HintStr;
331 :
332 3 : misspell = DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (SymbolTable_GetSymName (unknown)));
333 3 : HintStr = static_cast<DynamicStrings_String> (NULL);
334 3 : PushCount = 0;
335 3 : PushCandidate = m2spellcheck_InitCandidates ();
336 3 : SymbolTable_ForeachExportedDo (module, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
337 3 : SymbolTable_ForeachLocalSymDo (module, (SymbolKey_PerformOperation) {(SymbolKey_PerformOperation_t) PushName});
338 3 : if (PushCount > 0)
339 : {
340 3 : content = m2spellcheck_FindClosestCharStar (PushCandidate, const_cast <const char * > (static_cast <char * > (DynamicStrings_string (misspell))));
341 3 : HintStr = BuildHintStr (HintStr, content);
342 : }
343 3 : m2spellcheck_KillCandidates (&PushCandidate);
344 3 : return AddPunctuation (HintStr, (const char *) "?", 1);
345 : /* static analysis guarentees a RETURN statement will be used before here. */
346 : __builtin_unreachable ();
347 : }
348 :
349 :
350 : /*
351 : GetScopeSpellHint - return a string describing a spelling hint
352 : using the visible scopes.
353 : */
354 :
355 240 : static DynamicStrings_String GetScopeSpellHint (unsigned int unknown)
356 : {
357 240 : unsigned int i;
358 240 : unsigned int n;
359 240 : unsigned int sym;
360 240 : DynamicStrings_String misspell;
361 240 : DynamicStrings_String HintStr;
362 :
363 240 : misspell = DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (SymbolTable_GetSymName (unknown)));
364 240 : HintStr = static_cast<DynamicStrings_String> (NULL);
365 240 : n = M2StackWord_NoOfItemsInStackWord (DefaultStack);
366 240 : i = 1;
367 486 : while ((i <= n) && (HintStr == NULL))
368 : {
369 204 : sym = static_cast<unsigned int> (M2StackWord_PeepWord (DefaultStack, i));
370 204 : HintStr = CheckForHintStr (sym, HintStr, misspell);
371 204 : if ((SymbolTable_IsModule (sym)) || (SymbolTable_IsDefImp (sym)))
372 : {
373 : /* Cannot see beyond a module scope. */
374 198 : return AddPunctuation (HintStr, (const char *) "?", 1);
375 : }
376 6 : i += 1;
377 : }
378 42 : return AddPunctuation (HintStr, (const char *) "?", 1);
379 : /* static analysis guarentees a RETURN statement will be used before here. */
380 : __builtin_unreachable ();
381 : }
382 :
383 :
384 : /*
385 : Init -
386 : */
387 :
388 14952 : static void Init (void)
389 : {
390 0 : DefaultStack = M2StackWord_InitStackWord ();
391 0 : }
392 :
393 :
394 : /*
395 : GetSpellHint - return a string describing a spelling hint.
396 : */
397 :
398 243 : extern "C" DynamicStrings_String M2StackSpell_GetSpellHint (unsigned int unknown)
399 : {
400 243 : if (((SymbolTable_IsUnknown (unknown)) && (SymbolTable_GetUnknownOnImport (unknown))) && ((SymbolTable_GetUnknownDeclScope (unknown)) != (SymbolTable_GetScope (unknown))))
401 : {
402 : /* It was created during an import statement. */
403 3 : return GetExportedSpellHint (unknown, SymbolTable_GetUnknownDeclScope (unknown));
404 : }
405 240 : return GetScopeSpellHint (unknown);
406 : /* static analysis guarentees a RETURN statement will be used before here. */
407 : __builtin_unreachable ();
408 : }
409 :
410 :
411 : /*
412 : Push - push a scope onto the spelling stack.
413 : sym might be a ModSym, DefImpSym or a varsym
414 : of a record type denoting a with statement.
415 : */
416 :
417 5385453 : extern "C" void M2StackSpell_Push (unsigned int sym)
418 : {
419 5385453 : M2StackWord_PushWord (DefaultStack, sym);
420 5385453 : }
421 :
422 :
423 : /*
424 : Pop - remove the top scope from the spelling stack.
425 : */
426 :
427 5385197 : extern "C" void M2StackSpell_Pop (void)
428 : {
429 5385197 : if ((M2StackWord_PopWord (DefaultStack)) == 0)
430 : {} /* empty. */
431 5385197 : }
432 :
433 :
434 : /*
435 : GetRecordField - return the record field containing fieldName.
436 : An error is generated if the fieldName is not
437 : found in record.
438 : */
439 :
440 26388 : extern "C" unsigned int M2StackSpell_GetRecordField (unsigned int tokno, unsigned int record, NameKey_Name fieldName)
441 : {
442 26388 : DynamicStrings_String str;
443 26388 : unsigned int sym;
444 26388 : NameKey_Name recordName;
445 26388 : const char * content;
446 26388 : m2spellcheck_Candidates cand;
447 26388 : DynamicStrings_String fieldStr;
448 26388 : DynamicStrings_String recordStr;
449 26388 : DynamicStrings_String contentStr;
450 :
451 26388 : sym = SymbolTable_GetLocalSym (record, fieldName);
452 26388 : if (sym == SymbolTable_NulSym)
453 : {
454 6 : recordName = SymbolTable_GetSymName (record);
455 6 : content = static_cast<const char *> (NULL);
456 6 : cand = m2spellcheck_InitCandidates ();
457 6 : if ((PushCandidates (cand, record)) > 0)
458 : {
459 6 : content = m2spellcheck_FindClosestCharStar (cand, const_cast <const char * > (static_cast <char * > (NameKey_KeyToCharStar (fieldName))));
460 : }
461 6 : fieldStr = DynamicStrings_Mark (DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (fieldName)));
462 6 : recordStr = DynamicStrings_Mark (DynamicStrings_InitStringCharStar (NameKey_KeyToCharStar (recordName)));
463 6 : if (content == NULL)
464 : {
465 0 : str = FormatStrings_Sprintf2 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) "field %s does not exist within record %s", 40)), (const unsigned char *) &fieldStr, (sizeof (fieldStr)-1), (const unsigned char *) &recordStr, (sizeof (recordStr)-1));
466 : }
467 : else
468 : {
469 6 : contentStr = DynamicStrings_Mark (DynamicStrings_InitStringCharStar (static_cast <void *> (const_cast <char * > (content))));
470 6 : str = FormatStrings_Sprintf3 (DynamicStrings_Mark (DynamicStrings_InitString ((const char *) "field %s does not exist within record %s, did you mean %s?", 58)), (const unsigned char *) &fieldStr, (sizeof (fieldStr)-1), (const unsigned char *) &recordStr, (sizeof (recordStr)-1), (const unsigned char *) &contentStr, (sizeof (contentStr)-1));
471 : }
472 6 : M2MetaError_MetaErrorStringT0 (tokno, str);
473 6 : m2spellcheck_KillCandidates (&cand);
474 : }
475 26388 : return sym;
476 : /* static analysis guarentees a RETURN statement will be used before here. */
477 : __builtin_unreachable ();
478 : }
479 :
480 :
481 : /*
482 : GetDefModuleSpellHint - return a string describing a spelling
483 : hint for the definition module name
484 : similiar to defimp. The premise is that
485 : defimp has been misspelt. NIL is returned
486 : if no hint can be given.
487 : */
488 :
489 23 : extern "C" DynamicStrings_String M2StackSpell_GetDefModuleSpellHint (unsigned int defimp)
490 : {
491 23 : unsigned int i;
492 23 : unsigned int sym;
493 23 : m2spellcheck_Candidates cand;
494 23 : const char * misspell;
495 23 : const char * content;
496 23 : DynamicStrings_String HintStr;
497 :
498 23 : HintStr = static_cast<DynamicStrings_String> (NULL);
499 23 : if ((SymbolTable_GetSymName (defimp)) != NameKey_NulName)
500 : {
501 17 : misspell = static_cast<const char *> (NameKey_KeyToCharStar (SymbolTable_GetSymName (defimp)));
502 17 : i = 1;
503 17 : sym = M2Batch_GetModuleNo (i);
504 17 : cand = m2spellcheck_InitCandidates ();
505 117 : while (sym != SymbolTable_NulSym)
506 : {
507 100 : if (sym != defimp)
508 : {
509 83 : CandidatePushName (cand, sym);
510 : }
511 100 : i += 1;
512 100 : sym = M2Batch_GetModuleNo (i);
513 : }
514 17 : content = m2spellcheck_FindClosestCharStar (cand, misspell);
515 17 : HintStr = BuildHintStr (HintStr, content);
516 17 : m2spellcheck_KillCandidates (&cand);
517 : }
518 23 : return AddPunctuation (HintStr, (const char *) "?", 1);
519 : /* static analysis guarentees a RETURN statement will be used before here. */
520 : __builtin_unreachable ();
521 : }
522 :
523 14952 : extern "C" void _M2_M2StackSpell_init (__attribute__((unused)) int argc, __attribute__((unused)) char *argv[], __attribute__((unused)) char *envp[])
524 : {
525 14952 : Init ();
526 14952 : }
527 :
528 0 : extern "C" void _M2_M2StackSpell_fini (__attribute__((unused)) int argc, __attribute__((unused)) char *argv[], __attribute__((unused)) char *envp[])
529 : {
530 0 : }
|