1%{
2
3// levcomp.lpp:
4//      Level compiler lexer for Dungeon Crawl Stone Soup.
5//
6// Based loosely on NetHack's lev_comp.l
7
8#include "AppHdr.h"
9#include <algorithm>
10#include <cstring>
11#include <queue>
12#include "end.h"
13#include "mapdef.h"
14#include "levcomp.tab.h"
15
16static bool alloced = false;
17
18static queue<const char *> free_queue;
19
20static void flush_free_queue(unsigned int max_allowed)
21{
22    while (free_queue.size() > max_allowed)
23    {
24        const char *s = free_queue.front();
25        free((void *) s);
26        free_queue.pop();
27    }
28}
29
30static void add_to_queue(const char *s)
31{
32    free_queue.push(s);
33    flush_free_queue(100);
34}
35
36static void clean()
37{
38    if (yylval.text && alloced)
39        add_to_queue(yylval.text);
40    yylval.text = nullptr;
41    alloced = false;
42}
43
44// Enter a new state, first clearing yylval of junk.
45#define CBEGIN(x) do { BEGIN(x); clean(); } while (0)
46
47static void post_proc_text(char *text, bool trim_right,
48                           int strip_trailing, int strip_leading)
49{
50    char *s = nullptr;
51    if (trim_right)
52    {
53        s = text + strlen(text) - 1;
54        while (s >= text && isspace(*s))
55            *s-- = 0;
56    }
57    if (strip_trailing)
58    {
59        if (!s)
60            s = text + strlen(text) - 1;
61        while (s >= text && --strip_trailing >= 0)
62            *s-- = 0;
63    }
64    if (strip_leading)
65    {
66        const int original_length = strlen(text);
67        if (original_length > strip_leading)
68            memmove(text, text + strip_leading,
69                    original_length - strip_leading);
70        if (original_length >= strip_leading)
71            text[original_length - strip_leading] = 0;
72    }
73}
74
75static char *copy_text(bool trim_right, int strip_trailing, int strip_leading)
76{
77    char *newstring = nullptr;
78    if ((yylval.text = newstring = strdup(yytext)))
79    {
80        alloced = true;
81        post_proc_text(newstring, trim_right, strip_trailing, strip_leading);
82    }
83    return (newstring);
84}
85
86static void settext(bool trim_right = false,
87                    int strip_trailing = 0,
88                    int strip_leading = 0)
89{
90    clean();
91    char *newstring = copy_text(trim_right, strip_trailing, strip_leading);
92    yylval.text = newstring;
93}
94
95static void str_check()
96{
97    if (!yylval.text)
98    {
99        char *buf = (char *) malloc(1);
100        if (buf)
101        {
102            yylval.text = buf;
103            *buf = 0;
104            alloced = true;
105        }
106    }
107}
108
109static void cattext(bool trim_right = false, int strip_trailing = 0)
110{
111    if (!yylval.text)
112        settext(trim_right, strip_trailing);
113    else
114    {
115        bool was_alloced = alloced;
116        char *newbuf = (char*) malloc(strlen(yylval.text) + strlen(yytext) + 1);
117        if (!newbuf)
118            end(1, false, "Out of memory");
119        alloced = true;
120        strcpy(newbuf, yylval.text);
121        strcat(newbuf, yytext);
122        post_proc_text(newbuf, trim_right, strip_trailing, 0);
123        if (was_alloced)
124            free((void*) yylval.text);
125        yylval.text = newbuf;
126    }
127}
128
129%}
130
131%x MAPDEF
132%x LUA
133%x LUA_ONELINER
134%s ARGUMENT
135%s MNAME
136%s KEYWORDS
137%x ITEM_LIST
138
139%option yylineno
140%option never-interactive
141
142NSPACE [^\ \t\r\n]
143SPACE [\ \t\r]
144
145%%
146
147<MAPDEF>^\s*ENDMAP[ ]*  { BEGIN(INITIAL); }
148
149<MAPDEF>^[^\r\n\t]+ {
150                        settext(true);
151                        return MAP_LINE;
152                    }
153
154<MAPDEF>^[ ]*\r?\n      return CHARACTER;
155<MAPDEF>#               return CHARACTER;
156<MAPDEF>[\ ][^\ \r\n]   return CHARACTER;
157
158<MAPDEF>[ ]*\r?\n       ;
159
160<LUA>\s*\}\}[ \t\r]*$    { BEGIN(INITIAL); }
161<LUA>[^\r\n]+\}\}[ \t\r]*$ {
162                            settext(true, 2);
163                            BEGIN(INITIAL);
164                            return LUA_LINE;
165                        }
166<LUA>[^\r\n]+           {
167                            settext(true);
168                            return LUA_LINE;
169                        }
170<LUA>\r?\n              ;
171
172<LUA_ONELINER>[^\r\n]+\r?$ {
173                            settext(true);
174                            return LUA_LINE;
175                        }
176<LUA_ONELINER>\r?\n     { BEGIN(INITIAL); }
177
178<KEYWORDS>[A-Za-z_0-9\-]+   {
179                        settext();
180                        return STRING;
181                    }
182
183<KEYWORDS>{SPACE}*\\{SPACE}*\n   ;
184<KEYWORDS>[ \t]+    ;
185<KEYWORDS>[ \t]*\r?\n  { BEGIN(INITIAL); }
186
187<ITEM_LIST>[^,\ \t\r\n][^,\r\n]*\\{SPACE}*\n {
188                        cattext(true, 1);
189                    }
190
191<ITEM_LIST>{SPACE}*\\{SPACE}*\n   ;
192
193<ITEM_LIST>[^, \t\r\n][^,\r\n]*[^, \t\r\n] {
194                        cattext();
195                        return ITEM_INFO;
196                    }
197
198<ITEM_LIST>,            { clean(); return COMMA; }
199<ITEM_LIST>[ \t]+       ;
200<ITEM_LIST>[ \t]*\r?\n  { BEGIN(INITIAL); }
201
202<MNAME>[\ \t\r]*\n  { BEGIN(INITIAL); }
203
204<MNAME>[^,\ \t\r\n][^,\r\n]*\\{SPACE}*\n {
205                        cattext(true, 1);
206                    }
207
208<MNAME>{SPACE}*\\{SPACE}*\n   ;
209
210<MNAME>[^,\ \t\r\n][^,\r\n]*[^,\ \t\r\n] {
211                        cattext();
212                        return MONSTER_NAME;
213                    }
214
215<MNAME>,            { clean(); return COMMA; }
216<MNAME>[ \t\r]+     ;
217
218<ARGUMENT>{NSPACE}.*\\{SPACE}*\n {
219                        cattext(true, 1);
220                    }
221
222<ARGUMENT>{SPACE}*\\{SPACE}*\n   ;
223
224<ARGUMENT>{NSPACE}.*{NSPACE} {
225                        cattext();
226                    }
227
228<ARGUMENT>{NSPACE}  cattext();
229
230<ARGUMENT>{SPACE}*$ { BEGIN(INITIAL); str_check(); return STRING; }
231
232^[ \t]*#.*          ;
233
234^\s*MAP[ \t\r]*$    { BEGIN(MAPDEF); }
235
236^[ \t]*:                  { BEGIN(LUA_ONELINER); return MAIN; }
237
238^[ \t]*prelude[ \t]*\{\{  { BEGIN(LUA); return PRELUDE; }
239^[ \t]*lua[ \t]*\{\{      { BEGIN(LUA); return MAIN; }
240^[ \t]*\{\{         { BEGIN(LUA); return MAIN; }
241^[ \t]*validate[ \t]*\{\{ { BEGIN(LUA); return VALIDATE; }
242^[ \t]*veto[ \t]*\{\{     { BEGIN(LUA); return VETO; }
243^[ \t]*epilogue[ \t]*\{\{ { BEGIN(LUA); return EPILOGUE; }
244
245NAME:               { CBEGIN(ARGUMENT); return NAME; }
246default-depth:      { CBEGIN(ARGUMENT); return DEFAULT_DEPTH; }
247DESC:               { CBEGIN(ARGUMENT); return DESC; }
248DEPTH:              { CBEGIN(ARGUMENT); return DEPTH; }
249ORIENT:             { CBEGIN(ARGUMENT); return ORIENT; }
250PLACE:              { CBEGIN(ARGUMENT); return PLACE; }
251WELCOME:            { CBEGIN(ARGUMENT); return WELCOME; }
252CHANCE:             return CHANCE;
253WEIGHT:             return WEIGHT;
254TAGS:               { CBEGIN(KEYWORDS); return TAGS; }
255SUBST:              { CBEGIN(ITEM_LIST); return SUBST; }
256NSUBST:             { CBEGIN(ITEM_LIST); return NSUBST; }
257COLOUR:             { CBEGIN(ITEM_LIST); return COLOUR; }
258LFLOORCOL:          { CBEGIN(ARGUMENT); return LFLOORCOL; }
259LROCKCOL:           { CBEGIN(ARGUMENT); return LROCKCOL; }
260LFLOORTILE:         { CBEGIN(ARGUMENT); return LFLOORTILE; }
261LROCKTILE:          { CBEGIN(ARGUMENT); return LROCKTILE; }
262FTILE:              { CBEGIN(ITEM_LIST); return FTILE; }
263RTILE:              { CBEGIN(ITEM_LIST); return RTILE; }
264TILE:               { CBEGIN(ITEM_LIST); return TILE; }
265FHEIGHT:            { CBEGIN(ITEM_LIST); return FHEIGHT; }
266MONS:               { CBEGIN(MNAME); return MONS; }
267ITEM:               { CBEGIN(ITEM_LIST); return ITEM; }
268MARKER:             { CBEGIN(ARGUMENT); return MARKER; }
269SHUFFLE:            { CBEGIN(ITEM_LIST); return SHUFFLE; }
270CLEAR:              { CBEGIN(ARGUMENT); return CLEAR; }
271ORDER:              { return ORDER; }
272
273KFEAT:              { CBEGIN(ARGUMENT); return KFEAT; }
274KITEM:              { CBEGIN(ARGUMENT); return KITEM; }
275KMONS:              { CBEGIN(ARGUMENT); return KMONS; }
276KMASK:              { CBEGIN(ARGUMENT); return KMASK; }
277KPROP:              { CBEGIN(ARGUMENT); return KPROP; }
278SUBVAULT:           { CBEGIN(ARGUMENT); return SUBVAULT; }
279
280,                   return COMMA;
281
282:                   return COLON;
283
284%                   return PERC;
285-                   return DASH;
286
287[+-]?[.][0-9]+|[+-]?[0-9]+[.]?[0-9]* {
288                        clean();
289                        yylval.f = strtod(yytext, nullptr);
290                        return NUMBER;
291                    }
292
293\([^\r\n)]*\)       {
294                        settext(true, 1, 1);
295                        return STRING;
296                    }
297
298[\ \t]+             ;
299\r?\n               ;
300\r                  ;
301.                   return CHARACTER;
302
303%%
304
305int yywrap()
306{
307    clean();
308    flush_free_queue(0);
309    return 1;
310}
311