1 /*
2     SuperCollider real time audio synthesis system
3     Copyright (c) 2002 James McCartney. All rights reserved.
4     http://www.audiosynth.com
5     Copyright (c) 2017 Brian Heim (boost::filesystem additions)
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
20 */
21 
22 #include <stdlib.h>
23 #include <string.h>
24 #include <float.h>
25 #include <math.h>
26 #include <new>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <cerrno>
30 #include <limits>
31 #include <set>
32 
33 #ifdef _WIN32
34 #    include <direct.h>
35 #else
36 #    include <sys/param.h>
37 #endif
38 
39 #include <boost/filesystem/path.hpp>
40 #include <boost/filesystem/operations.hpp>
41 #include <boost/filesystem/string_file.hpp>
42 
43 #include "PyrParseNode.h"
44 #include "Bison/lang11d_tab.h"
45 #include "SCBase.h"
46 #include "PyrObject.h"
47 #include "PyrObjectProto.h"
48 #include "PyrLexer.h"
49 #include "PyrSched.h"
50 #include "SC_InlineUnaryOp.h"
51 #include "SC_InlineBinaryOp.h"
52 #include "GC.h"
53 #include "SimpleStack.h"
54 
55 #include "PyrSymbolTable.h"
56 #include "PyrInterpreter.h"
57 #include "PyrPrimitive.h"
58 #include "PyrObjectProto.h"
59 #include "PyrPrimitiveProto.h"
60 #include "PyrKernelProto.h"
61 #include "InitAlloc.h"
62 #include "PredefinedSymbols.h"
63 #ifdef _WIN32
64 #else
65 #    include "dirent.h"
66 #endif
67 #include <string.h>
68 
69 #include "SC_LanguageConfig.hpp"
70 
71 #include "SC_Filesystem.hpp" // getDirectory, resolveIfAlias, isStandalone
72 #include "SC_Codecvt.hpp" // path_to_utf8_str
73 #include "SC_TextUtils.hpp"
74 
75 int yyparse();
76 int processaccidental1(char* s);
77 int processaccidental2(char* s);
78 
79 
80 extern bool gFullyFunctional;
81 double compileStartTime;
82 int gNumCompiledFiles;
83 /*
84 thisProcess.interpreter.executeFile("Macintosh HD:score").size.postln;
85 */
86 
87 namespace bfs = boost::filesystem;
88 using DirName = SC_Filesystem::DirName;
89 
90 PyrSymbol* gCompilingFileSym = nullptr;
91 VMGlobals* gCompilingVMGlobals = nullptr;
92 static bfs::path gCompileDir;
93 
94 //#define DEBUGLEX 1
95 bool gDebugLexer = false;
96 
97 bool gShowWarnings = false;
98 LongStack brackets;
99 LongStack closedFuncCharNo;
100 LongStack generatorStack;
101 int lastClosedFuncCharNo = 0;
102 
103 const char* binopchars = "!@%&*-+=|<>?/";
104 char yytext[MAXYYLEN];
105 bfs::path currfilename;
106 std::string printingCurrfilename; // for error reporting
107 
108 int yylen;
109 int lexCmdLine = 0;
110 bool compilingCmdLine = false;
111 bool compilingCmdLineErrorWindow = false;
112 
113 intptr_t zzval;
114 
115 int lineno, charno, linepos;
116 int* linestarts;
117 int maxlinestarts;
118 
119 char* text;
120 int textlen;
121 int textpos;
122 int errLineOffset, errCharPosOffset;
123 int parseFailed = 0;
124 bool compiledOK = false;
125 std::set<bfs::path> compiledDirectories;
126 
127 /* so the text editor's dumb paren matching will work */
128 #define OPENPAREN '('
129 #define OPENCURLY '{'
130 #define OPENSQUAR '['
131 #define CLOSSQUAR ']'
132 #define CLOSCURLY '}'
133 #define CLOSPAREN ')'
134 
sc_strtoi(const char * str,int n,int base)135 int sc_strtoi(const char* str, int n, int base) {
136     int z = 0;
137     for (int i = 0; i < n; ++i) {
138         int c = *str++;
139         if (!c)
140             break;
141         if (c >= '0' && c <= '0' + sc_min(10, base) - 1)
142             z = z * base + c - '0';
143         else if (c >= 'a' && c <= 'a' + sc_min(36, base) - 11)
144             z = z * base + c - 'a' + 10;
145         else if (c >= 'A' && c <= 'A' + sc_min(36, base) - 11)
146             z = z * base + c - 'A' + 10;
147     }
148     return z;
149 }
150 
sc_strtof(const char * str,int n,int base)151 double sc_strtof(const char* str, int n, int base) {
152     double z = 0.;
153     int decptpos = 0;
154     for (int i = 0; i < n; ++i) {
155         int c = *str++;
156         if (!c)
157             break;
158         if (c >= '0' && c <= '0' + sc_min(10, base) - 1)
159             z = z * base + c - '0';
160         else if (c >= 'a' && c <= 'a' + sc_min(36, base) - 11)
161             z = z * base + c - 'a' + 10;
162         else if (c >= 'A' && c <= 'A' + sc_min(36, base) - 11)
163             z = z * base + c - 'A' + 10;
164         else if (c == '.')
165             decptpos = i;
166     }
167     // calculation previously included decimal point in count of columns (was n-decptpos); there are 1 less than n
168     // characters which are columns in the number contribution
169     z = z / pow((double)base, n - 1 - decptpos);
170     return z;
171 }
172 
173 bool startLexer(PyrSymbol* fileSym, const bfs::path& p, int startPos, int endPos, int lineOffset);
startLexer(PyrSymbol * fileSym,const bfs::path & p,int startPos,int endPos,int lineOffset)174 bool startLexer(PyrSymbol* fileSym, const bfs::path& p, int startPos, int endPos, int lineOffset) {
175     const char* filename = fileSym->name;
176 
177     textlen = -1;
178 
179     if (!fileSym->u.source) {
180         try {
181             bfs::ifstream file;
182             file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
183             file.open(p, std::ios_base::binary);
184             size_t sz = bfs::file_size(p);
185 
186             text = (char*)pyr_pool_compile->Alloc((sz + 1) * sizeof(char));
187             file.read(text, sz);
188             text[sz] = '\0';
189             fileSym->u.source = text;
190             rtf2txt(text);
191         } catch (const std::exception& ex) {
192             error("Could not read %s: %s.\n", SC_Codecvt::path_to_utf8_str(p).c_str(), ex.what());
193             return false;
194         }
195     } else
196         text = fileSym->u.source;
197 
198     if ((startPos >= 0) && (endPos > 0)) {
199         textlen = endPos - startPos;
200         text += startPos;
201     } else if (textlen == -1)
202         textlen = strlen(text);
203 
204     if (lineOffset > 0)
205         errLineOffset = lineOffset;
206     else
207         errLineOffset = 0;
208 
209     if (startPos > 0)
210         errCharPosOffset = startPos;
211     else
212         errCharPosOffset = 0;
213 
214     initLongStack(&brackets);
215     initLongStack(&closedFuncCharNo);
216     initLongStack(&generatorStack);
217     lastClosedFuncCharNo = 0;
218     textpos = 0;
219     linepos = 0;
220     lineno = 1;
221     charno = 0;
222 
223     yylen = 0;
224     zzval = 0;
225     parseFailed = 0;
226     lexCmdLine = 0;
227     currfilename = bfs::path(filename);
228     printingCurrfilename = "file '" + SC_Codecvt::path_to_utf8_str(currfilename) + "'";
229     maxlinestarts = 1000;
230     linestarts = (int*)pyr_pool_compile->Alloc(maxlinestarts * sizeof(int*));
231     linestarts[0] = 0;
232     linestarts[1] = 0;
233 
234     return true;
235 }
236 
startLexerCmdLine(char * textbuf,int textbuflen)237 void startLexerCmdLine(char* textbuf, int textbuflen) {
238     // pyrmalloc:
239     // lifetime: kill after compile. (this one gets killed anyway)
240     text = (char*)pyr_pool_compile->Alloc((textbuflen + 2) * sizeof(char));
241     MEMFAIL(text);
242     memcpy(text, textbuf, textbuflen);
243     text[textbuflen] = ' ';
244     text[textbuflen + 1] = 0;
245     textlen = textbuflen + 1;
246 
247     rtf2txt(text);
248 
249     initLongStack(&brackets);
250     initLongStack(&closedFuncCharNo);
251     initLongStack(&generatorStack);
252     lastClosedFuncCharNo = 0;
253     textpos = 0;
254     linepos = 0;
255     lineno = 1;
256     charno = 0;
257 
258     yylen = 0;
259     zzval = 0;
260     parseFailed = 0;
261     lexCmdLine = 1;
262     currfilename = bfs::path("interpreted text");
263     printingCurrfilename = currfilename.string();
264     maxlinestarts = 1000;
265     linestarts = (int*)pyr_pool_compile->Alloc(maxlinestarts * sizeof(int*));
266     linestarts[0] = 0;
267     linestarts[1] = 0;
268 
269     errLineOffset = 0;
270     errCharPosOffset = 0;
271 }
272 
finiLexer()273 void finiLexer() {
274     pyr_pool_compile->Free(linestarts);
275     freeLongStack(&brackets);
276     freeLongStack(&closedFuncCharNo);
277     freeLongStack(&generatorStack);
278 }
279 
initLexer()280 void initLexer() {
281     // strcpy(binopchars, "!@%&*-+=|:<>?/");
282 }
283 
input()284 int input() {
285     int c;
286     if (textpos >= textlen) {
287         c = 0;
288     } else {
289         c = text[textpos++];
290         charno++;
291     }
292     if (c == '\n' || c == '\r') {
293         lineno++;
294         linepos = textpos;
295         if (linestarts) {
296             if (lineno >= maxlinestarts) {
297                 maxlinestarts += maxlinestarts;
298                 linestarts = (int*)pyr_pool_compile->Realloc(linestarts, maxlinestarts * sizeof(int*));
299             }
300             linestarts[lineno] = linepos;
301         }
302         charno = 0;
303     }
304     if (c != 0 && yylen < MAXYYLEN - 2)
305         yytext[yylen++] = c;
306     // if (gDebugLexer) postfl("input '%c' %d\n",c,c);
307     return c;
308 }
309 
input0()310 int input0() {
311     int c;
312     if (textpos >= textlen) {
313         c = 0;
314         textpos++; // so unput will work properly
315     } else {
316         c = text[textpos++];
317         charno++;
318     }
319     if (c == '\n' || c == '\r') {
320         lineno++;
321         linepos = textpos;
322         if (linestarts) {
323             if (lineno >= maxlinestarts) {
324                 maxlinestarts += maxlinestarts;
325                 linestarts = (int*)pyr_pool_compile->Realloc(linestarts, maxlinestarts * sizeof(int*));
326             }
327             linestarts[lineno] = linepos;
328         }
329         charno = 0;
330     }
331     // if (gDebugLexer) postfl("input0 '%c' %d\n",c,c);
332     return c;
333 }
334 
unput(int c)335 void unput(int c) {
336     if (textpos > 0)
337         textpos--;
338     if (c) {
339         if (yylen)
340             --yylen;
341         if (charno)
342             --charno;
343         if (c == '\n' || c == '\r') {
344             --lineno;
345         }
346     }
347 }
348 
unput0(int c)349 void unput0(int c) {
350     if (textpos > 0)
351         textpos--;
352     if (charno)
353         --charno;
354     if (c == '\n' || c == '\r') {
355         --lineno;
356     }
357 }
358 
yylex()359 int yylex() {
360     int r, c, c2;
361     intptr_t d;
362     int radix;
363 
364     yylen = 0;
365     // finite state machine to parse input stream into tokens
366 
367     if (lexCmdLine == 1) {
368         lexCmdLine = 2;
369         r = INTERPRET;
370         goto leave;
371     }
372 start:
373     c = input();
374 
375     if (c == 0) {
376         r = 0;
377         goto leave;
378     } else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f') {
379         yylen = 0;
380         goto start;
381     } else if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
382         goto ident;
383     else if (c == '/') {
384         c = input();
385         if (c == '/')
386             goto comment1;
387         else if (c == '*')
388             goto comment2;
389         else {
390             unput(c);
391             goto binop;
392         }
393     } else if (c >= '0' && c <= '9')
394         goto digits_1;
395     else if (c == OPENPAREN || c == OPENSQUAR || c == OPENCURLY) {
396         pushls(&brackets, (intptr_t)c);
397         if (c == OPENCURLY) {
398             pushls(&closedFuncCharNo, (intptr_t)(linestarts[lineno] + charno - 1));
399         }
400         r = c;
401         goto leave;
402     } else if (c == CLOSSQUAR) {
403         if (!emptyls(&brackets)) {
404             if ((d = popls(&brackets)) != (intptr_t)OPENSQUAR) {
405                 fatal();
406                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
407                 goto error2;
408             }
409         } else {
410             fatal();
411             post("unmatched '%c'\n", c);
412             goto error2;
413         }
414         r = c;
415         goto leave;
416     } else if (c == CLOSPAREN) {
417         if (!emptyls(&brackets)) {
418             if ((d = popls(&brackets)) != OPENPAREN) {
419                 fatal();
420                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
421                 goto error2;
422             }
423         } else {
424             fatal();
425             post("unmatched '%c'\n", c);
426             goto error2;
427         }
428         r = c;
429         goto leave;
430     } else if (c == CLOSCURLY) {
431         if (!emptyls(&brackets)) {
432             if ((d = popls(&brackets)) != OPENCURLY) {
433                 fatal();
434                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
435                 goto error2;
436             }
437             lastClosedFuncCharNo = popls(&closedFuncCharNo);
438         } else {
439             fatal();
440             post("unmatched '%c'\n", c);
441             goto error2;
442         }
443         r = c;
444         goto leave;
445     } else if (c == '^') {
446         r = c;
447         goto leave;
448     } else if (c == '~') {
449         r = c;
450         goto leave;
451     } else if (c == ';') {
452         r = c;
453         goto leave;
454     } else if (c == ':') {
455         r = c;
456         goto leave;
457     } else if (c == '`') {
458         r = c;
459         goto leave;
460     } else if (c == '\\')
461         goto symbol1;
462     else if (c == '\'')
463         goto symbol3;
464     else if (c == '"')
465         goto string1;
466     else if (c == '.') {
467         if ((c = input()) == '.') {
468             if ((c = input()) == '.') {
469                 r = ELLIPSIS;
470                 goto leave;
471             } else {
472                 r = DOTDOT;
473                 unput(c);
474                 goto leave;
475             }
476         } else {
477             unput(c);
478             r = '.';
479             goto leave;
480         }
481 
482     } else if (c == '#') {
483         if ((c = input()) == OPENCURLY) {
484             pushls(&brackets, (intptr_t)OPENCURLY);
485             pushls(&closedFuncCharNo, (intptr_t)(linestarts[lineno] + charno - 2));
486             r = BEGINCLOSEDFUNC;
487         } else {
488             unput(c);
489             r = '#';
490         }
491         goto leave;
492     } else if (c == '$') {
493         c = input();
494         if (c == '\\') {
495             c = input();
496             switch (c) {
497             case 'n':
498                 c = '\n';
499                 break;
500             case 'r':
501                 c = '\r';
502                 break;
503             case 't':
504                 c = '\t';
505                 break;
506             case 'f':
507                 c = '\f';
508                 break;
509             case 'v':
510                 c = '\v';
511                 break;
512             }
513         }
514         r = processchar(c);
515         goto leave;
516     } else if (c == ',') {
517         r = c;
518         goto leave;
519     } else if (c == '=') {
520         c = input();
521         if (strchr(binopchars, c))
522             goto binop;
523         else {
524             unput(c);
525             r = '=';
526             goto leave;
527         }
528     } else if (strchr(binopchars, c))
529         goto binop;
530     else if (!(isprint(c) || isspace(c) || c == 0)) {
531         yylen = 0;
532         goto start;
533     } else
534         goto error1;
535 
536 ident:
537     c = input();
538 
539     if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9'))
540         goto ident;
541     else if (c == ':') {
542         yytext[yylen] = 0;
543         r = processkeywordbinop(yytext);
544         goto leave;
545     } else {
546         unput(c);
547         yytext[yylen] = 0;
548         r = processident(yytext);
549         goto leave;
550     }
551 
552 symbol1:
553     c = input();
554 
555     if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
556         goto symbol2;
557     else if (c >= '0' && c <= '9')
558         goto symbol4;
559     else {
560         unput(c);
561         yytext[yylen] = 0;
562         r = processsymbol(yytext);
563         goto leave;
564     }
565 
566 symbol2:
567     c = input();
568 
569     if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9'))
570         goto symbol2;
571     else {
572         unput(c);
573         yytext[yylen] = 0;
574         r = processsymbol(yytext);
575         goto leave;
576     }
577 
578 symbol4:
579     c = input();
580     if (c >= '0' && c <= '9')
581         goto symbol4;
582     else {
583         unput(c);
584         yytext[yylen] = 0;
585         r = processsymbol(yytext);
586         goto leave;
587     }
588 
589 
590 binop:
591 
592     c = input();
593 
594     if (c == 0)
595         goto binop2;
596     if (strchr(binopchars, c))
597         goto binop;
598     else {
599     binop2:
600         unput(c);
601         yytext[yylen] = 0;
602         r = processbinop(yytext);
603         goto leave;
604     }
605 
606 radix_digits_1:
607 
608     c = input();
609     if (c >= '0' && c <= '0' + sc_min(10, radix) - 1)
610         goto radix_digits_1;
611     if (c >= 'a' && c <= 'a' + sc_min(36, radix) - 11)
612         goto radix_digits_1;
613     if (c >= 'A' && c <= 'A' + sc_min(36, radix) - 11)
614         goto radix_digits_1;
615     if (c == '.') {
616         goto radix_digits_2;
617     }
618     unput(c);
619     yytext[yylen] = 0;
620     r = processintradix(yytext, yylen, radix);
621     goto leave;
622 
623 radix_digits_2:
624 
625     c = input();
626     if (c >= '0' && c <= '0' + sc_min(10, radix) - 1)
627         goto radix_digits_2;
628     if (c >= 'A' && c <= 'A' + sc_min(36, radix) - 11)
629         goto radix_digits_2;
630     // do not allow lower case after decimal point.
631     unput(c);
632     yytext[yylen] = 0;
633     r = processfloatradix(yytext, yylen, radix);
634     goto leave;
635 
636 hexdigits:
637 
638     c = input();
639     if (c >= '0' && c <= '9')
640         goto hexdigits;
641     if (c >= 'a' && c <= 'f')
642         goto hexdigits;
643     if (c >= 'A' && c <= 'F')
644         goto hexdigits;
645     unput(c);
646     yytext[yylen] = 0;
647     r = processhex(yytext);
648     goto leave;
649 
650 digits_1: /* number started with digits */
651 
652     c = input();
653 
654     if (c >= '0' && c <= '9')
655         goto digits_1;
656     else if (c == 'r') {
657         radix = sc_strtoi(yytext, yylen - 1, 10);
658         yylen = 0;
659         goto radix_digits_1;
660     } else if (c == 'e' || c == 'E')
661         goto expon_1;
662     else if (c == '.') {
663         c2 = input();
664         if (c2 >= '0' && c2 <= '9')
665             goto digits_2;
666         else {
667             unput(c2);
668             unput(c);
669             yytext[yylen] = 0;
670             r = processint(yytext);
671             goto leave;
672         }
673     } else if (c == 'b' || c == 's') {
674         d = input();
675         if (d >= '0' && d <= '9')
676             goto accidental1;
677         if (d == c)
678             goto accidental2;
679         goto accidental3;
680     accidental1:
681         d = input();
682         if (d >= '0' && d <= '9')
683             goto accidental1;
684         unput(d);
685         yytext[yylen] = 0;
686         r = processaccidental1(yytext);
687         goto leave;
688     accidental2:
689         d = input();
690         if (d == c)
691             goto accidental2;
692     accidental3:
693         unput(d);
694         yytext[yylen] = 0;
695         r = processaccidental2(yytext);
696         goto leave;
697     } else if (c == 'x') {
698         yylen = 0;
699         goto hexdigits;
700     } else {
701         unput(c);
702         yytext[yylen] = 0;
703         r = processint(yytext);
704         goto leave;
705     }
706 
707 digits_2:
708 
709     c = input();
710 
711     if (c >= '0' && c <= '9')
712         goto digits_2;
713     else if (c == 'e' || c == 'E')
714         goto expon_1;
715     //	else if (c == 'π' || c == '∏') {
716     //		--yylen;
717     //		yytext[yylen] = 0;
718     //		r = processfloat(yytext, 1);
719     //		goto leave;
720     //	}
721     else {
722         unput(c);
723         yytext[yylen] = 0;
724         r = processfloat(yytext, 0);
725         goto leave;
726     }
727 
728 expon_1: /* e has been seen, need digits */
729     c = input();
730 
731     if (c >= '0' && c <= '9')
732         goto expon_3;
733     else if (c == '+' || c == '-')
734         goto expon_2;
735     else
736         goto error1;
737 
738 expon_2: /* + or - seen but still need digits */
739     c = input();
740 
741     if (c >= '0' && c <= '9')
742         goto expon_3;
743     else
744         goto error1;
745 
746 expon_3:
747     c = input();
748 
749     if (c >= '0' && c <= '9')
750         goto expon_3;
751     //	else if (c == 'π' || c == '∏') {
752     //		--yylen;
753     //		yytext[yylen] = 0;
754     //		r = processfloat(yytext, 1);
755     //		goto leave;
756     //	}
757     else {
758         unput(c);
759         yytext[yylen] = 0;
760         r = processfloat(yytext, 0);
761         goto leave;
762     }
763 
764 symbol3 : {
765     int startline, endchar;
766     startline = lineno;
767     endchar = '\'';
768 
769     /*do {
770         c = input();
771     } while (c != endchar && c != 0);*/
772     for (; yylen < MAXYYLEN;) {
773         c = input();
774         if (c == '\n' || c == '\r') {
775             post("Symbol open at end of line on line %d of %s\n", startline + errLineOffset,
776                  printingCurrfilename.c_str());
777             yylen = 0;
778             r = 0;
779             goto leave;
780         }
781         if (c == '\\') {
782             yylen--;
783             c = input();
784         } else if (c == endchar)
785             break;
786         if (c == 0)
787             break;
788     }
789     if (c == 0) {
790         post("Open ended symbol started on line %d of %s\n", startline + errLineOffset, printingCurrfilename.c_str());
791         yylen = 0;
792         r = 0;
793         goto leave;
794     }
795     yytext[yylen] = 0;
796     yytext[yylen - 1] = 0;
797     r = processsymbol(yytext);
798     goto leave;
799 }
800 
801 string1 : {
802     int startline, endchar;
803     startline = lineno;
804     endchar = '"';
805 
806     for (; yylen < MAXYYLEN;) {
807         c = input();
808         if (c == '\\') {
809             yylen--;
810             c = input();
811             switch (c) {
812             case 'n':
813                 yytext[yylen - 1] = '\n';
814                 break;
815             case 'r':
816                 yytext[yylen - 1] = '\r';
817                 break;
818             case 't':
819                 yytext[yylen - 1] = '\t';
820                 break;
821             case 'f':
822                 yytext[yylen - 1] = '\f';
823                 break;
824             case 'v':
825                 yytext[yylen - 1] = '\v';
826                 break;
827             }
828         } else if (c == '\r')
829             c = '\n';
830         else if (c == endchar)
831             break;
832         if (c == 0)
833             break;
834     }
835     if (c == 0) {
836         post("Open ended string started on line %d of %s\n", startline + errLineOffset, printingCurrfilename.c_str());
837         yylen = 0;
838         r = 0;
839         goto leave;
840     }
841     yylen--;
842 
843     do {
844         c = input0();
845     } while (c && isspace(c));
846 
847     if (c == '"')
848         goto string1;
849     else if (c)
850         unput0(c);
851 
852     yytext[yylen] = 0;
853     r = processstring(yytext);
854     goto leave;
855 }
856 
857 comment1: /* comment -- to end of line */
858     do {
859         c = input0();
860     } while (c != '\n' && c != '\r' && c != 0);
861     yylen = 0;
862     if (c == 0) {
863         r = 0;
864         goto leave;
865     } else
866         goto start;
867 
868 comment2 : {
869     int startline, clevel, prevc;
870     startline = lineno;
871     prevc = 0;
872     clevel = 1;
873     do {
874         c = input0();
875         if (c == '/' && prevc == '*') {
876             if (--clevel <= 0)
877                 break;
878             else
879                 prevc = c, c = input0(); // eat both characters
880         } else if (c == '*' && prevc == '/') {
881             clevel++;
882             prevc = c, c = input0(); // eat both characters
883         }
884         prevc = c;
885     } while (c != 0);
886     yylen = 0;
887     if (c == 0) {
888         post("Open ended comment started on line %d of %s\n", startline + errLineOffset, printingCurrfilename.c_str());
889         r = 0;
890         goto leave;
891     }
892     goto start;
893 }
894 
895 
896 error1:
897 
898     yytext[yylen] = 0;
899 
900     post("illegal input string '%s' \n   in %s line %d char %d\n", yytext, printingCurrfilename.c_str(),
901          lineno + errLineOffset, charno);
902     post("code %d\n", c);
903     // postfl(" '%c' '%s'\n", c, binopchars);
904     // postfl("%d\n", strchr(binopchars, c));
905 
906 error2:
907     post("  in %s line %d char %d\n", printingCurrfilename.c_str(), lineno + errLineOffset, charno);
908     r = BADTOKEN;
909     goto leave;
910 
911 leave:
912     yytext[yylen] = 0;
913 
914 #if DEBUGLEX
915     if (gDebugLexer)
916         postfl("yylex: %d  '%s'\n", r, yytext);
917 #endif
918     // if (lexCmdLine>0) postfl("yylex: %d  '%s'\n",r,yytext);
919     return r;
920 }
921 
processbinop(char * token)922 int processbinop(char* token) {
923     PyrSymbol* sym;
924     PyrSlot slot;
925     PyrSlotNode* node;
926 
927 #if DEBUGLEX
928     if (gDebugLexer)
929         postfl("processbinop: '%s'\n", token);
930 #endif
931     sym = getsym(token);
932     SetSymbol(&slot, sym);
933     node = newPyrSlotNode(&slot);
934     zzval = (intptr_t)node;
935     if (strcmp(token, "<-") == 0)
936         return LEFTARROW;
937     if (strcmp(token, "<>") == 0)
938         return READWRITEVAR;
939     if (strcmp(token, "|") == 0)
940         return '|';
941     if (strcmp(token, "<") == 0)
942         return '<';
943     if (strcmp(token, ">") == 0)
944         return '>';
945     if (strcmp(token, "-") == 0)
946         return '-';
947     if (strcmp(token, "*") == 0)
948         return '*';
949     if (strcmp(token, "+") == 0)
950         return '+';
951     return BINOP;
952 }
953 
processkeywordbinop(char * token)954 int processkeywordbinop(char* token) {
955     PyrSymbol* sym;
956     PyrSlot slot;
957     PyrSlotNode* node;
958 
959     // post("'%s'  file '%s'\n", token, currfilename);
960 
961 #if DEBUGLEX
962     if (gDebugLexer)
963         postfl("processkeywordbinop: '%s'\n", token);
964 #endif
965     token[strlen(token) - 1] = 0; // strip off colon
966     sym = getsym(token);
967     SetSymbol(&slot, sym);
968     node = newPyrSlotNode(&slot);
969     zzval = (intptr_t)node;
970     return KEYBINOP;
971 }
972 
processident(char * token)973 int processident(char* token) {
974     char c;
975     PyrSymbol* sym;
976 
977     PyrSlot slot;
978     PyrParseNode* node;
979 
980     c = token[0];
981     zzval = (intptr_t)-1;
982 
983 #if DEBUGLEX
984     if (gDebugLexer)
985         postfl("word: '%s'\n", token);
986 #endif
987     /*
988     strcpy(uptoken, token);
989     for (str = uptoken; *str; ++str) {
990         if (*str >= 'a' && *str <= 'z') *str += 'A' - 'a';
991     }*/
992 
993     if (token[0] == '_') {
994         if (token[1] == 0) {
995             node = newPyrCurryArgNode();
996             zzval = (intptr_t)node;
997             return CURRYARG;
998         } else {
999             sym = getsym(token);
1000             SetSymbol(&slot, sym);
1001             node = newPyrSlotNode(&slot);
1002             zzval = (intptr_t)node;
1003             return PRIMITIVENAME;
1004         }
1005     }
1006     if (token[0] >= 'A' && token[0] <= 'Z') {
1007         sym = getsym(token);
1008         SetSymbol(&slot, sym);
1009         node = newPyrSlotNode(&slot);
1010         zzval = (intptr_t)node;
1011 #if DEBUGLEX
1012         if (gDebugLexer)
1013             postfl("CLASSNAME: '%s'\n", token);
1014 #endif
1015         return CLASSNAME;
1016     }
1017     if (strcmp("var", token) == 0)
1018         return VAR;
1019     if (strcmp("arg", token) == 0)
1020         return ARG;
1021     if (strcmp("classvar", token) == 0)
1022         return CLASSVAR;
1023     if (strcmp("const", token) == 0)
1024         return SC_CONST;
1025 
1026     if (strcmp("while", token) == 0) {
1027         sym = getsym(token);
1028         SetSymbol(&slot, sym);
1029         node = newPyrSlotNode(&slot);
1030         zzval = (intptr_t)node;
1031         return WHILE;
1032     }
1033     if (strcmp("pi", token) == 0) {
1034         SetFloat(&slot, pi);
1035         node = newPyrSlotNode(&slot);
1036         zzval = (intptr_t)node;
1037         return PIE;
1038     }
1039     if (strcmp("true", token) == 0) {
1040         SetTrue(&slot);
1041         node = newPyrSlotNode(&slot);
1042         zzval = (intptr_t)node;
1043         return TRUEOBJ;
1044     }
1045     if (strcmp("false", token) == 0) {
1046         SetFalse(&slot);
1047         node = newPyrSlotNode(&slot);
1048         zzval = (intptr_t)node;
1049         return FALSEOBJ;
1050     }
1051     if (strcmp("nil", token) == 0) {
1052         SetNil(&slot);
1053         node = newPyrSlotNode(&slot);
1054         zzval = (intptr_t)node;
1055         return NILOBJ;
1056     }
1057     if (strcmp("inf", token) == 0) {
1058         SetFloat(&slot, std::numeric_limits<double>::infinity());
1059         node = newPyrSlotNode(&slot);
1060         zzval = (intptr_t)node;
1061         return SC_FLOAT;
1062     }
1063 
1064     sym = getsym(token);
1065 
1066     SetSymbol(&slot, sym);
1067     node = newPyrSlotNode(&slot);
1068     zzval = (intptr_t)node;
1069     return NAME;
1070 }
1071 
processhex(char * s)1072 int processhex(char* s) {
1073     PyrSlot slot;
1074     PyrSlotNode* node;
1075     char* c;
1076     int val;
1077 #if DEBUGLEX
1078     if (gDebugLexer)
1079         postfl("processhex: '%s'\n", s);
1080 #endif
1081 
1082     c = s;
1083     val = 0;
1084     while (*c) {
1085         if (*c >= '0' && *c <= '9')
1086             val = val * 16 + *c - '0';
1087         else if (*c >= 'a' && *c <= 'z')
1088             val = val * 16 + *c - 'a' + 10;
1089         else if (*c >= 'A' && *c <= 'Z')
1090             val = val * 16 + *c - 'A' + 10;
1091         c++;
1092     }
1093 
1094     SetInt(&slot, val);
1095     node = newPyrSlotNode(&slot);
1096     zzval = (intptr_t)node;
1097     return INTEGER;
1098 }
1099 
1100 
processintradix(char * s,int n,int radix)1101 int processintradix(char* s, int n, int radix) {
1102     PyrSlot slot;
1103     PyrSlotNode* node;
1104 #if DEBUGLEX
1105     if (gDebugLexer)
1106         postfl("processintradix: '%s'\n", s);
1107 #endif
1108 
1109     SetInt(&slot, sc_strtoi(s, n, radix));
1110     node = newPyrSlotNode(&slot);
1111     zzval = (intptr_t)node;
1112     return INTEGER;
1113 }
1114 
processfloatradix(char * s,int n,int radix)1115 int processfloatradix(char* s, int n, int radix) {
1116     PyrSlot slot;
1117     PyrSlotNode* node;
1118 #if DEBUGLEX
1119     if (gDebugLexer)
1120         postfl("processfloatradix: '%s'\n", s);
1121 #endif
1122 
1123     SetFloat(&slot, sc_strtof(s, n, radix));
1124     node = newPyrSlotNode(&slot);
1125     zzval = (intptr_t)node;
1126     return SC_FLOAT;
1127 }
1128 
processint(char * s)1129 int processint(char* s) {
1130     PyrSlot slot;
1131     PyrSlotNode* node;
1132 #if DEBUGLEX
1133     if (gDebugLexer)
1134         postfl("processint: '%s'\n", s);
1135 #endif
1136 
1137     SetInt(&slot, atoi(s));
1138     node = newPyrSlotNode(&slot);
1139     zzval = (intptr_t)node;
1140     return INTEGER;
1141 }
1142 
processchar(int c)1143 int processchar(int c) {
1144     PyrSlot slot;
1145     PyrSlotNode* node;
1146 #if DEBUGLEX
1147     if (gDebugLexer)
1148         postfl("processhex: '%c'\n", c);
1149 #endif
1150 
1151     SetChar(&slot, c);
1152     node = newPyrSlotNode(&slot);
1153     zzval = (intptr_t)node;
1154     return ASCII;
1155 }
1156 
processfloat(char * s,int sawpi)1157 int processfloat(char* s, int sawpi) {
1158     PyrSlot slot;
1159     PyrSlotNode* node;
1160     double z;
1161 #if DEBUGLEX
1162     if (gDebugLexer)
1163         postfl("processfloat: '%s'\n", s);
1164 #endif
1165 
1166     if (sawpi) {
1167         z = atof(s) * pi;
1168         SetFloat(&slot, z);
1169     } else {
1170         SetFloat(&slot, atof(s));
1171     }
1172     node = newPyrSlotNode(&slot);
1173     zzval = (intptr_t)node;
1174     return SC_FLOAT;
1175 }
1176 
1177 
processaccidental1(char * s)1178 int processaccidental1(char* s) {
1179     PyrSlot slot;
1180     PyrSlotNode* node;
1181     char* c;
1182     double degree = 0.;
1183     double cents = 0.;
1184     double centsdiv = 1000.;
1185 #if 0
1186 	printf("processaccidental1: '%s'\n",s);
1187 #endif
1188 
1189     c = s;
1190     while (*c) {
1191         if (*c >= '0' && *c <= '9')
1192             degree = degree * 10. + *c - '0';
1193         else
1194             break;
1195         c++;
1196     }
1197 
1198     if (*c == 'b')
1199         centsdiv = -1000.;
1200     else if (*c == 's')
1201         centsdiv = 1000.;
1202     c++;
1203 
1204     while (*c) {
1205         if (*c >= '0' && *c <= '9') {
1206             cents = cents * 10. + *c - '0';
1207         } else
1208             break;
1209         c++;
1210     }
1211 
1212     if (cents > 499.)
1213         cents = 499.;
1214 
1215     SetFloat(&slot, degree + cents / centsdiv);
1216     node = newPyrSlotNode(&slot);
1217     zzval = (intptr_t)node;
1218     return ACCIDENTAL;
1219 }
1220 
processaccidental2(char * s)1221 int processaccidental2(char* s) {
1222     PyrSlot slot;
1223     PyrSlotNode* node;
1224     char* c;
1225     double degree = 0.;
1226     double semitones = 0.;
1227 #if 0
1228 	printf("processaccidental2: '%s'\n",s);
1229 #endif
1230 
1231     c = s;
1232     while (*c) {
1233         if (*c >= '0' && *c <= '9')
1234             degree = degree * 10. + *c - '0';
1235         else
1236             break;
1237         c++;
1238     }
1239 
1240     while (*c) {
1241         if (*c == 'b')
1242             semitones -= 1.;
1243         else if (*c == 's')
1244             semitones += 1.;
1245         c++;
1246     }
1247 
1248     if (semitones > 4.)
1249         semitones = 4.;
1250     else if (semitones < -4.)
1251         semitones = -4.;
1252 
1253     SetFloat(&slot, degree + semitones / 10.);
1254     node = newPyrSlotNode(&slot);
1255     zzval = (intptr_t)node;
1256     return ACCIDENTAL;
1257 }
1258 
processsymbol(char * s)1259 int processsymbol(char* s) {
1260     PyrSlot slot;
1261     PyrSlotNode* node;
1262     PyrSymbol* sym;
1263 #if DEBUGLEX
1264     if (gDebugLexer)
1265         postfl("processsymbol: '%s'\n", s);
1266 #endif
1267     sym = getsym(s + 1);
1268 
1269     SetSymbol(&slot, sym);
1270     node = newPyrSlotNode(&slot);
1271     zzval = (intptr_t)node;
1272     return SYMBOL;
1273 }
1274 
processstring(char * s)1275 int processstring(char* s) {
1276     PyrSlot slot;
1277     PyrSlotNode* node;
1278     PyrString* string;
1279 #if DEBUGLEX
1280     if (gDebugLexer)
1281         postfl("processstring: '%s'\n", s);
1282 #endif
1283     int flags = compilingCmdLine ? obj_immutable : obj_permanent | obj_immutable;
1284     string = newPyrString(gMainVMGlobals->gc, s + 1, flags, false);
1285     SetObject(&slot, string);
1286     node = newPyrSlotNode(&slot);
1287     zzval = (intptr_t)node;
1288     return STRING;
1289 }
1290 
yyerror(const char * s)1291 void yyerror(const char* s) {
1292     parseFailed = 1;
1293     yytext[yylen] = 0;
1294     error("%s\n", s);
1295     postErrorLine(lineno, linepos, charno);
1296     // Debugger();
1297 }
1298 
fatal()1299 void fatal() {
1300     parseFailed = 1;
1301     yytext[yylen] = 0;
1302     error("Parse error\n");
1303     postErrorLine(lineno, linepos, charno);
1304     // Debugger();
1305 }
1306 
postErrorLine(int linenum,int start,int charpos)1307 void postErrorLine(int linenum, int start, int charpos) {
1308     int i, j, end, pos;
1309     char str[256];
1310 
1311     // post("start %d\n", start);
1312     // parseFailed = true;
1313     post("  in %s\n", printingCurrfilename.c_str());
1314     post("  line %d char %d:\n\n", linenum + errLineOffset, charpos);
1315     // nice: postfl previous line for context
1316 
1317     // postfl("text '%s' %d\n", text, text);
1318 
1319     // postfl error line for context
1320     pos = start + charpos;
1321     for (i = pos; i < textlen; ++i) {
1322         if (text[i] == 0 || text[i] == '\r' || text[i] == '\n')
1323             break;
1324     }
1325     end = i;
1326     for (i = start, j = 0; i < end && j < 255; ++i) {
1327         str[j++] = text[i];
1328     }
1329     str[j] = 0;
1330     post("  %s\n  ", str);
1331     for (i = 0; i < charpos - yylen; i++)
1332         post(" ");
1333     for (i = 0; i < yylen; i++)
1334         post("^");
1335     post("\n");
1336 
1337     i = end + 1;
1338     if (i < textlen) {
1339         // postfl following line for context
1340         for (j = 0; j < 255 && i < textlen; ++i) {
1341             if (text[i] == 0 || text[i] == '\r' || text[i] == '\n')
1342                 break;
1343             str[j++] = text[i];
1344         }
1345         str[j] = 0;
1346         post("  %s\n", str);
1347     }
1348     post("-----------------------------------\n", str);
1349 }
1350 
1351 void pstrncpy(unsigned char* s1, unsigned char* s2, int n);
pstrncpy(unsigned char * s1,unsigned char * s2,int n)1352 void pstrncpy(unsigned char* s1, unsigned char* s2, int n) {
1353     int i, m;
1354     m = *s2++;
1355     n = (n < m) ? n : m;
1356     *s1 = n;
1357     s1++;
1358     for (i = 0; i < n; ++i) {
1359         *s1 = *s2;
1360         s1++;
1361         s2++;
1362     }
1363 }
1364 
1365 int pstrcmp(unsigned char* s1, unsigned char* s2);
pstrcmp(unsigned char * s1,unsigned char * s2)1366 int pstrcmp(unsigned char* s1, unsigned char* s2) {
1367     int i, len1, len2, len;
1368     len1 = *s1++;
1369     len2 = *s2++;
1370     len = sc_min(len1, len2);
1371     for (i = 0; i < len; ++i) {
1372         if (s1[i] < s2[i])
1373             return -1;
1374         if (s1[i] > s2[i])
1375             return 1;
1376     }
1377     if (len1 < len2)
1378         return -1;
1379     if (len1 > len2)
1380         return 1;
1381     return 0;
1382 }
1383 
scanForClosingBracket()1384 bool scanForClosingBracket() {
1385     int r, c, startLevel;
1386     intptr_t d;
1387     bool res = true;
1388     // finite state machine to parse input stream into tokens
1389 
1390 #if DEBUGLEX
1391     if (gDebugLexer)
1392         postfl("->scanForClosingBracket\n");
1393 #endif
1394     startLevel = brackets.num;
1395 start:
1396     c = input0();
1397 
1398     if (c == 0)
1399         goto leave;
1400     else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f') {
1401         goto start;
1402     } else if (c == '\'')
1403         goto symbol3;
1404     else if (c == '"')
1405         goto string1;
1406     else if (c == '/') {
1407         c = input0();
1408         if (c == '/')
1409             goto comment1;
1410         else if (c == '*')
1411             goto comment2;
1412         else {
1413             unput(c);
1414             goto start;
1415         }
1416     } else if (c == '$') {
1417         c = input0();
1418         if (c == '\\') {
1419             c = input0();
1420             switch (c) {
1421             case 'n':
1422                 c = '\n';
1423                 break;
1424             case 'r':
1425                 c = '\r';
1426                 break;
1427             case 't':
1428                 c = '\t';
1429                 break;
1430             case 'f':
1431                 c = '\f';
1432                 break;
1433             case 'v':
1434                 c = '\v';
1435                 break;
1436             }
1437         }
1438         goto start;
1439     } else if (c == OPENPAREN || c == OPENSQUAR || c == OPENCURLY) {
1440         pushls(&brackets, (intptr_t)c);
1441         r = c;
1442         goto start;
1443     } else if (c == CLOSSQUAR) {
1444         if (!emptyls(&brackets)) {
1445             if ((d = popls(&brackets)) != OPENSQUAR) {
1446                 fatal();
1447                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
1448                 goto error1;
1449             }
1450         } else {
1451             fatal();
1452             post("unmatched '%c'\n", c);
1453             goto error1;
1454         }
1455         r = c;
1456         if (brackets.num < startLevel)
1457             goto leave;
1458         else
1459             goto start;
1460     } else if (c == CLOSPAREN) {
1461         if (!emptyls(&brackets)) {
1462             if ((d = popls(&brackets)) != (intptr_t)OPENPAREN) {
1463                 fatal();
1464                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
1465                 goto error1;
1466             }
1467         } else {
1468             fatal();
1469             post("unmatched '%c'\n", c);
1470             goto error1;
1471         }
1472         if (brackets.num < startLevel)
1473             goto leave;
1474         else
1475             goto start;
1476     } else if (c == CLOSCURLY) {
1477         if (!emptyls(&brackets)) {
1478             if ((d = popls(&brackets)) != OPENCURLY) {
1479                 fatal();
1480                 post("opening bracket was a '%c', but found a '%c'\n", d, c);
1481                 goto error1;
1482             }
1483         } else {
1484             fatal();
1485             post("unmatched '%c'\n", c);
1486             goto error1;
1487         }
1488         if (brackets.num < startLevel)
1489             goto leave;
1490         else
1491             goto start;
1492     } else {
1493         goto start;
1494     }
1495 symbol3 : {
1496     int startline, endchar;
1497     startline = lineno;
1498     endchar = '\'';
1499 
1500     do {
1501         c = input0();
1502         if (c == '\\') {
1503             input0();
1504         }
1505     } while (c != endchar && c != 0);
1506     if (c == 0) {
1507         post("Open ended symbol started on line %d of %s\n", startline, printingCurrfilename.c_str());
1508         goto error2;
1509     }
1510     goto start;
1511 }
1512 
1513 string1 : {
1514     int startline, endchar;
1515     startline = lineno;
1516     endchar = '\"';
1517 
1518     do {
1519         c = input0();
1520         if (c == '\\') {
1521             input0();
1522         }
1523     } while (c != endchar && c != 0);
1524     if (c == 0) {
1525         post("Open ended string started on line %d of %s\n", startline, printingCurrfilename.c_str());
1526         goto error2;
1527     }
1528     goto start;
1529 }
1530 comment1: /* comment -- to end of line */
1531     do {
1532         c = input0();
1533     } while (c != '\n' && c != '\r' && c != 0);
1534     if (c == 0) {
1535         goto leave;
1536     } else
1537         goto start;
1538 
1539 comment2 : {
1540     int startline, clevel, prevc;
1541     startline = lineno;
1542     prevc = 0;
1543     clevel = 1;
1544     do {
1545         c = input0();
1546         if (c == '/' && prevc == '*') {
1547             if (--clevel <= 0)
1548                 break;
1549             else
1550                 prevc = c, c = input0(); // eat both characters
1551         } else if (c == '*' && prevc == '/') {
1552             clevel++;
1553             prevc = c, c = input0(); // eat both characters
1554         }
1555         prevc = c;
1556     } while (c != 0);
1557     if (c == 0) {
1558         post("Open ended comment started on line %d of %s\n", startline, printingCurrfilename.c_str());
1559         goto error2;
1560     }
1561     goto start;
1562 }
1563 
1564 error1:
1565     post("  in %s line %d char %d\n", printingCurrfilename.c_str(), lineno, charno);
1566     res = false;
1567     goto leave;
1568 
1569 error2:
1570     res = false;
1571     goto leave;
1572 
1573 leave:
1574 #if DEBUGLEX
1575     if (gDebugLexer)
1576         postfl("<-scanForClosingBracket\n");
1577 #endif
1578     return res;
1579 }
1580 
1581 
1582 int numClassDeps;
1583 static ClassExtFile* sClassExtFiles;
1584 static ClassExtFile* eClassExtFiles;
1585 
1586 ClassExtFile* newClassExtFile(PyrSymbol* fileSym, int startPos, int endPos);
newClassExtFile(PyrSymbol * fileSym,int startPos,int endPos)1587 ClassExtFile* newClassExtFile(PyrSymbol* fileSym, int startPos, int endPos) {
1588     ClassExtFile* classext;
1589     classext = (ClassExtFile*)pyr_pool_compile->Alloc(sizeof(ClassExtFile));
1590     classext->fileSym = fileSym;
1591     classext->next = nullptr;
1592     classext->startPos = startPos;
1593     classext->endPos = endPos;
1594     if (!sClassExtFiles)
1595         sClassExtFiles = classext;
1596     else
1597         eClassExtFiles->next = classext;
1598     eClassExtFiles = classext;
1599     return classext;
1600 }
1601 
1602 
newClassDependancy(PyrSymbol * className,PyrSymbol * superClassName,PyrSymbol * fileSym,int startPos,int endPos,int lineOffset)1603 ClassDependancy* newClassDependancy(PyrSymbol* className, PyrSymbol* superClassName, PyrSymbol* fileSym, int startPos,
1604                                     int endPos, int lineOffset) {
1605     ClassDependancy* classdep;
1606 
1607     // post("classdep '%s' '%s' '%s' %d %d\n", className->name, superClassName->name,
1608     //	fileSym->name, className, superClassName);
1609     // pyrmalloc:
1610     // lifetime: kill after compile.
1611     numClassDeps++;
1612     if (className->classdep) {
1613         error("duplicate Class found: '%s' \n", className->name);
1614         post("%s\n", className->classdep->fileSym->name);
1615         postfl("%s\n\n", fileSym->name);
1616         return className->classdep;
1617     }
1618     classdep = (ClassDependancy*)pyr_pool_compile->Alloc(sizeof(ClassDependancy));
1619     MEMFAIL(text);
1620     classdep->className = className;
1621     classdep->superClassName = superClassName;
1622     classdep->fileSym = fileSym;
1623     classdep->superClassDep = nullptr;
1624     classdep->next = nullptr;
1625     classdep->subclasses = nullptr;
1626 
1627     classdep->startPos = startPos;
1628     classdep->endPos = endPos;
1629     classdep->lineOffset = lineOffset;
1630 
1631     className->classdep = classdep;
1632     return classdep;
1633 }
1634 
buildDepTree()1635 void buildDepTree() {
1636     ClassDependancy* next;
1637     SymbolTable* symbolTable = gMainVMGlobals->symbolTable;
1638 
1639     // postfl("->buildDepTree\n"); fflush(stdout);
1640     for (int i = 0; i < symbolTable->TableSize(); ++i) {
1641         PyrSymbol* sym = symbolTable->Get(i);
1642         if (sym && (sym->flags & sym_Class)) {
1643             if (sym->classdep) {
1644                 if (sym->classdep->superClassName->classdep) {
1645                     next = sym->classdep->superClassName->classdep->subclasses;
1646                     sym->classdep->superClassName->classdep->subclasses = sym->classdep;
1647                     sym->classdep->next = next;
1648                 } else if (sym->classdep->superClassName != s_none) {
1649                     error("Superclass '%s' of class '%s' is not defined in any file.\n%s\n",
1650                           sym->classdep->superClassName->name, sym->classdep->className->name,
1651                           sym->classdep->fileSym->name);
1652                 }
1653             }
1654         }
1655     }
1656     // postfl("<-buildDepTree\n"); fflush(stdout);
1657 }
1658 
1659 extern PyrClass* gClassList;
1660 
1661 ClassDependancy** gClassCompileOrder;
1662 int gClassCompileOrderNum = 0;
1663 int gClassCompileOrderSize = 1000;
1664 
1665 void compileDepTree();
1666 
traverseFullDepTree()1667 void traverseFullDepTree() {
1668     // postfl("->traverseFullDepTree\n"); fflush(stdout);
1669     gClassCompileOrderNum = 0;
1670     gClassCompileOrder = (ClassDependancy**)pyr_pool_compile->Alloc(gClassCompileOrderSize * sizeof(ClassDependancy));
1671     MEMFAIL(gClassCompileOrder);
1672 
1673     // parse and compile all files
1674     initParser(); // sets compiler errors to 0
1675     gParserResult = -1;
1676 
1677     traverseDepTree(s_object->classdep, 0);
1678     compileDepTree(); // compiles backwards using the order defined in gClassCompileOrder
1679     compileClassExtensions();
1680 
1681     pyr_pool_compile->Free(gClassCompileOrder);
1682 
1683     finiParser();
1684     // postfl("<-traverseFullDepTree\n"); fflush(stdout);
1685 }
1686 
1687 
traverseDepTree(ClassDependancy * classdep,int level)1688 void traverseDepTree(ClassDependancy* classdep, int level) {
1689     ClassDependancy* subclassdep;
1690 
1691     if (!classdep)
1692         return;
1693 
1694     subclassdep = classdep->subclasses;
1695     for (; subclassdep; subclassdep = subclassdep->next) {
1696         traverseDepTree(subclassdep, level + 1);
1697     }
1698     if (gClassCompileOrderNum > gClassCompileOrderSize) {
1699         gClassCompileOrderSize *= 2;
1700         gClassCompileOrder = (ClassDependancy**)pyr_pool_compile->Realloc(
1701             gClassCompileOrder, gClassCompileOrderSize * sizeof(ClassDependancy));
1702         MEMFAIL(gClassCompileOrder);
1703     }
1704 
1705     /*	postfl("traverse level:%d, gClassCompileOrderNum:%d, '%s' '%s' '%s'\n", level, gClassCompileOrderNum,
1706        classdep->className->name, classdep->superClassName->name, classdep->fileSym->name); fflush(stdout);
1707     */
1708 
1709     gClassCompileOrder[gClassCompileOrderNum++] = classdep;
1710 }
1711 
1712 
compileClass(PyrSymbol * fileSym,int startPos,int endPos,int lineOffset)1713 void compileClass(PyrSymbol* fileSym, int startPos, int endPos, int lineOffset) {
1714     // fprintf(stderr, "compileClass: %d\n", fileSym->u.index);
1715 
1716     gCompilingFileSym = fileSym;
1717     gCompilingVMGlobals = nullptr;
1718     gRootParseNode = nullptr;
1719     initParserPool();
1720     if (startLexer(fileSym, bfs::path(), startPos, endPos, lineOffset)) {
1721         // postfl("->Parsing %s\n", fileSym->name); fflush(stdout);
1722         parseFailed = yyparse();
1723         // postfl("<-Parsing %s %d\n", fileSym->name, parseFailed); fflush(stdout);
1724         // post("parseFailed %d\n", parseFailed); fflush(stdout);
1725         if (!parseFailed && gRootParseNode) {
1726             // postfl("Compiling nodes %p\n", gRootParseNode);fflush(stdout);
1727             compilingCmdLine = false;
1728             compileNodeList(gRootParseNode, true);
1729             // postfl("done compiling\n");fflush(stdout);
1730         } else {
1731             compileErrors++;
1732             bfs::path pathname(fileSym->name);
1733             error("file '%s' parse failed\n", SC_Codecvt::path_to_utf8_str(pathname).c_str());
1734             postfl("error parsing\n");
1735         }
1736         finiLexer();
1737     } else {
1738         error("file '%s' open failed\n", fileSym->name);
1739     }
1740     freeParserPool();
1741 }
1742 
compileDepTree()1743 void compileDepTree() {
1744     ClassDependancy* classdep;
1745     int i;
1746 
1747     for (i = gClassCompileOrderNum - 1; i >= 0; --i) {
1748         classdep = gClassCompileOrder[i];
1749         /*postfl("compile %d '%s' '%s' '%s'...%d/%d/%d\n", i, classdep->className->name, classdep->superClassName->name,
1750             classdep->fileSym->name, classdep->startLine, classdep->endLine, classDep->lineOffset);*/
1751         compileClass(classdep->fileSym, classdep->startPos, classdep->endPos, classdep->lineOffset);
1752     }
1753     // postfl("<compile\n");
1754 }
1755 
compileClassExtensions()1756 void compileClassExtensions() {
1757     if (sClassExtFiles) {
1758         ClassExtFile* classext = sClassExtFiles;
1759         do {
1760             // postfl("compile class ext: %d/%d\n", classext->startPos, classext->endPos);
1761             compileClass(classext->fileSym, classext->startPos, classext->endPos, -1);
1762             classext = classext->next;
1763         } while (classext);
1764     }
1765 }
1766 
1767 void findDiscrepancy();
1768 
traverseFullDepTree2()1769 void traverseFullDepTree2() {
1770     // assign a class index to all classes
1771     if (!parseFailed && !compileErrors) {
1772         buildClassTree();
1773         gNumClasses = 0;
1774 
1775         // now I index them during pass one
1776         indexClassTree(class_object, 0);
1777         setSelectorFlags();
1778         if (2 * numClassDeps != gNumClasses) {
1779             error("There is a discrepancy.\n");
1780             /* not always correct
1781                     if(2*numClassDeps < gNumClasses) {
1782                         post("Duplicate files may exist in the directory structure.\n");
1783                     } else {
1784                         post("Some class files may be missing.\n");
1785                     }
1786                     */
1787             post("numClassDeps %d   gNumClasses %d\n", numClassDeps, gNumClasses);
1788             findDiscrepancy();
1789             compileErrors++;
1790         } else {
1791             double elapsed;
1792             buildBigMethodMatrix();
1793             SymbolTable* symbolTable = gMainVMGlobals->symbolTable;
1794             post("\tNumber of Symbols %d\n", symbolTable->NumItems());
1795             post("\tByte Code Size %d\n", totalByteCodes);
1796             // elapsed = TickCount() - compileStartTime;
1797             // elapsed = 0;
1798             elapsed = elapsedTime() - compileStartTime;
1799             post("\tcompiled %d files in %.2f seconds\n", gNumCompiledFiles, elapsed);
1800             if (numOverwrites == 1) {
1801                 post("\nInfo: One method is currently overwritten by an extension. To see which, "
1802                      "execute:\nMethodOverride.printAll\n\n");
1803             } else if (numOverwrites > 1) {
1804                 post("\nInfo: %i methods are currently overwritten by extensions. To see which, "
1805                      "execute:\nMethodOverride.printAll\n\n",
1806                      numOverwrites);
1807             }
1808             post("compile done\n");
1809         }
1810     }
1811 }
1812 
parseOneClass(PyrSymbol * fileSym)1813 bool parseOneClass(PyrSymbol* fileSym) {
1814     int token;
1815     PyrSymbol *className, *superClassName;
1816     ClassDependancy* classdep;
1817     bool res;
1818 
1819     int startPos, startLineOffset;
1820 
1821     res = true;
1822 
1823     startPos = textpos;
1824     startLineOffset = lineno - 1;
1825 
1826     token = yylex();
1827     if (token == CLASSNAME) {
1828         className = slotRawSymbol(&((PyrSlotNode*)zzval)->mSlot);
1829         // I think this is wrong: zzval is space pool alloced
1830         // pyrfree((PyrSlot*)zzval);
1831 
1832         token = yylex();
1833         if (token == 0)
1834             return false;
1835         if (token == OPENSQUAR) {
1836             scanForClosingBracket(); // eat indexing spec
1837             token = yylex();
1838             if (token == 0)
1839                 return false;
1840         }
1841         if (token == ':') {
1842             token = yylex(); // get super class
1843             if (token == 0)
1844                 return false;
1845             if (token == CLASSNAME) {
1846                 superClassName = slotRawSymbol(&((PyrSlotNode*)zzval)->mSlot);
1847                 // I think this is wrong: zzval is space pool alloced
1848                 // pyrfree((PyrSlot*)zzval);
1849                 token = yylex();
1850                 if (token == 0)
1851                     return false;
1852                 if (token == OPENCURLY) {
1853                     scanForClosingBracket(); // eat class body
1854                     classdep =
1855                         newClassDependancy(className, superClassName, fileSym, startPos, textpos, startLineOffset);
1856                 } else {
1857                     compileErrors++;
1858                     postfl("Expected %c.  got token: '%s' %d\n", OPENCURLY, yytext, token);
1859                     postErrorLine(lineno, linepos, charno);
1860                     return false;
1861                 }
1862             } else {
1863                 compileErrors++;
1864                 post("Expected superclass name.  got token: '%s' %d\n", yytext, token);
1865                 postErrorLine(lineno, linepos, charno);
1866                 return false;
1867             }
1868         } else if (token == OPENCURLY) {
1869             if (className == s_object)
1870                 superClassName = s_none;
1871             else
1872                 superClassName = s_object;
1873             scanForClosingBracket(); // eat class body
1874             classdep = newClassDependancy(className, superClassName, fileSym, startPos, textpos, startLineOffset);
1875         } else {
1876             compileErrors++;
1877             post("Expected ':' or %c.  got token: '%s' %d\n", OPENCURLY, yytext, token);
1878             postErrorLine(lineno, linepos, charno);
1879             return false;
1880         }
1881     } else if (token == '+') {
1882         token = yylex();
1883         if (token == 0)
1884             return false;
1885         scanForClosingBracket();
1886 
1887         newClassExtFile(fileSym, startPos, textpos);
1888         return false;
1889     } else {
1890         if (token != 0) {
1891             compileErrors++;
1892             post("Expected class name.  got token: '%s' %d\n", yytext, token);
1893             postErrorLine(lineno, linepos, charno);
1894             return false;
1895         } else {
1896             res = false;
1897         }
1898     }
1899     return res;
1900 }
1901 
initPassOne()1902 void initPassOne() {
1903     // dump_pool_histo(pyr_pool_runtime);
1904     pyr_pool_runtime->FreeAllInternal();
1905     // dump_pool_histo(pyr_pool_runtime);
1906     // gPermanentObjPool.Init(pyr_pool_runtime, PERMOBJCHUNK);
1907     sClassExtFiles = nullptr;
1908 
1909     void* ptr = pyr_pool_runtime->Alloc(sizeof(SymbolTable));
1910     gMainVMGlobals->symbolTable = new (ptr) SymbolTable(pyr_pool_runtime, 65536);
1911 
1912     initSymbols(); // initialize symbol globals
1913     initSpecialSelectors();
1914     initSpecialClasses();
1915     initClasses();
1916     initParserPool();
1917     initParseNodes();
1918     initPrimitives();
1919 
1920     initLexer();
1921 
1922     compileErrors = 0;
1923     numClassDeps = 0;
1924     compiledOK = false;
1925     compiledDirectories.clear();
1926 
1927     // main class library folder: only used for relative path resolution
1928     gCompileDir = SC_Filesystem::instance().getDirectory(DirName::Resource) / "SCClassLibrary";
1929 }
1930 
finiPassOne()1931 void finiPassOne() {
1932     // postfl("->finiPassOne\n");
1933     freeParserPool();
1934     // postfl("<-finiPassOne\n");
1935 }
1936 
1937 /**
1938  * \brief \c true if \c dir is one of the language config's default classlib directories
1939  */
isDefaultClassLibraryDirectory(const bfs::path & dir)1940 static bool isDefaultClassLibraryDirectory(const bfs::path& dir) {
1941     auto const& defaultDirs = gLanguageConfig->defaultClassLibraryDirectories();
1942     auto const iter = std::find(defaultDirs.begin(), defaultDirs.end(), dir);
1943     return iter != defaultDirs.end();
1944 }
1945 
1946 /**
1947  * \brief Handles a missing directory encountered during compilation.
1948  *
1949  * If the directory is one of the default directories traversed during compilation,
1950  * try to create it, silently ignoring failure (most likely from permissions failure).
1951  * Otherwise, warn the user to help catch mistyped/missing directory names. See #3468.
1952  */
passOne_HandleMissingDirectory(const bfs::path & dir)1953 static void passOne_HandleMissingDirectory(const bfs::path& dir) {
1954     if (isDefaultClassLibraryDirectory(dir)) {
1955         boost::system::error_code ec {};
1956         bfs::create_directories(dir, ec);
1957     } else {
1958         post("WARNING: Could not open directory: '%s'\n"
1959              "\tTo resolve this, either create the directory or remove it from your compilation paths.\n\n",
1960              SC_Codecvt::path_to_utf8_str(dir).c_str());
1961     }
1962 }
1963 
relativeToCompileDir(const bfs::path & p)1964 bfs::path relativeToCompileDir(const bfs::path& p) { return bfs::relative(p, gCompileDir); }
1965 
1966 /** \brief Determines whether the directory should be skipped during compilation.
1967  *
1968  * \param dir : The directory to check, as a `path` object
1969  * \returns `true` iff any of the following conditions is true:
1970  * - the directory has already been compiled
1971  * - the language configuration says this path is excluded
1972  * - SC_Filesystem::shouldNotCompileDirectory(dir) returns `true`
1973  */
passOne_ShouldSkipDirectory(const bfs::path & dir)1974 static bool passOne_ShouldSkipDirectory(const bfs::path& dir) {
1975     return (compiledDirectories.find(dir) != compiledDirectories.end())
1976         || (gLanguageConfig && gLanguageConfig->pathIsExcluded(dir))
1977         || (SC_Filesystem::instance().shouldNotCompileDirectory(dir));
1978 }
1979 
1980 /** \brief Compile the contents of a single directory
1981  *
1982  * This method compiles any .sc files in a single directory, working
1983  * via depth-first recursion. This routine is designed to fail gracefully,
1984  * and only indicates failure if something truly unexpected happens. These
1985  * conditions are:
1986  * - an error occurred while trying to open a directory, other than the case
1987  *    the case that the object doesn't exist.
1988  * - an error occurred while calling `passOne_processOneFile` on a file
1989  * - an error occurred in a recursive call of this routine on a macOS alias
1990  * Otherwise, this method returns success, even if:
1991  * - `dir` does not exist
1992  * - Iterating to the next file fails for any reason at all
1993  *
1994  * This method returns with a success state immediately if the directory
1995  * should not be compiled according to the language configuration.
1996  *
1997  * \param dir : The directory to traverse, as a `path` object
1998  * \returns `true` if processing was successful, `false` if it failed.
1999  *   See above for what constitutes success and failure conditions.
2000  */
passOne_ProcessDir(const bfs::path & dir)2001 static bool passOne_ProcessDir(const bfs::path& dir) {
2002     // Prefer non-throwing versions of filesystem functions, since they are actually not unexpected
2003     // and because it's faster to use error codes.
2004     boost::system::error_code ec;
2005 
2006     // Perform tilde expansion on incoming dir.
2007     const bfs::path expdir = SC_Filesystem::instance().expandTilde(dir);
2008 
2009     // Using a recursive_directory_iterator is much faster than actually calling this function
2010     // recursively. Speedup from the switch was about 1.5x. _Do_ recurse on symlinks.
2011     bfs::recursive_directory_iterator rditer(expdir, bfs::symlink_option::recurse, ec);
2012 
2013     // Check preconditions: are we able to access the file, and should we compile it according to
2014     // the language configuration?
2015     if (ec) {
2016         // If we got an error, post a warning if it was because the target wasn't found, and return success.
2017         // Otherwise, post the error and fail.
2018         if (ec.default_error_condition().value() == boost::system::errc::no_such_file_or_directory) {
2019             passOne_HandleMissingDirectory(expdir);
2020             return true;
2021         } else {
2022             error("Could not open directory '%s': (%d) %s\n", SC_Codecvt::path_to_utf8_str(expdir).c_str(), ec.value(),
2023                   ec.message().c_str());
2024 
2025             return false;
2026         }
2027     } else if (passOne_ShouldSkipDirectory(expdir)) {
2028         // If we should skip the directory, just return success now.
2029         return true;
2030     } else {
2031         // Let the user know we are in fact compiling this directory.
2032         post("\tCompiling directory '%s'\n", SC_Codecvt::path_to_utf8_str(expdir).c_str());
2033     }
2034 
2035     // Record that we have touched this directory already.
2036     compiledDirectories.insert(expdir);
2037 
2038     // Invariant: we have processed (or begun to process) every directory or file already
2039     // touched by the iterator.
2040     while (rditer != bfs::end(rditer)) {
2041         const bfs::path path = *rditer;
2042 
2043         // If the file is a directory, perform the same checks as above to see if we should
2044         // skip compilation on it.
2045         if (bfs::is_directory(path)) {
2046             if (passOne_ShouldSkipDirectory(path)) {
2047                 rditer.no_push(); // don't "push" into the next level of the hierarchy
2048             } else {
2049                 // Mark this directory as compiled.
2050                 // By not calling no_push(), we allow the iterator to enter the directory
2051                 compiledDirectories.insert(path);
2052             }
2053 
2054         } else { // ordinary file
2055             // Try to resolve a potential alias. Possible outcomes:
2056             // - it was an alias & is also a directory: try to recurse on it
2057             // - resolution failed: returns empty path: let the user know
2058             // - it was not an alias, or was an alias that wasn't a directory: try to process it as a source file
2059             bool isAlias = false;
2060             const bfs::path& respath = SC_Filesystem::resolveIfAlias(path, isAlias);
2061             if (isAlias && bfs::is_directory(respath)) {
2062                 // If the resolved alias is a directory, recurse on it.
2063                 if (!passOne_ProcessDir(respath)) {
2064                     return false;
2065                 }
2066             } else if (respath.empty()) {
2067                 error("Could not resolve symlink: %s\n", SC_Codecvt::path_to_utf8_str(path).c_str());
2068             } else if (!passOne_ProcessOneFile(respath)) {
2069                 return false;
2070             }
2071         }
2072 
2073         // Error-code version of `++`
2074         rditer.increment(ec);
2075         if (ec) {
2076             // If iteration failed, allow compilation to continue, but bail out of this directory.
2077             error("Could not iterate on '%s': %s\n", SC_Codecvt::path_to_utf8_str(path).c_str(), ec.message().c_str());
2078             return true;
2079         }
2080     }
2081     return true;
2082 }
2083 
passOne()2084 bool passOne() {
2085     initPassOne();
2086     bool success = gLanguageConfig->forEachIncludedDirectory(passOne_ProcessDir);
2087     finiPassOne();
2088 
2089     return success;
2090 }
2091 
2092 /// True if file doesn't begin with '.', and ends with either '.sc' or '.rtf'
isValidSourceFileName(const bfs::path & path)2093 bool isValidSourceFileName(const bfs::path& path) {
2094     const bfs::path& ext = path.extension();
2095     return path.filename().c_str()[0] != '.' && // must not be hidden file
2096         ((ext == ".sc") || (ext == ".rtf" && path.stem().extension() == ".sc"));
2097 }
2098 
2099 /** \brief Attempt to parse a single SuperCollider source file
2100  *
2101  * Parsing is aborted if the file doesn't have a valid source file name,
2102  * or if the file can't be opened.
2103  * (Sekhar's replacement)
2104  *
2105  * \returns Whether parsing was successful. The only failure condition occurs
2106  * when the file can't be opened.
2107  */
passOne_ProcessOneFile(const bfs::path & path)2108 bool passOne_ProcessOneFile(const bfs::path& path) {
2109     bool success = true;
2110 
2111     const std::string path_str = SC_Codecvt::path_to_utf8_str(path);
2112     const char* path_c_str = path_str.c_str();
2113     if (gLanguageConfig && gLanguageConfig->pathIsExcluded(path)) {
2114         post("\texcluding file: '%s'\n", path_c_str);
2115         return success;
2116     }
2117 
2118     if (isValidSourceFileName(path)) {
2119         gNumCompiledFiles++;
2120         PyrSymbol* fileSym = getsym(path_c_str);
2121         fileSym->u.source = nullptr;
2122         if (startLexer(fileSym, path, -1, -1, -1)) {
2123             while (parseOneClass(fileSym)) {
2124             };
2125             finiLexer();
2126         } else {
2127             error("file '%s' open failed\n", path_c_str);
2128             success = false;
2129         }
2130     } else {
2131         // wasn't a valid source file; ignore
2132     }
2133     return success;
2134 }
2135 
2136 void schedRun();
2137 
2138 void compileSucceeded();
compileSucceeded()2139 void compileSucceeded() {
2140     compiledOK = !(parseFailed || compileErrors);
2141     if (compiledOK) {
2142         compiledOK = true;
2143 
2144         compiledOK = initRuntime(gMainVMGlobals, 128 * 1024, pyr_pool_runtime);
2145 
2146         if (compiledOK) {
2147             VMGlobals* g = gMainVMGlobals;
2148 
2149             g->canCallOS = true;
2150 
2151             ++g->sp;
2152             SetObject(g->sp, g->process);
2153             runInterpreter(g, s_startup, 1);
2154             g->canCallOS = false;
2155 
2156             schedRun();
2157         }
2158         flushPostBuf();
2159     }
2160 }
2161 
runShutdown()2162 static void runShutdown() {
2163     // printf("->aboutToCompileLibrary\n");
2164     gLangMutex.lock();
2165     if (compiledOK) {
2166         VMGlobals* g = gMainVMGlobals;
2167 
2168         g->canCallOS = true;
2169 
2170         ++g->sp;
2171         SetObject(g->sp, g->process);
2172         runInterpreter(g, s_shutdown, 1);
2173 
2174         g->canCallOS = false;
2175     }
2176     gLangMutex.unlock();
2177     // printf("<-aboutToCompileLibrary\n");
2178 }
2179 
2180 void closeAllGUIScreens();
2181 void TempoClock_stopAll(void);
2182 void closeAllCustomPorts();
2183 
shutdownLibrary()2184 void shutdownLibrary() {
2185     closeAllGUIScreens();
2186 
2187     schedStop();
2188 
2189     runShutdown();
2190 
2191     TempoClock_stopAll();
2192 
2193     gLangMutex.lock();
2194     closeAllCustomPorts();
2195 
2196     if (compiledOK) {
2197         VMGlobals* g = gMainVMGlobals;
2198         g->canCallOS = true;
2199         g->gc->RunAllFinalizers();
2200         g->canCallOS = false;
2201     }
2202 
2203     pyr_pool_runtime->FreeAll();
2204 
2205     compiledOK = false;
2206 
2207     gLangMutex.unlock();
2208     deinitPrimitives();
2209 
2210     SC_LanguageConfig::freeLibraryConfig();
2211 }
2212 
compileLibrary(bool standalone)2213 SCLANG_DLLEXPORT_C bool compileLibrary(bool standalone) {
2214     // printf("->compileLibrary\n");
2215     shutdownLibrary();
2216 
2217     gLangMutex.lock();
2218     gNumCompiledFiles = 0;
2219     compiledOK = false;
2220 
2221     SC_LanguageConfig::readLibraryConfig(standalone);
2222 
2223     compileStartTime = elapsedTime();
2224 
2225     totalByteCodes = 0;
2226 
2227 #ifdef NDEBUG
2228     postfl("compiling class library...\n");
2229 #else
2230     postfl("compiling class library (debug build)...\n");
2231 #endif
2232 
2233     bool res = passOne();
2234     if (res) {
2235         if (!compileErrors) {
2236             buildDepTree();
2237             traverseFullDepTree();
2238             traverseFullDepTree2();
2239             flushPostBuf();
2240 
2241             if (!compileErrors && gShowWarnings) {
2242                 SymbolTable* symbolTable = gMainVMGlobals->symbolTable;
2243                 symbolTable->CheckSymbols();
2244             }
2245         }
2246         pyr_pool_compile->FreeAll();
2247         flushPostBuf();
2248         compileSucceeded();
2249     } else {
2250         compiledOK = false;
2251     }
2252 
2253     gLangMutex.unlock();
2254     // printf("<-compileLibrary\n");
2255     return compiledOK;
2256 }
2257 
2258 void signal_init_globs();
2259 
2260 void dumpByteCodes(PyrBlock* theBlock);
2261 
runLibrary(PyrSymbol * selector)2262 SCLANG_DLLEXPORT_C void runLibrary(PyrSymbol* selector) {
2263     VMGlobals* g = gMainVMGlobals;
2264     g->canCallOS = true;
2265     try {
2266         if (compiledOK) {
2267             ++g->sp;
2268             SetObject(g->sp, g->process);
2269             runInterpreter(g, selector, 1);
2270         } else {
2271             postfl("Library has not been compiled successfully.\n");
2272         }
2273     } catch (std::exception& ex) {
2274         PyrMethod* meth = g->method;
2275         if (meth) {
2276             int ip = slotRawInt8Array(&meth->code) ? g->ip - slotRawInt8Array(&meth->code)->b : -1;
2277             post("caught exception in runLibrary %s:%s %3d\n",
2278                  slotRawSymbol(&slotRawClass(&meth->ownerclass)->name)->name, slotRawSymbol(&meth->name)->name, ip);
2279             dumpByteCodes(meth);
2280         } else {
2281             post("caught exception in runLibrary\n");
2282         }
2283         error(ex.what());
2284     } catch (...) {
2285         postfl("DANGER: OUT of MEMORY. Operation failed.\n");
2286     }
2287     g->canCallOS = false;
2288 }
2289