3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
18 #include <sys/types.h>
28 ////////////////////////////////////////////////////////////////////////////////
29 struct InputFileSaved
{
33 TextInput
*ifile
= nullptr;
34 void *svbuf
= nullptr;
35 int mCharBuf
[4] = {0};
42 InputFileSaved (TextInput
*aifile
) : ifile(aifile
), svbuf(nullptr) {
44 memcpy(mCharBuf
, aifile
->mCharBuf
, sizeof(mCharBuf
));
45 mCharBufPos
= aifile
->mCharBufPos
;
46 mCurrentLine
= aifile
->mCurrentLine
;
47 mTokenLine
= aifile
->mTokenLine
;
48 mRealPos
= aifile
->realGetPos();
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
) {
71 TextInput::~TextInput () {
72 //fprintf(stderr, "TI:~this();\n");
74 //fprintf(stderr, "TI:~this(); -- exit\n");
78 void TextInput::setup (const valuemap
*aValueMap
) {
81 lastWordWasString
= false;
86 mCollectingNumStr
= false;
87 mAllowFloatNums
= false;
91 void TextInput::Close () {
92 //fprintf(stderr, "TI:Close();\n");
94 while (!mIfStack
.empty()) mIfStack
.pop();
99 mCollectingNumStr
= false;
100 mAllowFloatNums
= false;
101 //fprintf(stderr, "TI:Close(); -- exit\n");
105 int TextInput::GetChar () {
106 if (mCharBufPos
> 0) {
107 return mCharBuf
[--mCharBufPos
];
109 if (lastWasNL
) { ++mCurrentLine
; lastWasNL
= false; }
110 int ch
= realGetChar();
111 if (ch
== 0) ch
= ' ';
112 lastWasNL
= (ch
== '\n');
113 return (ch
< 0 ? EOF
: ch
);
118 void TextInput::UngetChar (int ch
) {
120 if (mCharBufPos
> MaxUngetChars
) die("too many unread chars");
121 mCharBuf
[mCharBufPos
++] = ch
;
126 truth
TextInput::Eof () {
127 if (mCharBufPos
> 0) return false;
132 // just read `ch`, skip possible comment; returns `ch` or -1
133 int TextInput::gotCharSkipComment (int ch
, truth allowSingleLineComments
) {
134 if (ch
< 0) ABORT("The thing that should not be");
135 if (ch
!= '/') return ch
;
137 if (ch
== EOF
) return '/';
138 // single-line comment?
139 if (allowSingleLineComments
&& ch
== '/') {
140 while (ch
!= EOF
&& ch
!= '\n') ch
= GetChar();
143 // multiline comment? (possibly nested)
144 if (ch
!= '*') { UngetChar(ch
); return '/'; }
145 int prevch
= 0, level
= 1;
148 if (ch
== EOF
) ABORT("Unterminated comment in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine
);
150 if (prevch
== '*' && ch
== '/') {
151 if (--level
== 0) return -1;
156 if (prevch
== '/' && ch
== '*') {
167 void TextInput::skipBlanks () {
170 if (ch
== EOF
) return;
171 if (ch
<= ' ') continue;
172 ch
= gotCharSkipComment(ch
);
173 if (ch
< 0) continue;
180 #define StackDepth (256)
181 int TextInput::countArrayItems (char echar
) {
182 auto savedPos
= InputFileSaved(this);
183 char stack
[StackDepth
]; // end chars
188 if (ch
== EOF
) return -1; // oops
189 if (ch
== ',' || ch
== ';') return -1;
190 //fprintf(stderr, "COUNT: ch='%c'\n", ch);
191 if (ch
== echar
) return 0;
197 if (ch
== EOF
) return -1; // oops
199 if (ch
== '"' || ch
== '\'') {
205 if (ch
== EOF
) return -1; // oops
206 } else if (ch
== echar
) {
212 if (sp
== 0 && (ch
== ',' || ch
== ';')) {
215 if (ch
== EOF
) return -1;
216 if (ch
== ',' || ch
== ';') return -1;
217 //fprintf(stderr, " oldcount=%d; ch='%c'; sp=%d\n", count, ch, sp);
218 if (sp
== 0 && ch
== stack
[0]) {} else ++count
;
219 //fprintf(stderr, " newcount=%d; ch='%c'; sp=%d\n", count, ch, sp);
220 //if (ch != ')' && ch != ']' && ch != '}') ++count;
223 if (ch
== stack
[sp
]) {
224 //fprintf(stderr, " *close; ch='%c'; sp=%d\n", ch, sp);
228 // check for openings
230 case '(': echar
= ')'; break;
231 case '[': echar
= ']'; break;
232 case '{': echar
= '}'; break;
233 case ')': case ']': case '}': return -1; // oops
234 default: echar
= 0; break;
237 if (sp
>= StackDepth
-1) return -1; // oops
238 //fprintf(stderr, " *open; ch='%c'; echar='%c'; sp=%d\n", ch, echar, sp);
247 // ////////////////////////////////////////////////////////////////////////// //
248 festring
TextInput::findVar (cfestring
&name
, truth
*found
) const {
249 VarMap::const_iterator i
= mVars
.find(name
);
250 if (i
!= mVars
.end()) {
251 if (found
) *found
= true;
254 if (found
) *found
= false;
259 festring
TextInput::getVar (cfestring
&name
) {
261 festring res
= findVar(name
, &found
);
264 res
= mGetVar(this, name
);
266 festring s
= "unknown variable: "+name
;
274 void TextInput::setVar (cfestring
&name
, cfestring
&value
) {
279 //TODO: invoke callback
280 truth
TextInput::delVar (cfestring
&name
) {
281 VarMap::iterator i
= mVars
.find(name
);
282 if (i
!= mVars
.end()) {
290 // ////////////////////////////////////////////////////////////////////////// //
291 void TextInput::die (cfestring
&msg
) {
292 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), mTokenLine
, msg
.CStr());
296 // ////////////////////////////////////////////////////////////////////////// //
302 static const int maxCPrio
= 4;
303 static const char *opers
[5][7] = {
306 {"<", ">", "<=", ">=", "==", "!=", NULL
},
311 festring
TextInput::readCondition (festring
&token
, int prio
, truth skipIt
) {
312 festring res
, op1
, opc
;
313 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
317 readWordIntr(token
, true);
318 res
= readCondition(token
, maxCPrio
, skipIt
);
319 if (token
!= ")") die("')' expected");
320 } else if (token
== "@") {
321 readWordIntr(token
, true);
322 if (!skipIt
) res
= getVar(token
);
326 readWordIntr(token
, true);
331 readWordIntr(token
, true);
332 res
= readCondition(token
, 1, skipIt
);
334 if (res
== "") res
= "tan"; else res
= "";
337 res
= readCondition(token
, prio
-1, skipIt
);
343 if (prio
> 4) return res
;
344 res
= readCondition(token
, prio
-1, skipIt
);
346 //readWordIntr(token, true);
348 if (token
== "=") die("no assignments yet!");
350 //fprintf(stderr, " RET: [%s]\n", res.CStr());
353 if (token
== "less") token
= "<";
354 else if (token
== "great") token
= ">";
355 else if (token
== "equ") token
= "==";
356 else if (token
== "neq") token
= "!=";
357 else if (token
== "lessequ") token
= "<=";
358 else if (token
== "greatequ") token
= ">=";
359 for (int f
= 0; opers
[prio
][f
]; f
++) {
360 if (!strcmp(opers
[prio
][f
], token
.CStr())) { myOp
= true; break; }
362 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
365 readWordIntr(token
, true);
366 op1
= readCondition(token
, prio
-1, skipIt
);
367 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
369 case 2: // comparisons
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" : "");
376 } else if (opc
== ">") {
377 if (!skipIt
) res
= (res
> op1
? "tan" : "");
378 } else if (opc
== "<=") {
379 if (!skipIt
) res
= (res
<= op1
? "tan" : "");
380 } else if (opc
== ">=") {
381 if (!skipIt
) res
= (res
>= op1
? "tan" : "");
387 res
= (res
!= "" && op1
!= "" ? "tan" : "");
388 if (res
== "") skipIt
= true;
395 res
= (res
!= "" || op1
!= "" ? "tan" : "");
396 if (res
!= "") skipIt
= true;
401 die("invalid priority");
405 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
411 // 1: processing 'then'
412 // 2: processing 'else'
413 // -1: skiping 'then'
414 // -2: skiping 'else'
415 // -3: skiping whole 'if', 'then' part
416 // -4: skiping whole 'if', 'else' part
417 // -666: skiping '{}'
418 // 666: in '{}', processing
419 truth
TextInput::ReadWord (festring
&str
, truth abortOnEOF
) {
421 int prc
= (mIfStack
.empty() ? 0 : mIfStack
.top());
422 if (!readWordIntr(str
, abortOnEOF
)) return false; // EOF
424 readWordIntr(str
, true);
425 festring res
= readCondition(str
, maxCPrio
, prc
<0);
426 if (str
!= ";") die("';' expected");
431 mIfStack
.push(res
.IsEmpty() ? -1 : 1);
437 case 1: // processing 'then'
441 case -1: // skiping 'then'
445 case -3: // skiping whole, 'then'
449 default: die("unexpected 'else'");
453 if (str
== "endif") {
455 case 1: // processing 'then'
456 case 2: // processing 'else'
457 case -1: // skiping 'then'
458 case -2: // skiping 'else'
459 case -3: // skiping whole, 'then'
460 case -4: // skiping whole, 'else'
463 default: die("unexpected 'endif'");
468 mIfStack
.push(prc
>= 0 ? 666 : -666);
469 if (prc
>= 0) return true;
473 if (abs(prc
) != 666) die("unexpected '}'");
475 if (prc
>= 0) return true;
478 if (prc
>= 0) return true;
483 festring
TextInput::ReadWord (truth abortOnEOF
) {
485 ReadWord(ToReturn
, abortOnEOF
);
490 truth
TextInput::readWordIntr (festring
&String
, truth abortOnEOF
) {
492 lastWordWasString
= false;
494 mTokenLine
= mCurrentLine
;
498 if (abortOnEOF
) ABORT("Unexpected end of file %s!", GetFileName().CStr());
502 if (isalpha(ch
) || ch
== '_') {
503 String
<< (char)(ch
);
506 if (ch
== EOF
) break;
507 if (ch
!= '_' && !isalpha(ch
) && !isdigit(ch
)) { UngetChar(ch
); break; }
508 String
<< (char)(ch
);
514 String
<< (char)(ch
);
515 bool wasdot
= !mAllowFloatNums
;
517 // allow hex literals
518 if (!mAllowFloatNums
&& ch
== '0') {
520 if (ch
== 'X' || ch
== 'x') {
529 if (ch
== EOF
) break;
530 if (ch
== '_') continue;
532 if (wasdot
) die("invalid number");
538 if (isxdigit(ch
)) { String
<< (char)(ch
); continue; }
540 if (isdigit(ch
)) { String
<< (char)(ch
); continue; }
542 if (isalpha(ch
)) die("invalid number");
550 lastWordWasString
= true;
553 if (ch
== EOF
) ABORT("Unterminated string in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine
);
554 if (ch
== '"') return true;
557 if (ch
== EOF
) ABORT("Unterminated string in file %s, beginning at line %d!", GetFileName().CStr(), mTokenLine
);
559 case 't': String
<< '\t'; break;
560 case 'n': String
<< '\n'; break;
561 case 'r': String
<< '\r'; break;
562 case '1': String
<< '\x01'; break;
563 case '2': String
<< '\x02'; break;
564 case '"': String
<< '"'; break;
565 default: ABORT("Invalid escape in string in file %s at line %d!", GetFileName().CStr(), mTokenLine
);
573 ch
= gotCharSkipComment(ch
);
574 if (ch
< 0) continue;
577 // two-char delimiters?
578 if (ch
== '=' || ch
== '<' || ch
== '>' || ch
== '!') {
580 if (ch
== '=') String
<< (char)ch
; else UngetChar(ch
);
581 } else if (ch
== '&' || ch
== '|') {
583 if (c1
== ch
) String
<< (char)c1
; else UngetChar(c1
);
584 } else if (ch
== ':') {
586 if (ch
== '=') String
<< (char)ch
; else UngetChar(ch
);
593 char TextInput::ReadLetter (truth abortOnEOF
) {
594 mTokenLine
= mCurrentLine
;
598 if (abortOnEOF
) ABORT("Unexpected end of file %s!", GetFileName().CStr());
601 if (ch
<= ' ') continue;
602 //ch = gotCharSkipComment(ch);
603 //if (ch >= 0) return ch;
609 /* Reads a number or a formula from inputfile. Valid values could be for
610 instance "3", "5 * 4+5", "2+Variable%4" etc. */
611 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
612 template<typename numtype
> festring
TextInput::ReadNumberIntr (int CallLevel
, numtype
*num
, truth
*isString
, truth allowStr
, truth PreserveTerminator
, truth
*wasCloseBrc
, truth allowFloats
) {
613 struct AllowFloatSaver
{
616 AllowFloatSaver (truth
*avar
, truth newval
) { oldallowfloat
= *avar
; var
= avar
; *avar
= newval
; }
617 ~AllowFloatSaver () { *var
= oldallowfloat
; }
621 truth NumberCorrect
= false;
622 truth firstWord
= true;
623 if (isString
) *isString
= false;
625 if (wasCloseBrc
) *wasCloseBrc
= false;
626 mTokenLine
= mCurrentLine
;
627 auto oldflt
= AllowFloatSaver(&mAllowFloatNums
, allowFloats
);
628 //fprintf(stderr, ">>> ReadNumberIntr()\n");
631 //fprintf(stderr, " ReadNumberIntr: word='%s'\n", Word.CStr());
635 if (mCollectingNumStr
) mNumStr
<< Word
;
636 ReadWord(Word
, true);
637 if (mCollectingNumStr
) mNumStr
<< Word
;
638 //fprintf(stderr, "var: [%s]\n", Word.CStr());
640 //fprintf(stderr, " value: [%s]\n", Word.CStr());
641 const char *s
= Word
.CStr();
643 sLong l
= strtoll(s
, &e
, 10);
645 //fprintf(stderr, " number: [%d]\n", l);
647 NumberCorrect
= true;
650 if (firstWord
&& allowStr
) {
651 if (isString
) *isString
= true;
654 ABORT("Number expected in file %s, line %d!", GetFileName().CStr(), mTokenLine
);
659 if (allowStr
&& lastWordWasString
) {
660 if (isString
) *isString
= true;
662 if (res
.GetSize() == 1) {
663 if (res
[0] != ';' && res
[0] != ',' && res
[0] != ':') {
664 ABORT("Invalid terminator in file %s, line %d!", GetFileName().CStr(), mTokenLine
);
666 if (PreserveTerminator
) UngetChar(res
[0]);
668 ABORT("Terminator expected in file %s, line %d!", GetFileName().CStr(), mTokenLine
);
675 char First
= Word
[0];
677 if (isdigit(First
)) {
678 if (mCollectingNumStr
) mNumStr
<< Word
;
681 Value
= (numtype
)strtod(Word
.CStr(), &e
);
683 Value
= (numtype
)strtol(Word
.CStr(), &e
, 0);
685 if (*e
!= '\0') ABORT("Invalid number '%s' in file %s, line %d!", Word
.CStr(), GetFileName().CStr(), mTokenLine
);
686 NumberCorrect
= true;
687 // HACK: autoinsert terminator
692 if (ch
== '}') UngetChar(';');
697 if (Word
.GetSize() == 1) {
698 //fprintf(stderr, " ReadNumberIntr: First='%c'\n", First);
699 if (First
== ';' || First
== ',' || First
== ':' || (wasCloseBrc
&& First
== '}')) {
700 if (First
== '}' && wasCloseBrc
) *wasCloseBrc
= true;
701 if (CallLevel
!= HIGHEST
|| PreserveTerminator
) UngetChar(First
);
702 if (num
) *num
= Value
;
706 if ((CallLevel
!= HIGHEST
&& CallLevel
!= 4) || PreserveTerminator
) UngetChar(')');
707 if (num
) *num
= Value
;
711 if (mCollectingNumStr
) mNumStr
<< Word
;
712 Value
= ~ReadNumber(4);
713 NumberCorrect
= true;
716 /* Convert this into an inline function! */
717 #define CHECK_OP(op, cl, opertype) \
718 if (First == #op[0]) { \
719 if (cl < CallLevel) {\
720 if (mCollectingNumStr) mNumStr << Word; \
721 /*Value op##= ReadNumber(cl);*/\
723 ReadNumberIntr<numtype>(cl, &rhs, nullptr, false, false, nullptr, allowFloats);\
724 /*Value op##= rhs;*/\
725 Value = (numtype)((opertype)Value op (opertype)rhs);\
726 NumberCorrect = true;\
730 if (num) *num = Value;\
737 CHECK_OP(*, 2, numtype
);
738 CHECK_OP(/, 2, numtype
);
740 CHECK_OP(+, 3, numtype
);
741 CHECK_OP(-, 3, numtype
);
744 char Next
= GetChar();
747 if (mCollectingNumStr
) mNumStr
<< "<<";
748 //Value <<= ReadNumber(1);
749 Value
= (numtype
)((int)Value
<<(int)ReadNumber(1));
750 NumberCorrect
= true;
755 if (num
) *num
= Value
;
763 char Next
= GetChar();
766 if (mCollectingNumStr
) mNumStr
<< ">>";
767 //Value >>= ReadNumber(1);
768 Value
= (numtype
)((int)Value
>>(int)ReadNumber(1));
769 NumberCorrect
= true;
774 if (num
) *num
= Value
;
784 if (num
) *num
= Value
;
787 if (mCollectingNumStr
) mNumStr
<< Word
;
788 Value
= ReadNumber(4);
789 if (mCollectingNumStr
) mNumStr
<< ")";
790 NumberCorrect
= false;
794 if (First
== '=' && CallLevel
== HIGHEST
) continue;
798 if (num
) *num
= Value
;
803 if (Word == "enum" || Word == "bitenum") {
804 if (CallLevel != HIGHEST || PreserveTerminator) UngetChar(';');
805 if (num) *num = Value;
810 if (Word
== "rgb16" || Word
== "rgb24") {
811 truth is16
= (Word
== "rgb16");
812 if (mCollectingNumStr
) mNumStr
<< Word
;
813 int Red
= ReadNumber();
814 if (Red
< 0 || Red
> 255) ABORT("Illegal Red value (%d) file %s, line %d!", Red
, GetFileName().CStr(), mTokenLine
);
815 int Green
= ReadNumber();
816 if (Green
< 0 || Green
> 255) ABORT("Illegal Green value (%d) file %s, line %d!", Green
, GetFileName().CStr(), mTokenLine
);
817 int Blue
= ReadNumber();
818 if (Blue
< 0 || Blue
> 255) ABORT("Illegal Blue value (%d) file %s, line %d!", Blue
, GetFileName().CStr(), mTokenLine
);
819 Value
= (is16
? MakeRGB16(Red
, Green
, Blue
) : MakeRGB24(Red
, Green
, Blue
));
820 NumberCorrect
= true;
824 if (Word
== "true" || Word
== "tan") {
825 if (mCollectingNumStr
) mNumStr
<< Word
;
827 NumberCorrect
= true;
831 if (Word
== "false" || Word
== "ona") {
832 if (mCollectingNumStr
) mNumStr
<< Word
;
834 NumberCorrect
= true;
839 valuemap::const_iterator Iterator
= ValueMap
->find(Word
);
840 if (Iterator
!= ValueMap
->end()) {
841 if (mCollectingNumStr
) mNumStr
<< Word
;
842 Value
= Iterator
->second
;
843 NumberCorrect
= true;
848 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!", Word
.CStr(), GetFileName().CStr(), mTokenLine
);
853 sLong
TextInput::ReadNumber (int CallLevel
, truth PreserveTerminator
, truth
*wasCloseBrc
) {
855 ReadNumberIntr
<sLong
>(CallLevel
, &num
, nullptr, false, PreserveTerminator
, wasCloseBrc
, false);
860 festring
TextInput::ReadStringOrNumber (sLong
*num
, truth
*isString
, truth PreserveTerminator
, truth
*wasCloseBrc
) {
861 return ReadNumberIntr
<sLong
>(HIGHEST
, num
, isString
, true, PreserveTerminator
, wasCloseBrc
, false);
865 float TextInput::ReadFloat () {
867 ReadNumberIntr
<float>(HIGHEST
, &num
, nullptr, false, false, nullptr, true);
872 // ////////////////////////////////////////////////////////////////////////// //
873 // fuck you, shitplusplus!
874 struct FuckedShitForFuckedFinally
{
877 FuckedShitForFuckedFinally (truth
*avar
, truth nval
=true) {
882 ~FuckedShitForFuckedFinally () {
888 sLong
TextInput::ReadNumberKeepStr (int CallLevel
, truth PreserveTerminator
, truth
*wasCloseBrc
) {
889 auto fuck
= FuckedShitForFuckedFinally(&mCollectingNumStr
);
891 return ReadNumber(CallLevel
, PreserveTerminator
, wasCloseBrc
);
895 festring
TextInput::ReadStringOrNumberKeepStr (sLong
*num
, truth
*isString
, truth PreserveTerminator
, truth
*wasCloseBrc
) {
896 auto fuck
= FuckedShitForFuckedFinally(&mCollectingNumStr
);
898 return ReadStringOrNumber(num
, isString
, PreserveTerminator
, wasCloseBrc
);
902 // ////////////////////////////////////////////////////////////////////////// //
903 v2
TextInput::ReadVector2d () {
906 if (ch
== '{') ch
= '}'; else { UngetChar(ch
); ch
= 0; }
909 Vector
.X
= ReadNumber();
910 Vector
.Y
= ReadNumber();
914 if (GetChar() != ch
) ABORT("Vector syntax error: \"%c\" expected in file %s, line %d!", ch
, GetFileName().CStr(), TokenLine());
917 if (ch
== '}') UngetChar(ch
);
918 else if (ch
!= ';' && ch
!= ',') ABORT("Vector syntax error: terminator expected in file %s, line %d!", GetFileName().CStr(), TokenLine());
925 rect
TextInput::ReadRect () {
928 if (ch
== '{') ch
= '}'; else { UngetChar(ch
); ch
= 0; }
931 Rect
.X1
= ReadNumber();
932 Rect
.Y1
= ReadNumber();
933 Rect
.X2
= ReadNumber();
934 Rect
.Y2
= ReadNumber();
938 if (GetChar() != ch
) ABORT("Vector syntax error: \"%c\" expected in file %s, line %d!", ch
, GetFileName().CStr(), TokenLine());
941 if (ch
== '}') UngetChar(ch
);
942 else if (ch
!= ';' && ch
!= ',') ABORT("Vector syntax error: terminator expected in file %s, line %d!", GetFileName().CStr(), TokenLine());
949 // ////////////////////////////////////////////////////////////////////////// //
950 #define GETC(var) do { \
951 var = inFile->GetChar(); \
953 if (infStack.empty()) ABORT("'}' missing in datafile %s line %d!", inFile->GetFileName().CStr(), inFile->TokenLine()); \
955 *iff = inFile = infStack.top(); \
963 festring collectSourceCode (std::stack<TextInput *> &infStack, TextInput **iff) {
964 if (!iff
|| !*iff
) ABORT("Wut?!");
965 TextInput
*inFile
= *iff
;
974 if (ch
== '{') { ++brclevel
; continue; }
975 if (ch
== '}') { if (--brclevel
== 0) break; continue; }
978 // waiting for bracket?
979 if (brclevel
== 0) ABORT("'{' expected in datafile %s line %d!", inFile
->GetFileName().CStr(), inFile
->TokenLine()); \
983 if (ch
== '"') break;
1000 if (ch
== '\n') break;
1011 // comment start (nested)
1012 if (prevch
== '/' && ch
== '*') {
1018 if (prevch
== '*' && ch
== '/') {
1019 if (--level
== 0) break;
1027 // waiting for bracket?
1028 if (brclevel
== 0) ABORT("'{' expected in datafile %s line %d!", inFile
->GetFileName().CStr(), inFile
->TokenLine()); \
1031 if (brclevel
== 0 && ch
> ' ') ABORT("'{' expected in datafile %s line %d!", inFile
->GetFileName().CStr(), inFile
->TokenLine()); \
1040 EventHandlerSource
collectEventHandlerSource (std::stack
<TextInput
*> &infStack
, TextInput
**iff
) {
1041 if (!iff
|| !*iff
) ABORT("Wut?!");
1042 TextInput
*inFile
= *iff
;
1043 EventHandlerSource res
;
1045 res
.fname
= inFile
->GetFileName();
1046 res
.type
= inFile
->ReadWord();
1047 res
.startline
= inFile
->CurrentLine();
1048 res
.text
= collectSourceCode(infStack
, iff
);
1053 // ////////////////////////////////////////////////////////////////////////// //
1054 EventHandlerMap::EventHandlerMap () {
1058 EventHandlerMap::~EventHandlerMap () {
1063 void EventHandlerMap::clear () {
1068 // "on" skipped, expecting type
1069 void EventHandlerMap::collectSource (std::stack
<TextInput
*> &infStack
, TextInput
**iff
) {
1070 auto ehs
= collectEventHandlerSource(infStack
, iff
);
1071 if (ehs
.type
.IsEmpty()) ABORT("Empty handler type in file '%s' at line %d", ehs
.fname
.CStr(), ehs
.startline
);
1072 auto it
= mMap
.find(ehs
.type
);
1073 if (it
!= mMap
.end()) {
1074 //ABORT("Duplicate handler type '%s' in file '%s' at line %d", ehs.type.CStr(), ehs.fname.CStr(), ehs.startline);
1077 mMap
.insert(std::make_pair(ehs
.type
, ehs
));
1081 // "on" skipped, expecting type
1082 void EventHandlerMap::collectSource (TextInput
&iff
) {
1083 std::stack
<TextInput
*> infStack
;
1084 TextInput
*ifp
= &iff
;
1086 collectSource(infStack
, &ifp
);
1090 // can return `nullptr`
1091 TextInput
*EventHandlerMap::openHandler (cfestring
&atype
, const valuemap
*aValueMap
) const {
1092 auto it
= mMap
.find(atype
);
1093 if (it
== mMap
.end()) return nullptr;
1094 return new MemTextInputFile((*it
).second
.fname
, (*it
).second
.startline
, (*it
).second
.text
, aValueMap
);
1098 // ////////////////////////////////////////////////////////////////////////// //
1099 void ReadData (festring
&String
, TextInput
&SaveFile
) {
1100 SaveFile
.ReadWord(String
);
1101 if (String
== "=") SaveFile
.ReadWord(String
);
1102 SaveFile
.ReadWord();
1106 void ReadData (fearray
<sLong
> &Array
, TextInput
&SaveFile
) {
1109 SaveFile
.ReadWord(Word
);
1112 Array
.Data
[0] = SaveFile
.ReadNumber();
1113 } else if (Word
== ":=") {
1114 SaveFile
.ReadWord(Word
);
1115 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1116 std::vector
<sLong
> v
;
1118 truth wasCloseBrc
= false;
1119 sLong n
= SaveFile
.ReadNumber(HIGHEST
, false, &wasCloseBrc
);
1120 if (wasCloseBrc
) break;
1123 Array
.Allocate(v
.size());
1124 for (unsigned int f
= 0; f
< v
.size(); ++f
) Array
.Data
[f
] = v
[f
];
1125 } else if (Word
== "=") {
1126 SaveFile
.ReadWord(Word
);
1127 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1128 fearray
<sLong
>::sizetype Size
= SaveFile
.ReadNumber();
1129 Array
.Allocate(Size
);
1130 for (fearray
<sLong
>::sizetype c
= 0; c
< Size
; ++c
) Array
.Data
[c
] = SaveFile
.ReadNumber();
1131 if (SaveFile
.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1133 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1138 void ReadData (fearray
<festring
> &Array
, TextInput
&SaveFile
) {
1141 SaveFile
.ReadWord(Word
);
1144 SaveFile
.ReadWord(Array
.Data
[0]);
1145 if (SaveFile
.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1146 } else if (Word
== ":=") {
1147 SaveFile
.ReadWord(Word
);
1148 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1150 std::vector
<festring
> v
;
1153 SaveFile
.ReadWord(Word
);
1154 if (Word
== "}") break;
1156 SaveFile
.ReadWord(Word
);
1157 if (Word
== "}") break;
1158 if (Word
!= "," && Word
!= ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1160 Array
.Allocate(v
.size());
1161 for (unsigned int f
= 0; f
< v
.size(); ++f
) Array
.Data
[f
] = v
[f
];
1162 } else if (Word
== "=") {
1163 SaveFile
.ReadWord(Word
);
1164 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1165 fearray
<festring
>::sizetype Size
= SaveFile
.ReadNumber();
1166 Array
.Allocate(Size
);
1167 for (fearray
<festring
>::sizetype c
= 0; c
< Size
; ++c
) {
1168 SaveFile
.ReadWord(Array
.Data
[c
]);
1169 SaveFile
.ReadWord(Word
);
1170 if (Word
!= "," && Word
!= ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1172 if (SaveFile
.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1174 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile
.GetFileName().CStr(), SaveFile
.TokenLine());
1179 void ReadData (RandomChance
&rc
, TextInput
&fl
) {
1183 auto tkline
= fl
.TokenLine();
1185 rc
.rnd
= fl
.ReadNumber();
1186 } else if (w
== "=" || w
== ":=") {
1187 if (fl
.ReadWord() != "{") ABORT("RandomChance syntax error: '{' expected in file %s, line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1190 if (w
== "}") break;
1191 sLong
*fptr
= nullptr;
1192 if (w
.CompareIgnoreCase("add") == 0) fptr
= &rc
.add
;
1193 else if (w
.CompareIgnoreCase("rnd") == 0) fptr
= &rc
.rnd
;
1194 else if (w
.CompareIgnoreCase("rand") == 0) fptr
= &rc
.rnd
;
1195 else if (w
.CompareIgnoreCase("rmin") == 0) fptr
= &rc
.rmin
;
1196 else if (w
.CompareIgnoreCase("rmax") == 0) fptr
= &rc
.rmax
;
1197 if (!fptr
) ABORT("RandomChance syntax error: unknown field '%s' in file %s, line %d!", w
.CStr(), fl
.GetFileName().CStr(), fl
.TokenLine());
1199 if (w
!= ":" && w
!= "=") ABORT("RandomChance syntax error: ':' expected in file %s, line %d!", fl
.GetFileName().CStr(), fl
.TokenLine());
1200 *fptr
= fl
.ReadNumber();
1203 ABORT("RandomChance syntax error: '=' or '==' expected in file %s, line %d!", fl
.GetFileName().CStr(), tkline
);
1205 if (rc
.rnd
< 0) ABORT("Invalid random chance in file %s, line %d!", fl
.GetFileName().CStr(), tkline
);
1209 // ////////////////////////////////////////////////////////////////////////// //
1210 TextInputFile::TextInputFile (cfestring
&FileName
, const valuemap
*aValueMap
, truth AbortOnErr
) {
1211 ifile
.Open(FileName
, AbortOnErr
);
1213 //fprintf(stderr, "TextInputFile::TextInputFile: <%s> <%s>\n", FileName.CStr(), ifile.GetFileName().CStr());
1217 TextInputFile::~TextInputFile () {
1218 //fprintf(stderr, "TIF:~this();\n");
1220 //fprintf(stderr, "TIF:~this(); -- exit\n");
1224 cfestring
&TextInputFile::GetFileName () const { return ifile
.GetFileName(); }
1225 int TextInputFile::realGetChar () { return ifile
.Get(); }
1226 truth
TextInputFile::isRealEof () { return ifile
.Eof(); }
1227 truth
TextInputFile::IsOpen () { return ifile
.IsOpen(); }
1228 void TextInputFile::Close () { /*fprintf(stderr, "TIF:Close();\n");*/ ifile
.Close(); TextInput::Close(); }
1230 sLong
TextInputFile::realGetPos () { return (ifile
.IsOpen() ? ifile
.TellPos() : 0); }
1231 void TextInputFile::realSetPos (sLong apos
) { if (ifile
.IsOpen()) ifile
.SeekPosBegin(apos
); }
1235 // ////////////////////////////////////////////////////////////////////////// //
1236 MemTextInputFile::MemTextInputFile (cfestring
&afname
, cfestring
&str
, const valuemap
*aValueMap
) :
1242 bufSize
= str
.GetSize();
1243 buf
= (unsigned char *)calloc(1, bufSize
+1);
1244 memmove(buf
, str
.CStr(), bufSize
);
1249 MemTextInputFile::MemTextInputFile (cfestring
&afname
, int stline
, cfestring
&str
, const valuemap
*aValueMap
) :
1255 bufSize
= str
.GetSize();
1256 buf
= (unsigned char *)calloc(1, bufSize
+1);
1257 memmove(buf
, str
.CStr(), bufSize
);
1259 mCurrentLine
= mTokenLine
= stline
;
1263 MemTextInputFile::~MemTextInputFile () {
1264 //fprintf(stderr, "MTF:~this();\n");
1266 //fprintf(stderr, "MTF:~this(); -- exit\n");
1270 int MemTextInputFile::realGetChar () {
1271 if (bufPos
>= bufSize
) return EOF
;
1272 return buf
[bufPos
++];
1276 cfestring
&MemTextInputFile::GetFileName () const { return tfname
; }
1277 truth
MemTextInputFile::isRealEof () { return (bufPos
>= bufSize
); }
1278 truth
MemTextInputFile::IsOpen () { return (buf
!= nullptr); }
1281 void MemTextInputFile::Close () {
1282 //fprintf(stderr, "MTF:Close();\n");
1293 sLong
MemTextInputFile::realGetPos () { return bufPos
; }
1294 void MemTextInputFile::realSetPos (sLong apos
) { if (buf
!= nullptr) bufPos
= apos
; }