moved almost all hardcoded constants to "define.dat"
[k8-i-v-a-n.git] / src / felib / feparse.cpp
blob555750aff6b097af61dd668e160a79a9a9c5a5ec
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
22 #include <cctype>
24 #include "feparse.h"
25 #include "femath.h"
28 ////////////////////////////////////////////////////////////////////////////////
29 struct InputFileSaved {
30 friend TextInput;
32 private:
33 TextInput *ifile;
34 void *svbuf;
35 int mCharBuf[4];
36 int mCharBufPos;
37 int mCurrentLine;
38 int mTokenLine;
39 sLong mRealPos;
41 private:
42 InputFileSaved (TextInput *aifile) : ifile(aifile), svbuf(nullptr) {
43 if (aifile) {
44 memcpy(mCharBuf, aifile->mCharBuf, sizeof(mCharBuf));
45 mCharBufPos = aifile->mCharBufPos;
46 mCurrentLine = aifile->mCurrentLine;
47 mTokenLine = aifile->mTokenLine;
48 mRealPos = aifile->realGetPos();
52 public:
53 ~InputFileSaved () {
54 if (ifile) {
55 memcpy(ifile->mCharBuf, mCharBuf, sizeof(ifile->mCharBuf));
56 ifile->mCharBufPos = mCharBufPos;
57 ifile->mCurrentLine = mCurrentLine;
58 ifile->mTokenLine = mTokenLine;
59 ifile->realSetPos(mRealPos);
65 ////////////////////////////////////////////////////////////////////////////////
66 TextInput::TextInput (const valuemap *aValueMap) {
67 setup(aValueMap);
71 TextInput::~TextInput () {
72 //fprintf(stderr, "TI:~this();\n");
73 Close();
74 //fprintf(stderr, "TI:~this(); -- exit\n");
78 void TextInput::setup (const valuemap *aValueMap) {
79 ValueMap = aValueMap;
80 lastWasNL = false;
81 lastWordWasString = false;
82 mCharBufPos = 0;
83 mCurrentLine = 1;
84 mTokenLine = 1;
85 mNumStr = "";
86 mCollectingNumStr = false;
90 void TextInput::Close () {
91 //fprintf(stderr, "TI:Close();\n");
92 lastWasNL = false;
93 while (!mIfStack.empty()) mIfStack.pop();
94 mCharBufPos = 0;
95 mCurrentLine = 0;
96 mTokenLine = 0;
97 mNumStr = "";
98 mCollectingNumStr = false;
99 //fprintf(stderr, "TI:Close(); -- exit\n");
103 int TextInput::GetChar () {
104 if (mCharBufPos > 0) {
105 return mCharBuf[--mCharBufPos];
106 } else {
107 if (lastWasNL) { ++mCurrentLine; lastWasNL = false; }
108 int ch = realGetChar();
109 if (ch == 0) ch = ' ';
110 lastWasNL = (ch == '\n');
111 return (ch < 0 ? EOF : ch);
116 void TextInput::UngetChar (int ch) {
117 if (ch >= 0) {
118 if (mCharBufPos > MaxUngetChars) die("too many unread chars");
119 mCharBuf[mCharBufPos++] = ch;
124 truth TextInput::Eof () {
125 if (mCharBufPos > 0) return false;
126 return isRealEof();
130 void TextInput::skipBlanks () {
131 for (;;) {
132 int ch = GetChar();
133 if (ch == EOF) return;
134 // comment?
135 if (ch == '/') {
136 if (Eof()) { UngetChar(ch); return; }
137 ch = GetChar();
138 // multiline comment? (possibly nested)
139 if (ch == '*') {
140 int prevch = ' ', level = 1;
141 for (;;) {
142 if (Eof()) return;
143 ch = GetChar();
144 if (prevch == '*' && ch == '/') {
145 if (--level == 0) break;
146 prevch = ' ';
147 } else if (prevch == '/' && ch == '*') {
148 ++level;
149 prevch = ' ';
150 } else {
151 prevch = ch;
154 continue;
156 // one-line comment?
157 if (ch == '/') {
158 while (!Eof()) { ch = GetChar(); if (ch == '\n') break; }
159 continue;
161 UngetChar('/');
162 return;
164 // space?
165 if (ch >= 0 && ch <= ' ') continue;
166 // not a space
167 UngetChar(ch);
168 return;
173 #define StackDepth (256)
174 int TextInput::countArrayItems (char echar) {
175 auto savedPos = InputFileSaved(this);
176 char stack[StackDepth]; // end chars
177 int sp = 0;
178 stack[0] = echar;
179 skipBlanks();
180 int ch = GetChar();
181 if (ch == EOF) return -1; // oops
182 if (ch == ',' || ch == ';') return -1;
183 //fprintf(stderr, "COUNT: ch='%c'\n", ch);
184 if (ch == echar) return 0;
185 UngetChar(ch);
186 int count = 1;
187 while (sp >= 0) {
188 skipBlanks();
189 ch = GetChar();
190 if (ch == EOF) return -1; // oops
191 // string?
192 if (ch == '"' || ch == '\'') {
193 echar = ch;
194 while (ch != EOF) {
195 ch = GetChar();
196 if (ch == '\\') {
197 ch = GetChar();
198 if (ch == EOF) return -1; // oops
199 } else if (ch == echar) {
200 break;
203 continue;
205 if (sp == 0 && (ch == ',' || ch == ';')) {
206 skipBlanks();
207 ch = GetChar();
208 if (ch == EOF) return -1;
209 if (ch == ',' || ch == ';') return -1;
210 //fprintf(stderr, " oldcount=%d; ch='%c'; sp=%d\n", count, ch, sp);
211 if (sp == 0 && ch == stack[0]) {} else ++count;
212 //fprintf(stderr, " newcount=%d; ch='%c'; sp=%d\n", count, ch, sp);
213 //if (ch != ')' && ch != ']' && ch != '}') ++count;
215 // endchar?
216 if (ch == stack[sp]) {
217 //fprintf(stderr, " *close; ch='%c'; sp=%d\n", ch, sp);
218 --sp;
219 continue;
221 // check for openings
222 switch (ch) {
223 case '(': echar = ')'; break;
224 case '[': echar = ']'; break;
225 case '{': echar = '}'; break;
226 case ')': case ']': case '}': return -1; // oops
227 default: echar = 0; break;
229 if (echar) {
230 if (sp >= StackDepth-1) return -1; // oops
231 //fprintf(stderr, " *open; ch='%c'; echar='%c'; sp=%d\n", ch, echar, sp);
232 stack[++sp] = echar;
235 return count;
237 #undef StackDepth
240 // ////////////////////////////////////////////////////////////////////////// //
241 festring TextInput::findVar (cfestring &name, truth *found) const {
242 VarMap::const_iterator i = mVars.find(name);
243 if (i != mVars.end()) {
244 if (found) *found = true;
245 return i->second;
247 if (found) *found = false;
248 return "";
252 festring TextInput::getVar (cfestring &name) {
253 truth found;
254 festring res = findVar(name, &found);
256 if (!found) {
257 if (mGetVar) {
258 res = mGetVar(this, name);
259 } else {
260 festring s = "unknown variable: "+name;
261 die(s);
264 return res;
268 void TextInput::setVar (cfestring &name, cfestring &value) {
269 mVars[name] = value;
273 //TODO: invoke callback
274 truth TextInput::delVar (cfestring &name) {
275 VarMap::iterator i = mVars.find(name);
276 if (i != mVars.end()) {
277 mVars.erase(i);
278 return true;
280 return false;
284 // ////////////////////////////////////////////////////////////////////////// //
285 void TextInput::die (cfestring &msg) {
286 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), mTokenLine, msg.CStr());
290 // ////////////////////////////////////////////////////////////////////////// //
291 // 0: term
292 // 1: unary
293 // 2: comparisons
294 // 3: &&
295 // 4: ||
296 static const int maxCPrio = 4;
297 static const char *opers[5][7] = {
298 {NULL},
299 {NULL},
300 {"<", ">", "<=", ">=", "==", "!=", NULL},
301 {"&&", NULL},
302 {"||", NULL}
305 festring TextInput::readCondition (festring &token, int prio, truth skipIt) {
306 festring res, op1, opc;
307 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
308 switch (prio) {
309 case 0: // term
310 if (token == "(") {
311 readWordIntr(token, true);
312 res = readCondition(token, maxCPrio, skipIt);
313 if (token != ")") die("')' expected");
314 } else if (token == "@") {
315 readWordIntr(token, true);
316 if (!skipIt) res = getVar(token);
317 } else {
318 res = token;
320 readWordIntr(token, true);
321 goto done;
322 //return res;
323 case 1:
324 if (token == "!") {
325 readWordIntr(token, true);
326 res = readCondition(token, 1, skipIt);
327 if (!skipIt) {
328 if (res == "") res = "tan"; else res = "";
330 } else {
331 res = readCondition(token, prio-1, skipIt);
333 goto done;
334 //return res;
337 if (prio > 4) return res;
338 res = readCondition(token, prio-1, skipIt);
339 for (;;) {
340 //readWordIntr(token, true);
341 bool myOp = false;
342 if (token == "=") die("no assignments yet!");
343 if (token == ";") {
344 //fprintf(stderr, " RET: [%s]\n", res.CStr());
345 break;
347 if (token == "less") token = "<";
348 else if (token == "great") token = ">";
349 else if (token == "equ") token = "==";
350 else if (token == "neq") token = "!=";
351 else if (token == "lessequ") token = "<=";
352 else if (token == "greatequ") token = ">=";
353 for (int f = 0; opers[prio][f]; f++) {
354 if (!strcmp(opers[prio][f], token.CStr())) { myOp = true; break; }
356 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
357 if (!myOp) break;
358 opc = token;
359 readWordIntr(token, true);
360 op1 = readCondition(token, prio-1, skipIt);
361 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
362 switch (prio) {
363 case 2: // comparisons
364 if (opc == "==") {
365 if (!skipIt) res = (res == op1 ? "tan" : "");
366 } else if (opc == "!=") {
367 if (!skipIt) res = (res != op1 ? "tan" : "");
368 } else if (opc == "<") {
369 if (!skipIt) res = (res < op1 ? "tan" : "");
370 } else if (opc == ">") {
371 if (!skipIt) res = (res > op1 ? "tan" : "");
372 } else if (opc == "<=") {
373 if (!skipIt) res = (res <= op1 ? "tan" : "");
374 } else if (opc == ">=") {
375 if (!skipIt) res = (res >= op1 ? "tan" : "");
377 break;
378 case 3: // &&
379 if (opc == "&&") {
380 if (!skipIt) {
381 res = (res != "" && op1 != "" ? "tan" : "");
382 if (res == "") skipIt = true;
385 break;
386 case 4: // ||
387 if (opc == "||") {
388 if (!skipIt) {
389 res = (res != "" || op1 != "" ? "tan" : "");
390 if (res != "") skipIt = true;
393 break;
394 default:
395 die("invalid priority");
398 done:
399 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
400 return res;
404 // stack top:
405 // 1: processing 'then'
406 // 2: processing 'else'
407 // -1: skiping 'then'
408 // -2: skiping 'else'
409 // -3: skiping whole 'if', 'then' part
410 // -4: skiping whole 'if', 'else' part
411 // -666: skiping '{}'
412 // 666: in '{}', processing
413 void TextInput::ReadWord (festring &str, truth AbortOnEOF, truth skipIt) {
414 int prc;
415 for (;;) {
416 if (!mIfStack.empty()) prc = mIfStack.top(); else prc = 0;
417 readWordIntr(str, AbortOnEOF);
418 if (str == "if") {
419 readWordIntr(str, true);
420 festring res = readCondition(str, maxCPrio, prc<0);
421 if (str != ";") die("';' expected");
422 if (prc < 0) {
423 // skiping
424 mIfStack.push(-3);
425 } else {
426 mIfStack.push(res!="" ? 1 : -1);
428 continue;
430 if (str == "else") {
431 switch (prc) {
432 case 1: // processing 'then'
433 mIfStack.pop();
434 mIfStack.push(-2);
435 break;
436 case -1: // skiping 'then'
437 mIfStack.pop();
438 mIfStack.push(2);
439 break;
440 case -3: // skiping whole, 'then'
441 mIfStack.pop();
442 mIfStack.push(-4);
443 break;
444 default: die("unexpected 'else'");
446 continue;
448 if (str == "endif") {
449 switch (prc) {
450 case 1: // processing 'then'
451 case 2: // processing 'else'
452 case -1: // skiping 'then'
453 case -2: // skiping 'else'
454 case -3: // skiping whole, 'then'
455 case -4: // skiping whole, 'else'
456 mIfStack.pop();
457 break;
458 default: die("unexpected 'endif'");
460 continue;
462 if (str == "{") {
463 mIfStack.push(prc>=0 ? 666 : -666);
464 if (prc >= 0) return;
465 continue;
467 if (str == "}") {
468 if (abs(prc) != 666) die("unexpected '}'");
469 mIfStack.pop();
470 if (prc >= 0) return;
471 continue;
473 if (prc >= 0) return;
478 festring TextInput::ReadWord (truth AbortOnEOF) {
479 festring ToReturn;
480 ReadWord(ToReturn, AbortOnEOF);
481 return ToReturn;
485 #define MODE_WORD (1)
486 #define MODE_NUMBER (2)
488 #define PUNCT_RETURN (0)
489 #define PUNCT_CONTINUE (1)
492 int TextInput::HandlePunct (festring &String, int Char, int Mode) {
493 mTokenLine = mCurrentLine;
494 // comment?
495 if (Char == '/') {
496 if (!Eof()) {
497 Char = GetChar();
498 // multiline comment? (possibly nested)
499 if (Char == '*') {
500 int OldChar = 0, CommentLevel = 1;
501 for (;;) {
502 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine);
503 Char = GetChar();
504 if (OldChar != '*' || Char != '/') {
505 if (OldChar != '/' || Char != '*') {
506 OldChar = Char;
507 } else {
508 ++CommentLevel;
509 OldChar = 0;
511 } else {
512 if (!--CommentLevel) break;
513 OldChar = 0;
516 return PUNCT_CONTINUE;
518 // one-line comment?
519 if (Char == '/') {
520 while (!Eof()) {
521 int ch = GetChar();
522 if (ch == '\n') break;
524 return PUNCT_CONTINUE;
526 UngetChar(Char);
527 realClearFlags();
529 if (Mode) UngetChar('/'); else String << '/';
530 return PUNCT_RETURN;
533 if (Mode) {
534 UngetChar(Char);
535 return PUNCT_RETURN;
537 // string?
538 if (Char == '"') {
539 lastWordWasString = true;
540 for (;;) {
541 if (Eof()) ABORT("Unterminated string in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine);
542 Char = GetChar();
543 if (Char == '\\') {
544 Char = GetChar();
545 if (Char == EOF) ABORT("Unterminated string in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine);
546 switch (Char) {
547 case 't': String << '\t'; break;
548 case 'n': String << '\n'; break;
549 case 'r': String << '\r'; break;
550 case '1': String << '\x01'; break;
551 case '2': String << '\x02'; break;
552 case '"': String << '"'; break;
553 default:
554 ABORT("Invalid escape in string in file %s at line %d!", GetFileName().CStr(), mTokenLine);
556 } else if (Char == '"') {
557 return PUNCT_RETURN;
558 } else {
559 String << char(Char);
562 if (Char != '"') {
563 String << char(Char);
564 OldChar = Char;
565 } else if (OldChar == '\\') {
566 String[String.GetSize()-1] = '"';
567 OldChar = 0;
568 } else return PUNCT_RETURN;
571 ABORT("The thing that should not be");
573 // delimiter
574 String << char(Char);
575 // two-char delimiters?
576 if (!Eof()) {
577 if (Char == '=' || Char == '<' || Char == '>' || Char == '!') {
578 Char = GetChar();
579 if (Char == '=') String << char(Char); else UngetChar(Char);
580 } else if (Char == '&' || Char == '|') {
581 int ch = GetChar();
582 if (Char == ch) String << char(ch); else UngetChar(ch);
583 } else if (Char == ':') {
584 Char = GetChar();
585 if (Char == '=') String << char(Char); else UngetChar(Char);
588 return PUNCT_RETURN;
592 void TextInput::readWordIntr (festring &String, truth AbortOnEOF) {
593 int Mode = 0;
594 String.Empty();
595 lastWordWasString = false;
596 mTokenLine = mCurrentLine;
597 for (int Char = GetChar(); !Eof(); Char = GetChar()) {
598 if (isalpha(Char) || Char == '_') {
599 if (!Mode) {
600 mTokenLine = mCurrentLine;
601 Mode = MODE_WORD;
602 } else if (Mode == MODE_NUMBER) {
603 if (Char == '_') continue;
604 UngetChar(Char);
605 return;
607 String << char(Char);
608 continue;
610 if (isdigit(Char)) {
611 if (!Mode) {
612 mTokenLine = mCurrentLine;
613 Mode = MODE_NUMBER;
614 } else if (Mode == MODE_WORD) {
615 UngetChar(Char);
616 return;
618 String << char(Char);
619 continue;
621 if ((Char == ' ' || Char == '\n' || Char == '\r' || Char == '\t') && Mode) return;
622 if (ispunct(Char) && HandlePunct(String, Char, Mode) == PUNCT_RETURN) return;
624 if (AbortOnEOF) ABORT("Unexpected end of file %s!", GetFileName().CStr());
625 if (Mode) realClearFlags();
629 char TextInput::ReadLetter (truth AbortOnEOF) {
630 mTokenLine = mCurrentLine;
631 for (int Char = GetChar(); !Eof(); mTokenLine = mCurrentLine, Char = GetChar()) {
632 if (isalpha(Char) || isdigit(Char)) return Char;
633 if (ispunct(Char)) {
634 // comment?
635 if (Char == '/') {
636 if (!Eof()) {
637 Char = GetChar();
638 // multiline comment? (possibly nested)
639 if (Char == '*') {
640 int OldChar = 0, CommentLevel = 1;
641 for (;;) {
642 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine);
643 Char = GetChar();
644 if (OldChar != '*' || Char != '/') {
645 if (OldChar != '/' || Char != '*') OldChar = Char;
646 else {
647 ++CommentLevel;
648 OldChar = 0;
650 } else {
651 if (!--CommentLevel) break;
652 OldChar = 0;
655 continue;
656 } else {
657 UngetChar(Char);
660 return '/';
662 return Char;
665 if (AbortOnEOF) ABORT("Unexpected end of file %s!", GetFileName().CStr());
666 return 0;
670 /* Reads a number or a formula from inputfile. Valid values could be for
671 instance "3", "5 * 4+5", "2+Variable%4" etc. */
672 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
673 festring TextInput::ReadNumberIntr (int CallLevel, sLong *num, truth *isString, truth allowStr, truth PreserveTerminator, truth *wasCloseBrc) {
674 sLong Value = 0;
675 festring Word, res;
676 truth NumberCorrect = false;
677 truth firstWord = true;
678 if (isString) *isString = false;
679 if (num) *num = 0;
680 if (wasCloseBrc) *wasCloseBrc = false;
681 mTokenLine = mCurrentLine;
682 //fprintf(stderr, ">>> ReadNumberIntr()\n");
683 for (;;) {
684 ReadWord(Word);
685 //fprintf(stderr, " ReadNumberIntr: word='%s'\n", Word.CStr());
686 // specials?
687 if (Word == "@") {
688 // variable
689 if (mCollectingNumStr) mNumStr << Word;
690 ReadWord(Word, true);
691 if (mCollectingNumStr) mNumStr << Word;
692 //fprintf(stderr, "var: [%s]\n", Word.CStr());
693 Word = getVar(Word);
694 //fprintf(stderr, " value: [%s]\n", Word.CStr());
695 const char *s = Word.CStr();
696 char *e;
697 sLong l = strtoll(s, &e, 10);
698 if (*e == '\0') {
699 //fprintf(stderr, " number: [%d]\n", l);
700 Value = l;
701 NumberCorrect = true;
702 continue;
704 if (firstWord && allowStr) {
705 if (isString) *isString = true;
706 return Word;
707 } else {
708 ABORT("Number expected in file %s, line %d!", GetFileName().CStr(), mTokenLine);
711 // first word?
712 if (firstWord) {
713 if (allowStr && lastWordWasString) {
714 if (isString) *isString = true;
715 ReadWord(res);
716 if (res.GetSize() == 1) {
717 if (res[0] != ';' && res[0] != ',' && res[0] != ':') {
718 ABORT("Invalid terminator in file %s, line %d!", GetFileName().CStr(), mTokenLine);
720 if (PreserveTerminator) UngetChar(res[0]);
721 } else {
722 ABORT("Terminator expected in file %s, line %d!", GetFileName().CStr(), mTokenLine);
724 return Word;
726 firstWord = false;
728 // other things
729 char First = Word[0];
730 // number?
731 if (isdigit(First)) {
732 if (mCollectingNumStr) mNumStr << Word;
733 Value = atoi(Word.CStr());
734 NumberCorrect = true;
735 // HACK: autoinsert terminator
736 skipBlanks();
737 int ch = GetChar();
738 if (ch != EOF) {
739 UngetChar(ch);
740 if (ch == '}') UngetChar(';');
742 continue;
744 // delimiter/math?
745 if (Word.GetSize() == 1) {
746 //fprintf(stderr, " ReadNumberIntr: First='%c'\n", First);
747 if (First == ';' || First == ',' || First == ':' || (wasCloseBrc && First == '}')) {
748 if (First == '}' && wasCloseBrc) *wasCloseBrc = true;
749 if (CallLevel != HIGHEST || PreserveTerminator) UngetChar(First);
750 if (num) *num = Value;
751 return res;
753 if (First == ')') {
754 if ((CallLevel != HIGHEST && CallLevel != 4) || PreserveTerminator) UngetChar(')');
755 if (num) *num = Value;
756 return res;
758 if (First == '~') {
759 if (mCollectingNumStr) mNumStr << Word;
760 Value = ~ReadNumber(4);
761 NumberCorrect = true;
762 continue;
764 /* Convert this into an inline function! */
765 #define CHECK_OP(op, cl) \
766 if (First == #op[0]) { \
767 if (cl < CallLevel) {\
768 if (mCollectingNumStr) mNumStr << Word; \
769 Value op##= ReadNumber(cl);\
770 NumberCorrect = true;\
771 continue;\
772 } else {\
773 UngetChar(#op[0]);\
774 if (num) *num = Value;\
775 return res;\
778 CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1);
779 CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2);
780 CHECK_OP(+, 3); CHECK_OP(-, 3);
781 #undef CHECK_OP
782 if (First == '<') {
783 char Next = GetChar();
784 if (Next == '<') {
785 if (1 < CallLevel) {
786 if (mCollectingNumStr) mNumStr << "<<";
787 Value <<= ReadNumber(1);
788 NumberCorrect = true;
789 continue;
790 } else {
791 UngetChar('<');
792 UngetChar('<');
793 if (num) *num = Value;
794 return res;
796 } else {
797 UngetChar(Next);
800 if (First == '>') {
801 char Next = GetChar();
802 if (Next == '>') {
803 if (1 < CallLevel) {
804 if (mCollectingNumStr) mNumStr << ">>";
805 Value >>= ReadNumber(1);
806 NumberCorrect = true;
807 continue;
808 } else {
809 UngetChar('>');
810 UngetChar('>');
811 if (num) *num = Value;
812 return res;
814 } else {
815 UngetChar(Next);
818 if (First == '(') {
819 if (NumberCorrect) {
820 UngetChar('(');
821 if (num) *num = Value;
822 return res;
823 } else {
824 if (mCollectingNumStr) mNumStr << Word;
825 Value = ReadNumber(4);
826 if (mCollectingNumStr) mNumStr << ")";
827 NumberCorrect = false;
828 continue;
831 if (First == '=' && CallLevel == HIGHEST) continue;
832 if (First == '#') {
833 // for #defines
834 UngetChar('#');
835 if (num) *num = Value;
836 return res;
840 if (Word == "enum" || Word == "bitenum") {
841 if (CallLevel != HIGHEST || PreserveTerminator) UngetChar(';');
842 if (num) *num = Value;
843 return res;
846 // rgbX?
847 if (Word == "rgb") {
848 if (mCollectingNumStr) mNumStr << Word;
849 int Bits = ReadNumber();
850 if (Bits == 16) {
851 int Red = ReadNumber();
852 int Green = ReadNumber();
853 int Blue = ReadNumber();
854 Value = MakeRGB16(Red, Green, Blue);
855 } else if (Bits == 24) {
856 int Red = ReadNumber();
857 int Green = ReadNumber();
858 int Blue = ReadNumber();
859 Value = MakeRGB24(Red, Green, Blue);
860 } else {
861 ABORT("Illegal RGB bit size %d in file %s, line %d!", Bits, GetFileName().CStr(), mTokenLine);
863 NumberCorrect = true;
864 continue;
866 // `true` literal?
867 if (Word == "true" || Word == "tan") {
868 if (mCollectingNumStr) mNumStr << Word;
869 Value = 1;
870 NumberCorrect = true;
871 continue;
873 // `false` literal?
874 if (Word == "false" || Word == "ona") {
875 if (mCollectingNumStr) mNumStr << Word;
876 Value = 0;
877 NumberCorrect = true;
878 continue;
880 // known value?
881 if (ValueMap) {
882 valuemap::const_iterator Iterator = ValueMap->find(Word);
883 if (Iterator != ValueMap->end()) {
884 if (mCollectingNumStr) mNumStr << Word;
885 Value = Iterator->second;
886 NumberCorrect = true;
887 continue;
890 // something bad
891 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!", Word.CStr(), GetFileName().CStr(), mTokenLine);
896 festring TextInput::ReadCode (truth AbortOnEOF) {
897 int sqLevel = 1;
898 char inString = 0;
899 festring res;
900 mTokenLine = mCurrentLine;
901 for (int Char = GetChar(); !Eof(); Char = GetChar()) {
902 //fprintf(stderr, "char: [%c]; inString: %d; sqLevel: %d\n", (Char < 32 || Char > 126 ? '?' : Char), inString, sqLevel);
903 if (inString) {
904 res << ((char)Char);
905 if (Char == inString) {
906 inString = 0;
907 } else if (Char == '\\') {
908 if (Eof()) break;
909 Char = GetChar();
910 res << ((char)Char);
912 } else {
913 if (Char == '[') {
914 ++sqLevel;
915 res << ((char)Char);
916 } else if (Char == ']') {
917 if (--sqLevel == 0) break;
918 res << ((char)Char);
919 } else if (Char == '/') {
920 if (Eof()) { res << ((char)Char); break; }
921 switch ((Char = GetChar())) {
922 case '/': // eol comment
923 while (!Eof()) if (GetChar() == '\n') break;
924 break;
925 case '*': // c-like comment
926 while (!Eof()) {
927 if (GetChar() == '*') {
928 if (Eof()) break;
929 if (GetChar() == '/') break;
932 break;
933 default:
934 res << '/';
935 res << ((char)Char);
936 break;
938 } else if (Char == '"' || Char == '\'') {
939 res << ((char)Char);
940 inString = ((char)Char);
941 } else {
942 res << ((char)Char);
946 if (AbortOnEOF && Eof()) ABORT("Unexpected end of file %s!", GetFileName().CStr());
947 return res;
951 sLong TextInput::ReadNumber (int CallLevel, truth PreserveTerminator, truth *wasCloseBrc) {
952 sLong num = 0;
953 truth isString = false;
954 ReadNumberIntr(CallLevel, &num, &isString, false, PreserveTerminator, wasCloseBrc);
955 return num;
959 festring TextInput::ReadStringOrNumber (sLong *num, truth *isString, truth PreserveTerminator, truth *wasCloseBrc) {
960 return ReadNumberIntr(HIGHEST, num, isString, true, PreserveTerminator, wasCloseBrc);
964 // ////////////////////////////////////////////////////////////////////////// //
965 // fuck you, shitplusplus!
966 struct FuckedShitForFuckedFinally {
967 truth *var;
968 truth oval;
969 FuckedShitForFuckedFinally (truth *avar, truth nval=true) {
970 var = avar;
971 oval = *var;
972 *var = nval;
974 ~FuckedShitForFuckedFinally () {
975 *var = oval;
980 sLong TextInput::ReadNumberKeepStr (int CallLevel, truth PreserveTerminator, truth *wasCloseBrc) {
981 auto fuck = FuckedShitForFuckedFinally(&mCollectingNumStr);
982 mNumStr = "";
983 return ReadNumber(CallLevel, PreserveTerminator, wasCloseBrc);
987 festring TextInput::ReadStringOrNumberKeepStr (sLong *num, truth *isString, truth PreserveTerminator, truth *wasCloseBrc) {
988 auto fuck = FuckedShitForFuckedFinally(&mCollectingNumStr);
989 mNumStr = "";
990 return ReadStringOrNumber(num, isString, PreserveTerminator, wasCloseBrc);
994 // ////////////////////////////////////////////////////////////////////////// //
995 v2 TextInput::ReadVector2d () {
996 skipBlanks();
997 int ch = GetChar();
998 if (ch == '{') ch = '}'; else { UngetChar(ch); ch = 0; }
1000 v2 Vector;
1001 Vector.X = ReadNumber();
1002 Vector.Y = ReadNumber();
1004 if (ch) {
1005 skipBlanks();
1006 if (GetChar() != ch) ABORT("Vector syntax error: \"%c\" expected in file %s, line %d!", ch, GetFileName().CStr(), TokenLine());
1007 skipBlanks();
1008 ch = GetChar();
1009 if (ch == '}') UngetChar(ch);
1010 else if (ch != ';' && ch != ',') ABORT("Vector syntax error: terminator expected in file %s, line %d!", GetFileName().CStr(), TokenLine());
1013 return Vector;
1017 rect TextInput::ReadRect () {
1018 skipBlanks();
1019 int ch = GetChar();
1020 if (ch == '{') ch = '}'; else { UngetChar(ch); ch = 0; }
1022 rect Rect;
1023 Rect.X1 = ReadNumber();
1024 Rect.Y1 = ReadNumber();
1025 Rect.X2 = ReadNumber();
1026 Rect.Y2 = ReadNumber();
1028 if (ch) {
1029 skipBlanks();
1030 if (GetChar() != ch) ABORT("Vector syntax error: \"%c\" expected in file %s, line %d!", ch, GetFileName().CStr(), TokenLine());
1031 skipBlanks();
1032 ch = GetChar();
1033 if (ch == '}') UngetChar(ch);
1034 else if (ch != ';' && ch != ',') ABORT("Vector syntax error: terminator expected in file %s, line %d!", GetFileName().CStr(), TokenLine());
1037 return Rect;
1041 // ////////////////////////////////////////////////////////////////////////// //
1042 void ReadData (festring &String, TextInput &SaveFile) {
1043 SaveFile.ReadWord(String);
1044 if (String == "=") SaveFile.ReadWord(String);
1045 SaveFile.ReadWord();
1049 void ReadData (fearray<sLong> &Array, TextInput &SaveFile) {
1050 Array.Clear();
1051 festring Word;
1052 SaveFile.ReadWord(Word);
1053 if (Word == "==") {
1054 Array.Allocate(1);
1055 Array.Data[0] = SaveFile.ReadNumber();
1056 } else if (Word == ":=") {
1057 SaveFile.ReadWord(Word);
1058 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1059 std::vector<sLong> v;
1060 for (;;) {
1061 truth wasCloseBrc = false;
1062 sLong n = SaveFile.ReadNumber(HIGHEST, false, &wasCloseBrc);
1063 if (wasCloseBrc) break;
1064 v.push_back(n);
1066 Array.Allocate(v.size());
1067 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
1068 } else if (Word == "=") {
1069 SaveFile.ReadWord(Word);
1070 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1071 fearray<sLong>::sizetype Size = SaveFile.ReadNumber();
1072 Array.Allocate(Size);
1073 for (fearray<sLong>::sizetype c = 0; c < Size; ++c) Array.Data[c] = SaveFile.ReadNumber();
1074 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1075 } else {
1076 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1081 void ReadData (fearray<festring> &Array, TextInput &SaveFile) {
1082 Array.Clear();
1083 festring Word;
1084 SaveFile.ReadWord(Word);
1085 if (Word == "==") {
1086 Array.Allocate(1);
1087 SaveFile.ReadWord(Array.Data[0]);
1088 if (SaveFile.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1089 } else if (Word == ":=") {
1090 SaveFile.ReadWord(Word);
1091 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1093 std::vector<festring> v;
1095 for (;;) {
1096 SaveFile.ReadWord(Word);
1097 if (Word == "}") break;
1098 v.push_back(Word);
1099 SaveFile.ReadWord(Word);
1100 if (Word == "}") break;
1101 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1103 Array.Allocate(v.size());
1104 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
1105 } else if (Word == "=") {
1106 SaveFile.ReadWord(Word);
1107 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1108 fearray<festring>::sizetype Size = SaveFile.ReadNumber();
1109 Array.Allocate(Size);
1110 for (fearray<festring>::sizetype c = 0; c < Size; ++c) {
1111 SaveFile.ReadWord(Array.Data[c]);
1112 SaveFile.ReadWord(Word);
1113 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1115 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1116 } else {
1117 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1122 // ////////////////////////////////////////////////////////////////////////// //
1123 TextInputFile::TextInputFile (cfestring &FileName, const valuemap *aValueMap, truth AbortOnErr) {
1124 ifile.Open(FileName, AbortOnErr);
1125 setup(aValueMap);
1129 TextInputFile::~TextInputFile () {
1130 //fprintf(stderr, "TIF:~this();\n");
1131 Close();
1132 //fprintf(stderr, "TIF:~this(); -- exit\n");
1136 int TextInputFile::realGetChar () { return ifile.Get(); }
1137 bool TextInputFile::isRealEof () { return ifile.Eof(); }
1138 bool TextInputFile::isRealOpen () { return ifile.IsOpen(); }
1139 void TextInputFile::Close () { /*fprintf(stderr, "TIF:Close();\n");*/ ifile.Close(); TextInput::Close(); }
1140 void TextInputFile::realClearFlags () { ifile.ClearFlags(); }
1142 sLong TextInputFile::realGetPos () { return (ifile.IsOpen() ? ifile.TellPos() : 0); }
1143 void TextInputFile::realSetPos (sLong apos) { if (ifile.IsOpen()) ifile.SeekPosBegin(apos); }
1147 // ////////////////////////////////////////////////////////////////////////// //
1148 MemTextFile::MemTextFile (cfestring &afname, cfestring &str, const valuemap *aValueMap) :
1149 buf(nullptr),
1150 bufSize(0),
1151 bufPos(0),
1152 tfname(afname)
1154 bufSize = str.GetSize();
1155 buf = (unsigned char *)calloc(1, bufSize+1);
1156 memmove(buf, str.CStr(), bufSize);
1157 setup(aValueMap);
1161 MemTextFile::~MemTextFile () {
1162 //fprintf(stderr, "MTF:~this();\n");
1163 Close();
1164 //fprintf(stderr, "MTF:~this(); -- exit\n");
1168 int MemTextFile::realGetChar () {
1169 if (bufPos >= bufSize) return EOF;
1170 return buf[bufPos++];
1174 bool MemTextFile::isRealEof () { return (bufPos >= bufSize); }
1175 bool MemTextFile::isRealOpen () { return (buf != nullptr); }
1178 void MemTextFile::Close () {
1179 //fprintf(stderr, "MTF:Close();\n");
1180 if (buf) {
1181 free(buf);
1182 buf = nullptr;
1183 tfname = "";
1184 bufSize = 0;
1185 bufPos = 0;
1187 TextInput::Close();
1190 void MemTextFile::realClearFlags () {}
1193 sLong MemTextFile::realGetPos () { return bufPos; }
1194 void MemTextFile::realSetPos (sLong apos) { if (buf != nullptr) bufPos = apos; }