1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (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
23 %option noyywrap
24 %option nounput
25 %option noinput
26 %option never-interactive
27
28 %{
29
30 #define YY_NO_UNISTD_H
31 #define FORBIDDEN_SYMBOL_ALLOW_ALL
32
33 #include "common/str.h"
34
35 #include "director/lingo/lingo.h"
36 #include "director/lingo/lingo-gr.h"
37
38 using namespace Director;
39
40 int yyparse();
count()41 static void count() {
42 if (debugChannelSet(-1, kDebugLingoParse))
43 debug("LEXER: Read '%s' at %d:%d", yytext, g_lingo->_linenumber, g_lingo->_colnumber);
44
45 g_lingo->_colnumber += strlen(yytext);
46 }
47
countnl()48 static void countnl() {
49 char *p = yytext;
50
51 while(*p == '\n' || *p == '\r')
52 p++;
53
54 g_lingo->_linenumber++;
55 g_lingo->_colnumber = strlen(p);
56 }
57
checkImmediate(int token)58 static int checkImmediate(int token) {
59 if (g_lingo->_immediateMode) {
60 yylval.s = new Common::String(yytext);
61
62 return ID;
63 }
64
65 return token;
66 }
67
68 %}
69
70 identifier [_[:alpha:]][_[:alnum:]]*
71 constfloat [[:digit:]]+\.[[:digit:]]*
72 constinteger [[:digit:]]+
73 conststring \"[^\"\r\n]*\"
74 operator [-+*/%=^:,()><&\[\]]
75 newline [ \t]*[\n\r]
76 whitespace [\t ]
77
78 %%
79
80 \xC2[\r\n] { g_lingo->_linenumber++; g_lingo->_colnumber = 0; }
81 --[^\r\n]*
82 ^{whitespace}+ { count(); }
83 [\t]+ { count(); return ' '; }
84
85 [#]{identifier} { count(); yylval.s = new Common::String(yytext); return SYMBOL; } // D3
86
87 (?i:after) { count(); return tAFTER; } // D3
88 (?i:and) { count(); return tAND; }
89 (?i:before) { count(); return tBEFORE; } // D3
90 (?i:char) { count(); return tCHAR; } // D3
91 (?i:contains) { count(); return tCONTAINS; }
92 (?i:done) { count(); return tDONE; }
93 (?i:down) { count(); return tDOWN; }
94 (?i:if) { count(); return tIF; }
95 (?i:[\n\r]+[\t ]*else[\t ]+if) { countnl(); return tNLELSIF; }
96 (?i:[\n\r]+[\t ]*else) { countnl(); return tNLELSE; }
97 (?i:else) { count(); return tELSE; }
98 (?i:end)([\t ]*{identifier})? {
99 count();
100
101 const char *ptr = &yytext[4]; // Skip 'end '
102 while (*ptr == ' ' || *ptr == '\t')
103 ptr++;
104
105 yylval.s = new Common::String(ptr);
106
107 return ENDCLAUSE;
108 }
109 (?i:factory) { count(); return tFACTORY; }
110 (?i:exit) { count(); return tEXIT; }
111 (?i:frame) { count(); return tFRAME; }
112 (?i:global) { count(); return tGLOBAL; }
113 (?i:go[\t ]+to) { count(); return tGO; }
114 (?i:go) { count(); return tGO; }
115 (?i:instance) { count(); return tINSTANCE; }
116 (?i:intersects) { count(); return tINTERSECTS; }
117 (?i:into) { count(); return tINTO; }
118 (?i:item) { count(); return tITEM; }
119 (?i:line) { count(); return tLINE; }
120 (?i:loop) { count(); return checkImmediate(tLOOP); }
121 (?i:macro) { count(); return tMACRO; }
122 (?i:method) { count(); return tMETHOD; }
123 (?i:mod) { count(); return tMOD; }
124 (?i:movie) { count(); return tMOVIE; }
125 (?i:next) { count(); return tNEXT; }
126 (?i:not) { count(); return tNOT; }
127 (?i:of) { count(); return tOF; }
128 (?i:on) { count(); return tON; } // D3
129 (?i:open) { count(); return tOPEN; }
130 (?i:or) { count(); return tOR; }
131 (?i:play) { count(); return tPLAY; }
132 (?i:playAccel) { count(); yylval.s = new Common::String(yytext); return tPLAYACCEL; }
133 (?i:previous) { count(); return tPREVIOUS; }
134 (?i:property) { count(); return tPROPERTY; } // D4
135 (?i:put) { count(); return tPUT; }
136 (?i:repeat) { count(); return checkImmediate(tREPEAT); }
137 (?i:set) { count(); return tSET; }
138 (?i:starts) { count(); return tSTARTS; }
139 (?i:tell) { count(); return tTELL; }
140 (?i:the[ \t]+last[\t ]+of[\t ]+) {
141 count();
142
143 yylval.e[0] = g_lingo->_theEntities["last"]->entity;
144 yylval.e[1] = 0; // No field
145
146 return THEENTITYWITHID;
147 }
148 (?i:the[ \t]+sqrt[\t ]+of[\t ]+) {
149 count();
150
151 yylval.e[0] = g_lingo->_theEntities["sqrt"]->entity;
152 yylval.e[1] = 0; // No field
153
154 return THEENTITYWITHID;
155 }
156 (?i:the[ \t]+[[:alpha:]]+[\t ]+of[\t ]+[[:alpha:]]+) {
157 count();
158
159 const char *ptr = &yytext[4]; // Skip 'the '
160 while (*ptr == ' ' || *ptr == '\t')
161 ptr++;
162
163 Common::String field;
164 while (*ptr != ' ' && *ptr != '\t')
165 field += *ptr++;
166
167 while (*ptr == ' ' || *ptr == '\t')
168 ptr++;
169
170 ptr += 3; // Skip 'of '
171
172 while (*ptr == ' ' || *ptr == '\t')
173 ptr++;
174
175 if (g_lingo->_theEntities.contains(ptr)) {
176 field = Common::String::format("%d%s", g_lingo->_theEntities[ptr]->entity, field.c_str());
177
178 if (!g_lingo->_theEntityFields.contains(field)) {
179 error("Unhandled the field %s", ptr);
180 }
181
182 if (g_lingo->_theEntityFields[field]->entity != g_lingo->_theEntities[ptr]->entity)
183 error("Unsupported field '%s' for entity '%s'", field.c_str(), ptr);
184
185 yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
186 yylval.e[1] = g_lingo->_theEntityFields[field]->field;
187
188 if (g_lingo->_theEntities[ptr]->hasId)
189 return THEENTITYWITHID;
190 else
191 return THEENTITY;
192 }
193
194 warning("Unhandled the entity %s", ptr);
195 }
196 (?i:the[ \t]+[[:alpha:]]+[ \t+](date|time)) {
197 count();
198
199 const char *ptr = &yytext[4]; // Skip 'the '
200 while (*ptr == ' ' || *ptr == '\t')
201 ptr++;
202
203 Common::String field;
204 while (*ptr != ' ' && *ptr != '\t')
205 field += *ptr++;
206
207 while (*ptr == ' ' || *ptr == '\t')
208 ptr++;
209
210 field = Common::String::format("%d%s", g_lingo->_theEntities[ptr]->entity, field.c_str());
211
212 if (!g_lingo->_theEntityFields.contains(field)) {
213 error("Unhandled the field %s", ptr);
214 }
215
216 if (g_lingo->_theEntityFields[field]->entity != g_lingo->_theEntities[ptr]->entity)
217 error("Unsupported field '%s' for entity '%s'", field.c_str(), ptr);
218
219 yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
220 yylval.e[1] = g_lingo->_theEntityFields[field]->field;
221
222 if (g_lingo->_theEntities[ptr]->hasId)
223 return THEENTITYWITHID;
224 else
225 return THEENTITY;
226 }
227 (?i:the[ \t]+[[:alpha:]]+) {
228 count();
229
230 const char *ptr = &yytext[4]; // Skip 'the '
231 while (*ptr == ' ' || *ptr == '\t')
232 ptr++;
233
234 if (g_lingo->_theEntities.contains(ptr)) {
235 yylval.e[0] = g_lingo->_theEntities[ptr]->entity;
236 yylval.e[1] = 0; // No field
237
238 if (g_lingo->_theEntities[ptr]->hasId)
239 return THEENTITYWITHID;
240 else
241 return THEENTITY;
242 }
243
244 warning("Unhandled the entity %s", ptr);
245 }
246 (?i:then) { count(); return tTHEN; }
247 (?i:then[\n\r]+) { count(); return tTHENNL; }
248 (?i:to) { count(); return tTO; }
249 (?i:sprite) { count(); return tSPRITE; }
250 (?i:with) { count(); return tWITH; }
251 (?i:within) { count(); return tWITHIN; }
252 (?i:when) { count(); return tWHEN; }
253 (?i:while) { count(); return tWHILE; }
254 (?i:word) { count(); return tWORD; }
255
256 [<][>] { count(); return tNEQ; }
257 [>][=] { count(); return tGE; }
258 [<][=] { count(); return tLE; }
259 [&][&] { count(); return tCONCAT; }
260
261 {identifier} {
262 count();
263 yylval.s = new Common::String(yytext);
264
265 if (g_lingo->_ignoreMe && yylval.s->equalsIgnoreCase("me"))
266 return ID;
267
268 if (g_lingo->_twoWordBuiltins.contains(yytext))
269 return TWOWORDBUILTIN;
270
271 // Special treatment of 'me'. First parameter is method name
272 if (!g_lingo->_currentFactory.empty()) {
273 if (yylval.s->equalsIgnoreCase("me"))
274 return tME;
275 }
276
277 if (g_lingo->_builtins.contains(yytext)) {
278 int type = g_lingo->_builtins[yytext]->type;
279 if ((type == BLTIN || type == FBLTIN || type == RBLTIN) && g_lingo->_builtins[yytext]->parens == false) {
280 if (type == RBLTIN) {
281 if (g_lingo->_builtins[yytext]->nargs != 1 || g_lingo->_builtins[yytext]->maxArgs != 1)
282 error("Incorrectly set RBLTIN %s", yytext);
283
284 return RBLTINONEARG;
285 }
286 if (g_lingo->_builtins[yytext]->nargs == 0) {
287 if (g_lingo->_builtins[yytext]->maxArgs == 0)
288 return type == BLTIN ? BLTINNOARGS : FBLTINNOARGS;
289 else if (g_lingo->_builtins[yytext]->maxArgs == 1)
290 return BLTINNOARGSORONE;
291 else
292 return type == BLTIN ? BLTINARGLIST : FBLTINARGLIST;
293 } else if (g_lingo->_builtins[yytext]->nargs == 1 &&
294 g_lingo->_builtins[yytext]->maxArgs == 1) {
295 return type == BLTIN ? BLTINONEARG : FBLTINONEARG;
296 } else if (g_lingo->_builtins[yytext]->nargs == -1) {
297 return type == BLTIN ? BLTINARGLIST : FBLTINARGLIST;
298 } else {
299 return type == BLTIN ? BLTINARGLIST : FBLTINARGLIST;
300 }
301 }
302 }
303
304 return ID;
305 }
306 {constfloat} { count(); yylval.f = atof(yytext); return FLOAT; }
307 {constinteger} { count(); yylval.i = strtol(yytext, NULL, 10); return INT; }
308 {operator} { count(); return *yytext; }
309 {newline} { return '\n'; }
310 {conststring} { count(); yylval.s = new Common::String(&yytext[1]); yylval.s->deleteLastChar(); return STRING; }
311 . { count(); }
312
313 %%
314
315 extern int yydebug;
316
317 namespace Director {
318
319 int Lingo::parse(const char *code) {
320 YY_BUFFER_STATE bp;
321
322 if (debugChannelSet(-1, kDebugLingoParse))
323 yydebug = 1;
324 else
325 yydebug = 0;
326
327 yy_delete_buffer(YY_CURRENT_BUFFER);
328
329 bp = yy_scan_string(code);
330 yy_switch_to_buffer(bp);
331 yyparse();
332 yy_delete_buffer(bp);
333
334 return 0;
335 }
336
337 }
338