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