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 // Heavily inspired by hoc
24 // Copyright (C) AT&T 1995
25 // All Rights Reserved
26 //
27 // Permission to use, copy, modify, and distribute this software and
28 // its documentation for any purpose and without fee is hereby
29 // granted, provided that the above copyright notice appear in all
30 // copies and that both that the copyright notice and this
31 // permission notice and warranty disclaimer appear in supporting
32 // documentation, and that the name of AT&T or any of its entities
33 // not be used in advertising or publicity pertaining to
34 // distribution of the software without specific, written prior
35 // permission.
36 //
37 // AT&T DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
38 // INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
39 // IN NO EVENT SHALL AT&T OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
40 // SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
42 // IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
43 // ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
44 // THIS SOFTWARE.
45 
46 
47 %debug
48 
49 %{
50 #define FORBIDDEN_SYMBOL_ALLOW_ALL
51 
52 #include "common/hash-str.h"
53 
54 #include "director/lingo/lingo.h"
55 #include "director/lingo/lingo-gr.h"
56 
57 extern int yylex();
58 extern int yyparse();
59 
60 using namespace Director;
yyerror(const char * s)61 void yyerror(const char *s) {
62 	g_lingo->_hadError = true;
63 	warning("%s at line %d col %d", s, g_lingo->_linenumber, g_lingo->_colnumber);
64 }
65 
checkEnd(Common::String * token,const char * expect,bool required)66 void checkEnd(Common::String *token, const char *expect, bool required) {
67 	if (required) {
68 		if (token->compareToIgnoreCase(expect)) {
69 			Common::String err = Common::String::format("end mismatch. Expected %s but got %s", expect, token->c_str());
70 			yyerror(err.c_str());
71 		}
72 	}
73 
74 	delete token;
75 }
76 
77 %}
78 
79 %union {
80 	Common::String *s;
81 	int i;
82 	double f;
83 	int e[2];	// Entity + field
84 	int code;
85 	int narg;	/* number of arguments */
86 	Common::Array<double> *arr;
87 }
88 
89 %token UNARY
90 %token CASTREF VOID VAR POINT RECT ARRAY OBJECT REFERENCE
91 %token<i> INT
92 %token<e> THEENTITY THEENTITYWITHID
93 %token<f> FLOAT
94 %token<s> BLTIN BLTINNOARGS BLTINNOARGSORONE BLTINONEARG BLTINARGLIST TWOWORDBUILTIN
95 %token<s> FBLTIN FBLTINNOARGS FBLTINONEARG FBLTINARGLIST RBLTIN RBLTINONEARG
96 %token<s> ID STRING HANDLER SYMBOL
97 %token<s> ENDCLAUSE tPLAYACCEL
98 %token tDOWN tELSE tNLELSIF tEXIT tFRAME tGLOBAL tGO tIF tINTO tLOOP tMACRO
99 %token tMOVIE tNEXT tOF tPREVIOUS tPUT tREPEAT tSET tTHEN tTHENNL tTO tWHEN
100 %token tWITH tWHILE tNLELSE tFACTORY tMETHOD tOPEN tPLAY tDONE tINSTANCE
101 %token tGE tLE tGT tLT tEQ tNEQ tAND tOR tNOT tMOD
102 %token tAFTER tBEFORE tCONCAT tCONTAINS tSTARTS tCHAR tITEM tLINE tWORD
103 %token tSPRITE tINTERSECTS tWITHIN tTELL tPROPERTY
104 %token tON tME
105 
106 %type<code> asgn begin elseif elsestmtoneliner end expr if when repeatwhile repeatwith stmtlist tell
107 %type<narg> argdef arglist nonemptyarglist
108 %type<s> on
109 
110 %right '='
111 %left tLT tLE tGT tGE tNEQ tCONTAINS tSTARTS
112 %left '&'
113 %left '+' '-'
114 %left '*' '/' '%' tAND tOR tMOD
115 %right UNARY
116 
117 %%
118 
119 program: program nl programline
120 	| programline
121 	| error	nl		{ yyerrok; }
122 	;
123 
124 nl:	'\n' {
125 		g_lingo->_linenumber++;
126 		g_lingo->_colnumber = 1;
127 	}
128 
129 programline: /* empty */
130 	| defn
131 	| macro
132 	| stmt
133 	;
134 
135 asgn: tPUT expr tINTO ID 		{
136 		g_lingo->code1(g_lingo->c_varpush);
137 		g_lingo->codeString($4->c_str());
138 		g_lingo->code1(g_lingo->c_assign);
139 		$$ = $2;
140 		delete $4; }
141 	| tPUT expr tINTO reference 		{
142 			g_lingo->code1(g_lingo->c_assign);
143 			$$ = $2; }
144 	| tPUT expr tAFTER expr 		{ $$ = g_lingo->code1(g_lingo->c_after); }		// D3
145 	| tPUT expr tBEFORE expr 		{ $$ = g_lingo->code1(g_lingo->c_before); }		// D3
146 	| tSET ID '=' expr			{
147 		g_lingo->code1(g_lingo->c_varpush);
148 		g_lingo->codeString($2->c_str());
149 		g_lingo->code1(g_lingo->c_assign);
150 		$$ = $4;
151 		delete $2; }
152 	| tSET THEENTITY '=' expr	{
153 		g_lingo->codeConst(0); // Put dummy id
154 		g_lingo->code1(g_lingo->c_theentityassign);
155 		inst e = 0, f = 0;
156 		WRITE_UINT32(&e, $2[0]);
157 		WRITE_UINT32(&f, $2[1]);
158 		g_lingo->code2(e, f);
159 		$$ = $4; }
160 	| tSET THEENTITYWITHID expr '=' expr	{
161 		g_lingo->code1(g_lingo->c_swap);
162 		g_lingo->code1(g_lingo->c_theentityassign);
163 		inst e = 0, f = 0;
164 		WRITE_UINT32(&e, $2[0]);
165 		WRITE_UINT32(&f, $2[1]);
166 		g_lingo->code2(e, f);
167 		$$ = $5; }
168 	| tSET ID tTO expr			{
169 		g_lingo->code1(g_lingo->c_varpush);
170 		g_lingo->codeString($2->c_str());
171 		g_lingo->code1(g_lingo->c_assign);
172 		$$ = $4;
173 		delete $2; }
174 	| tSET THEENTITY tTO expr	{
175 		g_lingo->codeConst(0); // Put dummy id
176 		g_lingo->code1(g_lingo->c_theentityassign);
177 		inst e = 0, f = 0;
178 		WRITE_UINT32(&e, $2[0]);
179 		WRITE_UINT32(&f, $2[1]);
180 		g_lingo->code2(e, f);
181 		$$ = $4; }
182 	| tSET THEENTITYWITHID expr tTO expr	{
183 		g_lingo->code1(g_lingo->c_swap);
184 		g_lingo->code1(g_lingo->c_theentityassign);
185 		inst e = 0, f = 0;
186 		WRITE_UINT32(&e, $2[0]);
187 		WRITE_UINT32(&f, $2[1]);
188 		g_lingo->code2(e, f);
189 		$$ = $5; }
190 	;
191 
192 stmtoneliner: expr
193 	| proc
194 	;
195 
196 stmt: stmtoneliner
197 	| ifstmt
198 	// repeat while (expression = TRUE)
199 	//   statements
200 	// end repeat
201 	//
202 	| repeatwhile '(' cond ')' stmtlist end ENDCLAUSE	{
203 		inst body = 0, end = 0;
204 		WRITE_UINT32(&body, $5 - $1);
205 		WRITE_UINT32(&end, $6 - $1);
206 		(*g_lingo->_currentScript)[$1 + 1] = body;	/* body of loop */
207 		(*g_lingo->_currentScript)[$1 + 2] = end;	/* end, if cond fails */
208 
209 		checkEnd($7, "repeat", true); }
210 	;
211 	// repeat with index = start to end
212 	//   statements
213 	// end repeat
214 	//
215 	| repeatwith '=' expr end tTO expr end stmtlist end ENDCLAUSE {
216 		inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
217 		WRITE_UINT32(&init, $3 - $1);
218 		WRITE_UINT32(&finish, $6 - $1);
219 		WRITE_UINT32(&body, $8 - $1);
220 		WRITE_UINT32(&end, $9 - $1);
221 		WRITE_UINT32(&inc, 1);
222 		(*g_lingo->_currentScript)[$1 + 1] = init;	/* initial count value */
223 		(*g_lingo->_currentScript)[$1 + 2] = finish;/* final count value */
224 		(*g_lingo->_currentScript)[$1 + 3] = body;	/* body of loop */
225 		(*g_lingo->_currentScript)[$1 + 4] = inc;	/* increment */
226 		(*g_lingo->_currentScript)[$1 + 5] = end;	/* end, if cond fails */
227 
228 		checkEnd($10, "repeat", true); }
229 	// repeat with index = high down to low
230 	//   statements
231 	// end repeat
232 	//
233 	| repeatwith '=' expr end tDOWN tTO expr end stmtlist end ENDCLAUSE {
234 		inst init = 0, finish = 0, body = 0, end = 0, inc = 0;
235 		WRITE_UINT32(&init, $3 - $1);
236 		WRITE_UINT32(&finish, $7 - $1);
237 		WRITE_UINT32(&body, $9 - $1);
238 		WRITE_UINT32(&end, $10 - $1);
239 		WRITE_UINT32(&inc, -1);
240 		(*g_lingo->_currentScript)[$1 + 1] = init;	/* initial count value */
241 		(*g_lingo->_currentScript)[$1 + 2] = finish;/* final count value */
242 		(*g_lingo->_currentScript)[$1 + 3] = body;	/* body of loop */
243 		(*g_lingo->_currentScript)[$1 + 4] = inc;	/* increment */
244 		(*g_lingo->_currentScript)[$1 + 5] = end;	/* end, if cond fails */
245 
246 		checkEnd($11, "repeat", true); }
247 	| when stmtoneliner end {
248 			inst end = 0;
249 			WRITE_UINT32(&end, $3 - $1);
250 			g_lingo->code1(STOP);
251 			(*g_lingo->_currentScript)[$1 + 1] = end;
252 		}
253 	| tell expr nl stmtlist end ENDCLAUSE {
254 			warning("STUB: TELL is not implemented");
255 			checkEnd($6, "tell", true); }
256 	| tell expr tTO expr {
257 			warning("STUB: TELL is not implemented");
258 		}
259 	;
260 
261 ifstmt:	if cond tTHENNL stmtlist end ENDCLAUSE		{
262 		inst then = 0, end = 0;
263 		WRITE_UINT32(&then, $4 - $1);
264 		WRITE_UINT32(&end, $5 - $1);
265 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
266 		(*g_lingo->_currentScript)[$1 + 3] = end;	/* end, if cond fails */
267 
268 		checkEnd($6, "if", true);
269 
270 		g_lingo->processIf(0, 0); }
271 	| if cond tTHENNL stmtlist end tNLELSE stmtlist end ENDCLAUSE {
272 		inst then = 0, else1 = 0, end = 0;
273 		WRITE_UINT32(&then, $4 - $1);
274 		WRITE_UINT32(&else1, $7 - $1);
275 		WRITE_UINT32(&end, $8 - $1);
276 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
277 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
278 		(*g_lingo->_currentScript)[$1 + 3] = end;	/* end, if cond fails */
279 
280 		checkEnd($9, "if", true);
281 
282 		g_lingo->processIf(0, 0); }
283 	| if cond tTHENNL stmtlist end begin elseifstmt end ENDCLAUSE {
284 		inst then = 0, else1 = 0, end = 0;
285 		WRITE_UINT32(&then, $4 - $1);
286 		WRITE_UINT32(&else1, $6 - $1);
287 		WRITE_UINT32(&end, $8 - $1);
288 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
289 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
290 		(*g_lingo->_currentScript)[$1 + 3] = end;	/* end, if cond fails */
291 
292 		checkEnd($9, "if", true);
293 
294 		g_lingo->processIf(0, $8 - $1); }
295 	| if cond tTHENNL stmtlist end tNLELSE begin stmtoneliner end {
296 		inst then = 0, else1 = 0, end = 0;
297 		WRITE_UINT32(&then, $4 - $1);
298 		WRITE_UINT32(&else1, $7 - $1);
299 		WRITE_UINT32(&end, $9 - $1);
300 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
301 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
302 		(*g_lingo->_currentScript)[$1 + 3] = end;	/* end, if cond fails */
303 
304 		g_lingo->processIf(0, $9 - $1); }
305 	| if cond tTHEN begin stmtoneliner end {
306 		inst then = 0, else1 = 0, end = 0;
307 		WRITE_UINT32(&then, $4 - $1);
308 		WRITE_UINT32(&else1, 0);
309 		WRITE_UINT32(&end, $6 - $1);
310 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
311 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
312 		(*g_lingo->_currentScript)[$1 + 3] = end; 	/* end, if cond fails */
313 
314 		g_lingo->processIf(0, 0); }
315 	| if cond tTHEN begin stmtoneliner end tNLELSE begin stmtoneliner end {
316 		inst then = 0, else1 = 0, end = 0;
317 		WRITE_UINT32(&then, $4 - $1);
318 		WRITE_UINT32(&else1, $8 - $1);
319 		WRITE_UINT32(&end, $10 - $1);
320 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
321 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
322 		(*g_lingo->_currentScript)[$1 + 3] = end; 	/* end, if cond fails */
323 
324 		g_lingo->processIf(0, 0); }
325 	| if cond tTHEN begin stmtoneliner end elseifstmtoneliner end elsestmtoneliner end {
326 		inst then = 0, else1 = 0, end = 0;
327 		WRITE_UINT32(&then, $4 - $1);
328 		WRITE_UINT32(&else1, $6 - $1);
329 		WRITE_UINT32(&end, $10 - $1);
330 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
331 		(*g_lingo->_currentScript)[$1 + 2] = else1;	/* elsepart */
332 		(*g_lingo->_currentScript)[$1 + 3] = end; 	/* end, if cond fails */
333 
334 		g_lingo->processIf(0, $10 - $1); }
335 	;
336 
337 elsestmtoneliner: /* nothing */		{ $$ = 0; }
338 	| tNLELSE begin stmtoneliner	{ $$ = $2; }
339 	;
340 
341 elseifstmt:	elseifstmt elseifstmt1
342 	|	elseifstmt1
343 	;
344 
345 elseifstmtoneliner: elseifstmtoneliner elseifstmtoneliner1
346 	| elseifstmtoneliner1
347 	;
348 
349 elseifstmtoneliner1:	elseif cond tTHEN begin stmt end {
350 		inst then = 0;
351 		WRITE_UINT32(&then, $4 - $1);
352 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
353 
354 		g_lingo->codeLabel($1); }
355 	;
356 
357 elseifstmt1: elseifstmtoneliner
358 	| elseif cond tTHEN begin stmtlist end {
359 		inst then = 0;
360 		WRITE_UINT32(&then, $5 - $1);
361 		(*g_lingo->_currentScript)[$1 + 1] = then;	/* thenpart */
362 
363 		g_lingo->codeLabel($1); }
364 	;
365 
366 cond:	   expr 				{ g_lingo->code1(STOP); }
367 	| expr '=' expr				{ g_lingo->code2(g_lingo->c_eq, STOP); }
368 	| '(' cond ')'
369 	;
370 
371 repeatwhile:	tREPEAT tWHILE		{ $$ = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); }
372 	;
373 
374 repeatwith:		tREPEAT tWITH ID	{
375 		$$ = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP);
376 		g_lingo->code3(STOP, STOP, STOP);
377 		g_lingo->codeString($3->c_str());
378 		delete $3; }
379 	;
380 
381 if:	  tIF					{
382 		$$ = g_lingo->code1(g_lingo->c_ifcode);
383 		g_lingo->code3(STOP, STOP, STOP);
384 		g_lingo->code1(0);  // Do not skip end
385 		g_lingo->codeLabel(0); } // Mark beginning of the if() statement
386 	;
387 
388 elseif:	  tNLELSIF			{
389 		inst skipEnd;
390 		WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions
391 		$$ = g_lingo->code1(g_lingo->c_ifcode);
392 		g_lingo->code3(STOP, STOP, STOP);
393 		g_lingo->code1(skipEnd); }
394 	;
395 
396 begin:	  /* nothing */		{ $$ = g_lingo->_currentScript->size(); }
397 	;
398 
399 end:	  /* nothing */		{ g_lingo->code1(STOP); $$ = g_lingo->_currentScript->size(); }
400 	;
401 
402 stmtlist: /* nothing */		{ $$ = g_lingo->_currentScript->size(); }
403 	| stmtlist nl
404 	| stmtlist stmt
405 	;
406 
407 when:	  tWHEN	ID tTHEN	{
408 		$$ = g_lingo->code1(g_lingo->c_whencode);
409 		g_lingo->code1(STOP);
410 		g_lingo->codeString($2->c_str());
411 		delete $2; }
412 
413 tell:	  tTELL				{
414 		$$ = g_lingo->code1(g_lingo->c_tellcode);
415 		g_lingo->code1(STOP); }
416 
417 expr: INT		{ $$ = g_lingo->codeConst($1); }
418 	| FLOAT		{
419 		$$ = g_lingo->code1(g_lingo->c_fconstpush);
420 		g_lingo->codeFloat($1); }
421 	| SYMBOL	{											// D3
422 		$$ = g_lingo->code1(g_lingo->c_symbolpush);
423 		g_lingo->codeString($1->c_str()); }
424 	| STRING		{
425 		$$ = g_lingo->code1(g_lingo->c_stringpush);
426 		g_lingo->codeString($1->c_str()); }
427 	| FBLTINNOARGS 	{
428 		g_lingo->codeFunc($1, 0);
429 		delete $1; }
430 	| FBLTINONEARG expr		{
431 		g_lingo->codeFunc($1, 1);
432 		delete $1; }
433 	| FBLTINARGLIST nonemptyarglist			{ g_lingo->codeFunc($1, $2); }
434 	| FBLTINARGLIST '(' nonemptyarglist ')'	{ g_lingo->codeFunc($1, $3); }
435 	| ID '(' arglist ')'	{
436 		$$ = g_lingo->codeFunc($1, $3);
437 		delete $1; }
438 	| ID		{
439 		$$ = g_lingo->code1(g_lingo->c_eval);
440 		g_lingo->codeString($1->c_str());
441 		delete $1; }
442 	| THEENTITY	{
443 		$$ = g_lingo->codeConst(0); // Put dummy id
444 		g_lingo->code1(g_lingo->c_theentitypush);
445 		inst e = 0, f = 0;
446 		WRITE_UINT32(&e, $1[0]);
447 		WRITE_UINT32(&f, $1[1]);
448 		g_lingo->code2(e, f); }
449 	| THEENTITYWITHID expr	{
450 		$$ = g_lingo->code1(g_lingo->c_theentitypush);
451 		inst e = 0, f = 0;
452 		WRITE_UINT32(&e, $1[0]);
453 		WRITE_UINT32(&f, $1[1]);
454 		g_lingo->code2(e, f); }
455 	| asgn
456 	| expr '+' expr				{ g_lingo->code1(g_lingo->c_add); }
457 	| expr '-' expr				{ g_lingo->code1(g_lingo->c_sub); }
458 	| expr '*' expr				{ g_lingo->code1(g_lingo->c_mul); }
459 	| expr '/' expr				{ g_lingo->code1(g_lingo->c_div); }
460 	| expr tMOD expr			{ g_lingo->code1(g_lingo->c_mod); }
461 	| expr '>' expr				{ g_lingo->code1(g_lingo->c_gt); }
462 	| expr '<' expr				{ g_lingo->code1(g_lingo->c_lt); }
463 	| expr tNEQ expr			{ g_lingo->code1(g_lingo->c_neq); }
464 	| expr tGE expr				{ g_lingo->code1(g_lingo->c_ge); }
465 	| expr tLE expr				{ g_lingo->code1(g_lingo->c_le); }
466 	| expr tAND expr			{ g_lingo->code1(g_lingo->c_and); }
467 	| expr tOR expr				{ g_lingo->code1(g_lingo->c_or); }
468 	| tNOT expr  %prec UNARY	{ g_lingo->code1(g_lingo->c_not); }
469 	| expr '&' expr				{ g_lingo->code1(g_lingo->c_ampersand); }
470 	| expr tCONCAT expr			{ g_lingo->code1(g_lingo->c_concat); }
471 	| expr tCONTAINS expr		{ g_lingo->code1(g_lingo->c_contains); }
472 	| expr tSTARTS expr			{ g_lingo->code1(g_lingo->c_starts); }
473 	| '+' expr  %prec UNARY		{ $$ = $2; }
474 	| '-' expr  %prec UNARY		{ $$ = $2; g_lingo->code1(g_lingo->c_negate); }
475 	| '(' expr ')'				{ $$ = $2; }
476 	| '[' arglist ']'			{ $$ = g_lingo->codeArray($2); }
477 	| tSPRITE expr tINTERSECTS expr 	{ g_lingo->code1(g_lingo->c_intersects); }
478 	| tSPRITE expr tWITHIN expr		 	{ g_lingo->code1(g_lingo->c_within); }
479 	| tCHAR expr tOF expr				{ g_lingo->code1(g_lingo->c_charOf); }
480 	| tCHAR expr tTO expr tOF expr		{ g_lingo->code1(g_lingo->c_charToOf); }
481 	| tITEM expr tOF expr				{ g_lingo->code1(g_lingo->c_itemOf); }
482 	| tITEM expr tTO expr tOF expr		{ g_lingo->code1(g_lingo->c_itemToOf); }
483 	| tLINE expr tOF expr				{ g_lingo->code1(g_lingo->c_lineOf); }
484 	| tLINE expr tTO expr tOF expr		{ g_lingo->code1(g_lingo->c_lineToOf); }
485 	| tWORD expr tOF expr				{ g_lingo->code1(g_lingo->c_wordOf); }
486 	| tWORD expr tTO expr tOF expr		{ g_lingo->code1(g_lingo->c_wordToOf); }
487 	;
488 
489 reference: 	RBLTINONEARG expr		{
490 		g_lingo->codeFunc($1, 1);
491 		delete $1; }
492 	;
493 
494 proc: tPUT expr				{ g_lingo->code1(g_lingo->c_printtop); }
495 	| gotofunc
496 	| playfunc
497 	| tEXIT tREPEAT			{ g_lingo->code1(g_lingo->c_exitRepeat); }
498 	| tEXIT					{ g_lingo->code1(g_lingo->c_procret); }
499 	| tGLOBAL globallist
500 	| tPROPERTY propertylist
501 	| tINSTANCE instancelist
502 	| BLTINNOARGS 	{
503 		g_lingo->codeFunc($1, 0);
504 		delete $1; }
505 	| BLTINONEARG expr		{
506 		g_lingo->codeFunc($1, 1);
507 		delete $1; }
508 	| BLTINNOARGSORONE expr	{
509 		g_lingo->codeFunc($1, 1);
510 		delete $1; }
511 	| BLTINNOARGSORONE 		{
512 		g_lingo->code1(g_lingo->c_voidpush);
513 		g_lingo->codeFunc($1, 1);
514 		delete $1; }
515 	| BLTINARGLIST nonemptyarglist			{ g_lingo->codeFunc($1, $2); }
516 	| BLTINARGLIST '(' nonemptyarglist ')'	{ g_lingo->codeFunc($1, $3); }
517 	| tME '(' ID ')'				{ g_lingo->codeMe($3, 0); }
518 	| tME '(' ID ',' arglist ')'	{ g_lingo->codeMe($3, $5); }
519 	| tOPEN expr tWITH expr	{ g_lingo->code1(g_lingo->c_open); }
520 	| tOPEN expr 			{ g_lingo->code2(g_lingo->c_voidpush, g_lingo->c_open); }
521 	| TWOWORDBUILTIN ID arglist	{ Common::String s(*$1); s += '-'; s += *$2; g_lingo->codeFunc(&s, $3); }
522 	;
523 
524 globallist: ID					{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString($1->c_str()); delete $1; }
525 	| globallist ',' ID			{ g_lingo->code1(g_lingo->c_global); g_lingo->codeString($3->c_str()); delete $3; }
526 	;
527 
528 propertylist: ID				{ g_lingo->code1(g_lingo->c_property); g_lingo->codeString($1->c_str()); delete $1; }
529 	| propertylist ',' ID		{ g_lingo->code1(g_lingo->c_property); g_lingo->codeString($3->c_str()); delete $3; }
530 	;
531 
532 instancelist: ID				{ g_lingo->code1(g_lingo->c_instance); g_lingo->codeString($1->c_str()); delete $1; }
533 	| instancelist ',' ID		{ g_lingo->code1(g_lingo->c_instance); g_lingo->codeString($3->c_str()); delete $3; }
534 	;
535 
536 // go {to} {frame} whichFrame {of movie whichMovie}
537 // go {to} {frame "Open23" of} movie whichMovie
538 // go loop
539 // go next
540 // go previous
541 // go to {frame} whichFrame {of movie whichMovie}
542 // go to {frame whichFrame of} movie whichMovie
543 
544 gotofunc: tGO tLOOP				{ g_lingo->code1(g_lingo->c_gotoloop); }
545 	| tGO tNEXT					{ g_lingo->code1(g_lingo->c_gotonext); }
546 	| tGO tPREVIOUS				{ g_lingo->code1(g_lingo->c_gotoprevious); }
547 	| tGO gotoframe 			{
548 		g_lingo->codeConst(1);
549 		g_lingo->code1(g_lingo->c_goto); }
550 	| tGO gotoframe gotomovie	{
551 		g_lingo->codeConst(3);
552 		g_lingo->code1(g_lingo->c_goto); }
553 	| tGO gotomovie				{
554 		g_lingo->codeConst(2);
555 		g_lingo->code1(g_lingo->c_goto); }
556 	;
557 
558 gotoframe: tFRAME expr
559 	| expr
560 	;
561 
562 gotomovie: tOF tMOVIE expr
563 	| tMOVIE expr
564 	;
565 
566 playfunc: tPLAY tDONE			{ g_lingo->code1(g_lingo->c_playdone); }
567 	| tPLAY gotoframe 			{
568 		g_lingo->codeConst(1);
569 		g_lingo->code1(g_lingo->c_play); }
570 	| tPLAY gotoframe gotomovie	{
571 		g_lingo->codeConst(3);
572 		g_lingo->code1(g_lingo->c_play); }
573 	| tPLAY gotomovie				{
574 		g_lingo->codeConst(2);
575 		g_lingo->code1(g_lingo->c_play); }
576 	| tPLAYACCEL { g_lingo->codeSetImmediate(true); } arglist	{
577 		g_lingo->codeSetImmediate(false);
578 		g_lingo->codeFunc($1, $3); }
579 	;
580 
581 // macro
582 //
583 // Special Note  The macro keyword is retained in Director 3.0 to maintain compatibility
584 // with scripts developed under Version 2.0. When writing new scripts, or editing old
585 // scripts, you should use handlers instead of macros. (Handlers are defined with the on keyword.)
586 //
587 // Syntax:
588 //
589 // --  [comment]
590 // macro macroName [argument1] [, argument2]
591 // [, argument3]
592 // [statement1]
593 // [statement2]
594 //
595 // Keyword.  Defines a macro. A macro is a multiple-line script defined
596 // in the Text window. Macros can accept arguments (inputs) and
597 // optionally return a result. Macros can call other macros and can be
598 // called from any other script or factory.
599 //
600 // The first line of a castmember in the Text window that contains a macro must be
601 // a comment (--). You can define more than one macro in a given text castmember.
602 // The macro definition ends where the next macro (or factory) begins.
603 //
604 // See also:
605 //   on keyword
606 defn: tMACRO ID { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); }
607 		begin argdef nl argstore stmtlist 		{
608 			g_lingo->code1(g_lingo->c_procret);
609 			g_lingo->define(*$2, $4, $5);
610 			g_lingo->_indef = false; }
611 	| tFACTORY ID	{
612 			g_lingo->codeFactory(*$2);
613 		}
614 	| tMETHOD ID { g_lingo->_indef = true; }
615 		begin argdef nl argstore stmtlist 		{
616 			g_lingo->code1(g_lingo->c_procret);
617 			g_lingo->define(*$2, $4, $5 + 1, &g_lingo->_currentFactory);
618 			g_lingo->_indef = false; }	;
619 	| on begin  argdef nl argstore stmtlist ENDCLAUSE {	// D3
620 				g_lingo->code1(g_lingo->c_procret);
621 				g_lingo->define(*$1, $2, $3);
622 				g_lingo->_indef = false;
623 				g_lingo->_ignoreMe = false;
624 
625 				checkEnd($7, $1->c_str(), false);
626 			}
627 	| on begin argdef nl argstore stmtlist {	// D4. No 'end' clause
628 				g_lingo->code1(g_lingo->c_procret);
629 				g_lingo->define(*$1, $2, $3);
630 				g_lingo->_indef = false;
631 				g_lingo->_ignoreMe = false;
632 			}
633 
634 on:  tON ID { $$ = $2; g_lingo->_indef = true; g_lingo->_currentFactory.clear(); g_lingo->_ignoreMe = true; }
635 
636 argdef:  /* nothing */ 		{ $$ = 0; }
637 	| ID					{ g_lingo->codeArg($1); $$ = 1; }
638 	| argdef ',' ID			{ g_lingo->codeArg($3); $$ = $1 + 1; }
639 	| argdef nl ',' ID		{ g_lingo->codeArg($4); $$ = $1 + 1; }
640 	;
641 
642 argstore:	  /* nothing */		{ g_lingo->codeArgStore(); }
643 	;
644 
645 
646 macro: ID nonemptyarglist	{
647 		g_lingo->code1(g_lingo->c_call);
648 		g_lingo->codeString($1->c_str());
649 		inst numpar = 0;
650 		WRITE_UINT32(&numpar, $2);
651 		g_lingo->code1(numpar); }
652 	;
653 
654 arglist:  /* nothing */ 	{ $$ = 0; }
655 	| expr					{ $$ = 1; }
656 	| arglist ',' expr		{ $$ = $1 + 1; }
657 	;
658 
659 nonemptyarglist:  expr			{ $$ = 1; }
660 	| nonemptyarglist ',' expr	{ $$ = $1 + 1; }
661 	;
662 
663 %%
664