source: cpp/frams/vm/framscript.y

Last change on this file was 846, checked in by Maciej Komosinski, 3 months ago
  • Added support for ternary conditional operator a?b:c
Fixed incorrect stack allocation in some cases of &&
, and for(x in null)
&& and
operators always return one of the input expressions (previously the result was in some cases simplified to 0 or 1)
  • Property svn:eol-style set to native
File size: 52.3 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2015  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5%{
6#include "framscript-defs.h"
7#include "common/log.h"
8#include <math.h>
9#include <ctype.h>
10#include <stdio.h>
11
12#define YYERROR_VERBOSE
13#define YYPRINT(file,type,value) yyprint (file,type,value)
14
15enum NameKind { NameNotFound, VariableName, GlobalName, ConstName };
16static const char* name_kind_names[]={"","var","global","const"};
17
18static void yyprint (FILE *file,int type,YYSTYPE value);
19void handleTwoArg(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2,
20                  int optoken,const char* opname, bool negarg2, bool uniq);
21bool handleCompare(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2,
22                   ExtValue::CmpOperator,const char* opname);
23bool handleAssignOp(YYSTYPE& result,const YYSTYPE& var,const YYSTYPE& arg,const char* opname);
24bool handleAssignOp2(YYSTYPE& result,const char *var,const YYSTYPE& arg,const char* opname,int stackpos,bool push);
25bool canAddName(const SString &name,NameKind kind);
26bool variableOk(TokenValue &tok, const TokenValue& var,int &loc);
27int variableNameOk(const SString &name);
28bool globalOk(const TokenValue& var);
29bool globalNameOk(const SString& name);
30void badVariable(TokenValue &tok, const TokenValue &var);
31bool evalVariable(TokenValue &tok, const TokenValue &var);
32bool doBreak(int level);
33bool doContinue(int level);
34void warnTruthValue(const TokenValue& t);
35void outFunName(const TokenValue& t);
36static bool resultIsRelaxedEqual(ExtValue::CompareResult res);
37
38static const char* assign_op_names[]={"add","sub","mul","div","mod"};
39
40%}
41
42%token_table
43
44%token CONSTANT
45%token INVALID_NUMBER
46
47%nonassoc ASSIGN_ADD ASSIGN_SUB ASSIGN_MUL ASSIGN_DIV ASSIGN_MOD
48%nonassoc PLUSPLUS MINUSMINUS
49%left LOGIC_AND LOGIC_OR '!'
50%left EQUAL NOT_EQUAL GEQUAL LEQUAL '>' '<'
51%left '|' '&' '^'
52%left '-' '+'
53%left '*' '/' '%'
54%left NEG     /* negation--unary minus */
55%left TYPEOF
56%left INT_TYPE
57%left FLOAT_TYPE
58%left STRING_TYPE
59
60%token IDENT
61%token OBJNAME
62
63%token IF      "if"
64%token ELSE    "else"
65%token FOR     "for"
66%token INNN    "in"
67%token WHILE   "while"
68%token DO      "do"
69%token GOTO    "goto"
70%token RETURN  "return"
71%token BREAK    "break"
72%token CONTINUE "continue"
73%token SWITCH   "switch"
74%token CASE     "case"
75%token DEFAULT  "default"
76
77%token TYPEOF      "typeof"
78%token INT_TYPE    "int"
79%token FLOAT_TYPE  "float"
80%token STRING_TYPE "string"
81
82%token ASM     
83%token ASMLINE
84             
85%token VAR      "var"
86%token CONSTDEF "const"
87%token GLOBAL   "global"
88%token FUNCTION "function"
89
90%token CALL    "call"
91
92%token ARROW
93
94%token ASSIGN
95%token ASSIGN_ADD
96%token ASSIGN_SUB
97%token ASSIGN_MUL
98%token ASSIGN_DIV
99
100%token EQUAL
101%token NOT_EQUAL
102%token GEQUAL
103%token LEQUAL
104
105%token LOGIC_AND
106%token LOGIC_OR
107
108%token PLUSPLUS
109%token MINUSMINUS
110
111%token LSHIFT
112%token RSHIFT
113
114%%
115code: {$$.setInt(trstack.currentPos());} recurcode
116{
117int pos=$1.getInt();
118if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos());
119}
120;
121
122recurcode:    /* empty string */
123       | recurcode statement
124;
125     
126statement: ';'
127      | VAR vardeflist ';'
128      | CONSTDEF constdeflist ';'
129      | GLOBAL globaldeflist ';'
130      | IDENT ':'     {trctx.out->printf(":%s\n",str($1));}
131      | expr ';'      {if (!$1.constant) { trctx.out->printf("inc m0\n"); trstack.adjust(+1); } trctx.emitLine(); }
132      | functiondef
133      | blok
134      | if_statement
135      | goto_statement
136      | return_statement
137      | for_statement
138      | while_statement
139      | dowhile_statement
140      | break_statement
141      | continue_statement
142      | switch_statement
143//      | error ';'
144      | asmblock
145;
146
147asmblock: ASM asmlines '}'
148;
149
150asmlines: /* empty */
151        | ASMLINE            {trctx.out->Vputs(str($1));trctx.out->Vputc('\n');}
152        | asmlines ASMLINE   {trctx.out->Vputs(str($2));trctx.out->Vputc('\n');}
153;
154
155goto_statement: GOTO IDENT ';'
156 {
157#ifdef FRAMSCRIPT_GOTO
158trctx.out->printf("jump :%s\n",str($2)); logPrintf("FramScriptCompiler","translate",LOG_WARN,"goto is not recommended"); trctx.emitLine();
159#else
160trctx.err->printf("goto is not supported\n");return 1;
161#endif
162 }
163;
164
165return_statement: RETURN expr ';'
166{
167int offset;
168if (trctx.functionstackpos==TranslatorContext::NOT_IN_FUNCTION)
169        offset=-trstack.currentPos();
170else
171        offset=trctx.functionstackpos-trstack.currentPos();
172if (!offset)
173        {
174        if ($2.constant)
175                trctx.out->printf("move %s,s0\nreturn\n",litstr($2));
176        else
177                {
178                trctx.out->printf("move m[m0++],s0\nreturn\n");
179                trstack.adjust(+1);
180                }
181        }
182else
183        {
184        if ($2.constant)
185                {
186                trctx.out->printf("add %d,m0\nmove %s,s0\nreturn\n",offset,litstr($2));
187                trstack.adjust(offset);
188                }
189        else
190                {
191                trctx.out->printf("move s0,s%d\nadd %d,m0\nreturn\n",offset,offset);
192                trstack.adjust(offset);
193                }
194        }
195}
196          | RETURN ';'
197{
198int offset;
199if (trctx.functionstackpos==TranslatorContext::NOT_IN_FUNCTION)
200        offset=-trstack.currentPos();
201else
202        offset=trctx.functionstackpos-trstack.currentPos();
203trctx.emitLine();
204if (!offset)
205        trctx.out->printf("move invalid,s0\nreturn\n");
206else
207        trctx.out->printf("add %d,m0\nmove invalid,s0\nreturn\n",offset);
208}
209;
210
211vardeflist: vardef
212          | vardeflist ',' vardef
213;
214
215vardef: IDENT               { trctx.emitLine(); if (!canAddName($1.getString(),VariableName)) return 1; trstack.addVariable($1.getString()); trctx.out->printf("push invalid\n"); }
216      | IDENT '=' stackexpr { trctx.emitLine(); if (!canAddName($1.getString(),VariableName)) return 1; trstack.adjust(1); trstack.addVariable($1.getString());}
217;
218
219constdeflist: constdef
220          | constdeflist ',' constdef
221;
222
223constdef: IDENT '=' expr        { trctx.emitLine(); if (!canAddName($1.getString(),ConstName)) return 1; if (!$3.constant) {trctx.err->printf("const expression must be constant");return 1;} trstack.addConstant($1.getString(),$3); }
224;
225
226globaldeflist: globaldef
227          | globaldeflist ',' globaldef
228;
229
230globaldef: IDENT     { if (!canAddName($1.getString(),GlobalName)) return 1; trstack.globals.add($1.getString(),0); trctx.out->printf("global %s\n",str($1));}
231;
232
233funparam: IDENT { trstack.addVariable($1.getString()); };
234
235paramlist: /* empty */             {$$.setInt(0); }
236         | funparam                {$$.setInt(1); }
237         | paramlist ',' funparam  {$$.setInt($1.getInt()+1);}
238;
239
240funnamelist:
241         IDENT {outFunName($1);}
242         | funnamelist ',' IDENT {outFunName($3);}
243;
244
245functiondef:                FUNCTION funnamelist
246{
247trctx.emitLine();
248int pos=trstack.currentPos();
249$$.setInt(pos);
250if (trctx.functionstackpos!=TranslatorContext::NOT_IN_FUNCTION)
251        {trctx.err->printf("functions cannot be nested\n");return 1;}
252trctx.beforefunctionstackpos=trstack.currentPos();
253}
254                            '(' paramlist ')'
255{
256trctx.functionstackpos=trstack.currentPos();
257}
258                            blok
259{trctx.out->printf("move invalid,s0\nreturn\n");
260int pos=$3.getInt();
261trstack.dropToPos(pos);
262trctx.functionstackpos=TranslatorContext::NOT_IN_FUNCTION;
263trctx.beforefunctionstackpos=TranslatorContext::NOT_IN_FUNCTION;
264trctx.out->printf(":_skipfun_%d\n",trctx.functiontmplabel);
265trctx.functiontmplabel=-1;
266trctx.emitLine();
267};
268
269break_statement: BREAK ';'         {if (!doBreak(1)) return 1;}
270               | BREAK expr ';'
271{
272trctx.emitLine();
273if (!$2.constant)
274        {trctx.err->printf("break level must be a constant expression\n");return 1;}
275int level=$2.getInt();
276if (level<1)
277        {trctx.err->printf("break level must be a positive integer\n");return 1;}
278if (!doBreak(level)) return 1;
279trctx.emitLine();
280};
281
282continue_statement: CONTINUE ';'         {if (!doContinue(1)) return 1;}
283                  | CONTINUE expr ';'
284{
285if (!$2.constant)
286        {trctx.err->printf("continue level must be a constant expression\n");return 1;}
287int level=$2.getInt();
288if (level<1)
289        {trctx.err->printf("continue level must be a positive integer\n");return 1;}
290if (!doContinue(level)) return 1;
291trctx.emitLine();
292};
293
294while_statement: WHILE '('
295{
296int c=trctx.labelcounter++; $$.setInt(c);
297$$.stack=trstack.currentPos();
298trstack.loops.addLoop(c,$$.stack);
299trctx.out->printf(":_loop_%d\n",c);}
300                              expr ')'
301{
302int c=$3.getInt();
303warnTruthValue($4);
304if ($4.constant)
305        {if (!$4.getInt()) trctx.out->printf("jump :_loop_end_%d\n",c);}
306else
307        {
308        trctx.out->printf("if ~=,m[m0++],:_loop_end_%d\n",c,c);
309        trstack.adjust(+1);
310        }
311}
312                 pseudoblok_statement
313{
314trctx.out->printf("jump :_loop_%d\n:_loop_end_%d\n",$3.getInt(),$3.getInt());
315trstack.adjust($3.stack-trstack.currentPos());
316trstack.loops.drop();
317}
318;
319
320dowhile_statement: DO
321{
322trctx.emitLine();
323int c=trctx.labelcounter++; $$.setInt(c);
324$$.stack=trstack.currentPos();
325trstack.loops.addLoop(c,$$.stack);
326trctx.out->printf(":_loop_%d\n",c);} //2
327
328pseudoblok_statement WHILE '(' expr ')'
329
330{//8
331int c=$2.getInt();
332warnTruthValue($6);
333if ($6.constant)
334        {if ($6.getInt()) trctx.out->printf("jump :_loop_%d\n",c);}
335else
336        {
337        trctx.out->printf("if !~,m[m0++],:_loop_%d\n",c);
338        trstack.adjust(+1);
339        }
340trctx.out->printf(":_loop_end_%d\n",c);
341trstack.adjust($2.stack-trstack.currentPos());
342trstack.loops.drop();
343trctx.emitLine();
344}
345;
346
347switch_statement: SWITCH '('
348{
349int c=trctx.labelcounter++; $1.setInt(c);
350trstack.loops.addLoop(c,trstack.currentPos());}
351       stackexpr ')'
352{trctx.emitLine(); trctx.out->printf("dec m0\n"); trstack.adjust(-1);}
353 '{' inside_switch '}'
354{
355trctx.emitLine();
356LoopInfo *li=trstack.loops.getLoop(0);
357trctx.out->printf(":_case_after_%d_%d\n"
358                  "add 2,m0\n"
359                  ":_loop_end_%d\n",
360                  li->id,li->casecounter,
361                  li->id);
362trstack.adjust(+2);
363trstack.loops.drop();
364}
365;
366
367inside_switch: /* empty */
368       | case_label
369       | inside_switch case_label
370;
371
372case_label: CASE expr ':'
373{
374LoopInfo *li=trstack.loops.getLoop(0);
375if ($2.constant)
376        trctx.out->printf("if s1,!=,%s,:_case_before_%d_%d\n",
377                          litstr($2),
378                          li->id,li->casecounter+1);
379else
380        {
381        trctx.out->printf("if s2,!=,m[m0++],:_case_before_%d_%d\n",
382                          li->id,li->casecounter+1);
383        trstack.adjust(+1);
384        }
385trctx.out->printf(":_case_after_%d_%d\n",
386                  li->id,li->casecounter);
387int pos=trstack.currentPos(); $$.setInt(pos);
388}
389 recurcode
390{
391trctx.emitLine();
392LoopInfo *li=trstack.loops.getLoop(0);
393int pos=$4.getInt();
394if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos());
395trstack.dropToPos(pos);
396li->casecounter++;
397trctx.out->printf("jump :_case_after_%d_%d\n"
398                  ":_case_before_%d_%d\n",
399                  li->id,li->casecounter,
400                  li->id,li->casecounter);
401}
402      |  DEFAULT ':'
403  {
404  LoopInfo *li=trstack.loops.getLoop(0);
405  trctx.out->printf(":_case_after_%d_%d\n",li->id,li->casecounter);
406  }
407  recurcode
408  {
409  LoopInfo *li=trstack.loops.getLoop(0);
410  li->casecounter++;
411  }
412;
413
414newvar_or_expr:
415              VAR IDENT { $$.setInt(trstack.addVariable($2.getString())); trctx.out->printf("push invalid\n"); $$.ident=true; $$.var=true; }
416
417              |
418              VAR IDENT '=' stackexpr
419              {
420              //trctx.out->printf("# VAR IDENT '=' stackexpr pos=%d\n",trstack.currentPos());
421              trstack.adjust(+1);
422              $$.setInt(trstack.addVariable($2.getString()));
423              $$.ident=true; $$.var=true;
424              }
425
426              |
427              expr_special_ident
428              {
429              $$=$1;
430              }
431
432              | //nic
433              {
434              $$.setInt(1); $$.assign=false; $$.ident=false; $$.var=false; $$.constant=true;
435              }
436;
437
438expr_or_objname:
439              expr { $$=$1; $$.objname=false; }
440              |
441              OBJNAME { $$.setString($1.getString()); $$.objname=true; }
442;
443
444for_statement_begin: FOR '('
445{
446int c=trctx.labelcounter++; $$.counter=c; $$.stack=trstack.currentPos();
447}
448newvar_or_expr
449{
450$$=$4; $$.counter=$3.counter; $$.stack=$3.stack;
451};
452
453for_statement:
454
455           ///////////  for(in) ...  ////////////
456           for_statement_begin INNN
457           {//3
458           if (!$1.ident)
459                   {
460                   trctx.err->printf("for(... in ...) requires a variable\n");
461                   return 1;
462                   }
463           int loc;
464           if ($1.var) // for(var x[=expr] in
465                   $$.setInt($1.getInt());
466           else
467                   {  // for(x in
468                   if (variableOk($$,$1,loc))
469                           $$.setInt(loc);
470                   else if (globalOk($1))
471                           {
472                           trctx.err->printf("global '%s' can't be the iterating variable in 'for'\n",str($1));
473                           return 1;
474                           }
475                   else
476                           {
477                           badVariable($$,$1);
478                           return 1;
479                           }
480                   }
481           $$.stack=trstack.currentPos();
482           }
483           expr_or_objname ')'
484           {//6
485           trctx.emitLine();
486           if ($4.constant)
487                   {
488                   logPrintf("", "", LOG_WARN, "%s can't be iterated",str($4));
489                   trctx.out->printf("jump :_loop_end_%d\n",$1.counter);
490                   }
491           trstack.adjust(-1);
492           trstack.loops.addLoop($1.counter,trstack.currentPos());
493           if ($4.objname)
494                   trctx.out->printf("dec m0\nmove %s.iterator,m[m0]\n",$4.getString().c_str());
495           else
496                   trctx.out->printf("move s%d,m1\ndec m0\nif ~=,m1,:_loop_end_%d\nmove [m1].\"iterator\",m[m0]\n",0,$1.counter);
497           // s0=iterator s1=obj (=obj.iterator)
498           trctx.out->printf(":_loop1_%d\n",$1.counter);
499           trctx.out->printf(":_loop_%d\n",$1.counter);
500           trctx.out->printf("move s0,m1\nmove [m1].\"next\",m2\n");
501           trctx.out->printf("if ~=,m2,:_loop_end_%d\n",$1.counter);
502           trctx.out->printf("move [m1].\"value\",s%d\n",$3.getInt()-trstack.currentPos());
503           }
504           pseudoblok_statement
505           {
506           trctx.out->printf("jump :_loop1_%d\n",$1.counter);
507           trctx.out->printf(":_loop_end_%d\n",$1.counter);
508           trstack.loops.drop();
509           if ($4.constant)
510                   trstack.adjust($3.stack-trstack.currentPos());
511           int adj=$1.stack-trstack.currentPos();
512           trstack.adjust(adj);
513           if (adj!=0)
514                   trctx.out->printf("add %d,m0\n",adj);
515           }
516           
517|
518
519           ///////////  for(;;) ...  ////////////
520           for_statement_begin ';'
521           { //3
522           trctx.emitLine();
523           //trctx.out->printf("# for_statement_begin pos=%d ident=%d var=%d\n",trstack.currentPos(),$1.ident,$1.var);
524           if ((!$1.var) && ($1.ident))
525                   {  // for(x;
526                   int loc;
527                   if ((!variableOk($$,$1,loc)) || (globalOk($1)))
528                           {
529                           badVariable($$,$1);
530                           return 1;
531                           }
532                   }
533           if (!$1.constant && !$1.ident)
534                   {
535                   trctx.out->printf("inc m0\n");
536                   trstack.adjust(+1);
537                   }
538           trstack.loops.addLoop($1.counter,trstack.currentPos());
539           trctx.out->printf(":_loop1_%d\n",$1.counter);
540           //trctx.out->printf("# expr#2\n");
541           }
542           expr_or_empty ';'
543           { //6
544           trctx.emitLine();
545           int c=$1.counter;
546           warnTruthValue($4);
547           if ($4.constant)
548                   {if (!$4.getInt()) trctx.out->printf("jump :_loop_end_%d\n",c);}
549           else
550                   {
551                   trctx.out->printf("if m[m0++],==,0,:_loop_end_%d\n",c,c);
552                   trstack.adjust(+1);
553                   }
554           trctx.tmp="";
555           trctx.divertOut();
556           //trctx.out->printf("# expr#3\n");
557           }
558           expr_or_empty ')'
559           { //9
560           trctx.emitLine();
561           if (!$7.constant) { trctx.out->printf("inc m0\n"); trstack.adjust(+1); }
562           trctx.restoreOut();
563           $$.setString(trctx.tmp.c_str());
564           //trctx.out->printf("# pseudoblok_statement pos=%d\n",trstack.currentPos());
565           }
566           pseudoblok_statement
567           {//11
568           trctx.out->printf(":_loop_%d\n",$1.counter);
569           LoopInfo* li=trstack.loops.getLoop(0);
570           if (li->location != trstack.currentPos())
571                   trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos());
572           trctx.out->printf(str($9));
573           if (li->location != trstack.currentPos())
574                   trctx.out->printf("sub %d,m0\n",li->location-trstack.currentPos());
575           trctx.out->printf("jump :_loop1_%d\n:_loop_end_%d\n",$1.counter,$1.counter);
576           if ($1.stack != trstack.currentPos())
577                   trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos());
578           trstack.adjust($1.stack-trstack.currentPos());
579           trstack.loops.drop();
580           }
581;
582
583pseudoblok_statement:
584{trctx.emitLine(); int pos=trstack.currentPos(); $$.setInt(pos);}
585  statement
586{
587int pos=$1.getInt();
588if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos());
589trstack.dropToPos(pos);
590trctx.emitLine();
591};
592
593if_statement:
594 if_condition pseudoblok_statement
595                       {
596                       if ($1.stack!=trstack.currentPos())
597                               trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos());
598                       trstack.adjust(trstack.currentPos()-$1.stack);
599                       trctx.out->printf("jump :_if_end_%d\n:_if_else_%d\n",$1.getInt(),$1.getInt());
600                       }
601         ELSE
602                       {trstack.adjust($1.stack-trstack.currentPos());}
603         pseudoblok_statement
604                       {
605                       if ($1.stack!=trstack.currentPos())
606                               trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos());
607                       trstack.adjust(trstack.currentPos()-$1.stack);
608                       trctx.out->printf(":_if_end_%d\n",$1.getInt());
609                       }
610|
611 if_condition pseudoblok_statement
612                       {
613                       if ($1.stack!=trstack.currentPos())
614                               trctx.out->printf("add %d,m0\n",$1.stack-trstack.currentPos());
615                       trstack.dropToPos($1.stack);
616                       trctx.out->printf(":_if_else_%d\n",$1.getInt());
617                       }
618;
619
620if_condition: IF
621{$$.stack=trstack.currentPos();trctx.emitLine();}
622
623 '(' expr ')'
624{
625trctx.emitLine();
626int c=trctx.labelcounter++;
627$$.setInt(c);
628warnTruthValue($4);
629if ($4.constant)
630        {
631        if (!$4.getInt()) trctx.out->printf("jump :_if_else_%d\n",c);
632        }
633else
634        {
635        trctx.out->printf("if ~=,m[m0++],:_if_else_%d\n",c);
636        trstack.adjust(+1);
637        }
638$$.stack=$2.stack;
639};
640
641blok:    '{'
642{ int pos=trstack.currentPos();
643$$.setInt(pos);
644}
645         recurcode '}'
646{
647int pos=$2.getInt();
648if (pos!=trstack.currentPos()) trctx.out->printf("add %d,m0\n",pos-trstack.currentPos());
649trstack.dropToPos(pos);
650}
651
652assign_op: ASSIGN_ADD {$$.setInt(0);}
653         | ASSIGN_SUB {$$.setInt(1);}
654         | ASSIGN_MUL {$$.setInt(2);}
655         | ASSIGN_DIV {$$.setInt(3);}
656         | ASSIGN_MOD {$$.setInt(4);}
657
658plusminus: PLUSPLUS {$$.setInt(1);} | MINUSMINUS {$$.setInt(0);}
659
660expr: expr_special_ident
661  {
662  //trctx.out->printf("# expr: ident=%d str=%s\n",$1.ident,(const char*)$1.getString());
663  if ($1.ident)
664          {
665          if (evalVariable($$,$1))
666                  $$.constant=false;
667          else
668                  return 1;
669          }
670  else
671          {$$=$1; $$.ident=false;}
672  trctx.emitLine();
673  }
674;
675
676stackexpr: expr {if ($1.constant) {trctx.out->printf("push %s\n",litstr($1)); trstack.adjust(-1); $$.constant=0;} }
677
678expr_or_empty:
679         expr {$$=$1;}
680
681         | //nic
682         { $$.setInt(1); $$.assign=false; $$.constant=true; $$.ident=false; $$.ident=false; }
683;
684
685expr_special_ident:    CONSTANT             { $$=$1; $$.constant=1; $$.ident=0; }
686
687       | IDENT                {
688                              ExtValue c;
689                              if (trstack.getConstant($1.getString(),c))
690                                      { $$=c; $$.constant=1; $$.ident=0; }
691                              else
692                                      { $$.ident=true; $$.setString($1.getString()); }
693                              }
694
695       | OBJNAME ':' IDENT    {$$.constant=0; $$.ident=0;
696                              trctx.out->printf("push %s:%s\n",$1.getString().c_str(),
697                                                 $3.getString().c_str());
698                              trstack.adjust(-1);
699                              }
700       | plusminus IDENT
701{
702trctx.emitLine();
703$$.ident=0;
704int loc; if (variableOk($$,$2,loc))
705        { loc-=trstack.currentPos();
706        trctx.out->printf("%s s%d\npush s%d\n",$1.getInt()?"inc":"dec",loc,loc);
707        trstack.adjust(-1);}
708        else if (globalOk($2))
709        { trctx.out->printf("%s @%s\npush @%s\n",$1.getInt()?"inc":"dec",str($2),str($2));
710        trstack.adjust(-1);}
711        else {badVariable($$,$2); return 1;}
712}
713
714       | IDENT plusminus
715{
716trctx.emitLine();
717$$.ident=0;
718int loc; if (variableOk($$,$1,loc))
719        {loc-=trstack.currentPos(); trctx.out->printf("push s%d\n%s s%d\n",loc,$2.getInt()?"inc":"dec",loc+1);
720        trstack.adjust(-1);}
721        else if (globalOk($1))
722        { trctx.out->printf("push @%s\n%s @%s\n",$1.getString().c_str(),
723                            $2.getInt()?"inc":"dec",$1.getString().c_str());
724        trstack.adjust(-1);}
725        else {badVariable($$,$1); return 1;}
726}
727
728       | IDENT assign_op expr { trctx.emitLine(); $$.ident=0;
729                                if (!handleAssignOp($$,$1,$3,assign_op_names[$2.getInt()]))
730                                if (globalOk($1)) {SString t="@"; t+=$1.getString();
731                                  handleAssignOp2($$,t.c_str(),$3,assign_op_names[$2.getInt()],0,1);}
732                                else { badVariable($$,$1); return 1; }
733                              }
734
735       | TYPEOF '(' expr ')' { trctx.emitLine(); $$.ident=0;
736                       if ($3.constant)
737                             {$$.constant=1; $$=$3.getExtType();}
738                       else
739                             {trctx.out->printf("type s0,s0\n");}
740                     }
741       | INT_TYPE '(' expr ')' { trctx.emitLine(); $$.ident=0;
742                       if ($3.constant)
743                             {$$.constant=1; $$=ExtValue($3.getInt());}
744                       else
745                             {trctx.out->printf("conv 1,s0\n");}
746                     }
747       | FLOAT_TYPE '(' expr ')' { trctx.emitLine(); $$.ident=0;
748                       if ($3.constant)
749                             {$$.constant=1; $$=ExtValue($3.getDouble());}
750                       else
751                             {trctx.out->printf("conv 2,s0\n");}
752                     }
753       | STRING_TYPE '(' expr ')' { trctx.emitLine(); $$.ident=0;
754                       if ($3.constant)
755                             {$$.constant=1; $$=ExtValue($3.getString());}
756                       else
757                             {trctx.out->printf("conv 3,s0\n");}
758                     }
759
760       | expr '+' expr { handleTwoArg($$,$1,$3,'+',"add",0,1); }
761       | expr '-' expr { handleTwoArg($$,$1,$3,'-',"sub",0,0); }
762       | expr '*' expr { handleTwoArg($$,$1,$3,'*',"mul",0,1); }
763       | expr '/' expr { handleTwoArg($$,$1,$3,'/',"div",0,0); }
764       | expr '&' expr { handleTwoArg($$,$1,$3,'&',"and",0,0); }
765       | expr '|' expr { handleTwoArg($$,$1,$3,'|',"or",0,0); }
766       | expr '%' expr { handleTwoArg($$,$1,$3,'%',"mod",0,0); }
767
768       | expr LOGIC_AND
769 {
770 // a && b:
771 //   push a
772 //   if (a)
773 //     pop; goto and_b
774 //   else
775 //     goto and_end
776 // and_b:
777 //   push b
778 // and_end:
779 trctx.emitLine();
780// trctx.out->printf("\n####### logic AND\n");
781 int c=trctx.labelcounter++;
782 $$.counter=c;
783 if ($1.constant)
784         {
785         ExtValue::CompareResult cond=$1.compare(ExtValue::zero());
786         if (resultIsRelaxedEqual(cond))
787                 {
788                 $1.counter=0;
789                 trctx.out->printf("jump :_and_end_%d\n",c);
790                 }
791         else
792                 $1.counter=1;
793         }
794 else
795         {
796         trctx.out->printf("if !~,m[m0],:_and_b_%d\n" // skip to b if a!~=false, "a" stays on top
797                           "jump :_and_end_%d\n"
798                           ":_and_b_%d\n"
799                           ,c,c,c);
800         }
801 }
802         expr
803 {
804 if ($1.constant)
805         {
806         if ($1.counter==0)
807                 {
808                 $$=$1;
809                 if (!$4.constant)
810                         trstack.adjust(+1);
811                 }
812         else
813                 $$=$4;
814         }
815 else
816         {
817         $$.ident=false;
818         $$.constant=0;
819         if ($4.constant)
820                 {
821                 trctx.out->printf("inc m0\n"
822                                   "push %s\n",litstr($4));
823                 }
824         else
825                 {
826                 trstack.adjust(+1);
827                 trctx.out->printf("move m[m0],m[m0+1]\n"
828                                   "inc m0\n"); //drop "a"
829                 }
830         }
831 trctx.out->printf(":_and_end_%d\n",$3.counter);
832// trctx.out->printf("#################\n\n");
833 }
834
835       | expr LOGIC_OR
836 {
837 // a || b:
838 //   push a
839 //   if (!a)
840 //     pop; goto or_b
841 //   else
842 //     goto or_end
843 // or_b:
844 //   push b
845 // or_end:
846 trctx.emitLine();
847// trctx.out->printf("\n####### logic OR\n");
848 int c=trctx.labelcounter++;
849 $$.counter=c;
850 if ($1.constant)
851         {
852         ExtValue::CompareResult  cond=$1.compare(ExtValue::zero());
853         if (!resultIsRelaxedEqual(cond))
854                 {
855                 $1.counter=1;
856                 trctx.out->printf("jump :_or_end_%d\n",c);
857                 }
858         else
859                 $1.counter=0;
860         }
861 else
862         {
863         trctx.out->printf("if ~=,m[m0],:_or_b_%d\n" // skip to b if a~=false, "a" stays on top
864                           "jump :_or_end_%d\n"
865                           ":_or_b_%d\n"
866                           ,c,c,c);
867         }
868 }
869         expr
870 {
871 if ($1.constant)
872         {
873         if ($1.counter==1)
874                 {
875                 $$=$1;
876                 if (!$4.constant)
877                         trstack.adjust(+1);
878                 }
879         else
880                 $$=$4;
881         }
882 else
883         {
884         $$.ident=false;
885         $$.constant=0;
886         if ($4.constant)
887                 {
888                 trctx.out->printf("inc m0\n"
889                                   "push %s\n",litstr($4));
890                 }
891         else
892                 {
893                 trstack.adjust(+1);
894                 trctx.out->printf("move m[m0],m[m0+1]\n"
895                                   "inc m0\n"); //drop "a"
896                 }
897         }
898 trctx.out->printf(":_or_end_%d\n",$3.counter);
899// trctx.out->printf("#################\n\n");
900 }
901
902       | expr '?'
903 {
904 trctx.emitLine();
905// trctx.out->printf("\n####### conditional operator\n");
906 $$.counter=trctx.labelcounter++;
907 warnTruthValue($1);
908 if ($1.constant)
909         {
910         ExtValue::CompareResult cond=$1.compare(ExtValue::zero());
911         $1.counter=0;
912         if (resultIsRelaxedEqual(cond))
913                 trctx.out->printf("jump :_cond_false_%d\n",$$.counter);
914         }
915 else
916         {
917         trstack.adjust(+1);
918         trctx.out->printf("if ~=,m[m0++],:_cond_false_%d\n",$$.counter);
919         }
920 $$.stack=trstack.currentPos();
921// trctx.out->printf("\n####### conditional - true\n");
922 }
923         stackexpr ':'
924 {
925 trctx.emitLine();
926 trctx.out->printf("jump :_cond_end_%d\n",$3.counter);
927 trctx.out->printf(":_cond_false_%d\n",$3.counter);
928// trctx.out->printf("\n####### conditional - false\n");
929 trstack.adjust($3.stack-trstack.currentPos());
930 }
931         stackexpr
932 {
933 trctx.emitLine();
934 trctx.out->printf(":_cond_end_%d\n",$3.counter);
935// trctx.out->printf("\n####### conditional - end\n");
936 $$.ident=false; $$.constant=0; $$.parens=0; $$.assign=0;
937 }
938         
939       | expr LSHIFT expr { handleTwoArg($$,$1,$3,LSHIFT,"shift",0,0); }
940       | expr RSHIFT expr { handleTwoArg($$,$1,$3,RSHIFT,"shift",1,0); }
941       | expr EQUAL expr     { if (!handleCompare($$,$1,$3,ExtValue::CmpEQ,"==")) return 1; }
942       | expr NOT_EQUAL expr { if (!handleCompare($$,$1,$3,ExtValue::CmpNE,"!=")) return 1; }
943       | expr GEQUAL expr    { if (!handleCompare($$,$1,$3,ExtValue::CmpGE,">=")) return 1; }
944       | expr LEQUAL expr    { if (!handleCompare($$,$1,$3,ExtValue::CmpLE,"<=")) return 1; }
945       | expr '>' expr       { if (!handleCompare($$,$1,$3,ExtValue::CmpGT,">")) return 1; }
946       | expr '<' expr       { if (!handleCompare($$,$1,$3,ExtValue::CmpLT,"<")) return 1; }
947
948       | '!' expr        {
949                         trctx.emitLine(); $$.assign=$2.assign; $$.parens=0; $$.ident=0;
950                         if ($2.constant)
951                                 {$$.constant=1; ExtValue::CompareResult res=$2.compare(ExtValue((paInt)0)); $$.setInt(resultIsRelaxedEqual(res));}
952                         else
953                                {trctx.out->printf("setif ~=,s0,s0\n");}
954                         }
955
956     | '-' expr %prec NEG {
957                          trctx.emitLine(); $$.assign=$2.assign; $$.parens=0; $$.ident=0;
958                          if ($2.constant)
959                                  { $$.constant=$2.constant;
960                                   if ($2.type==TInt) $$.setInt(-$2.getInt());
961                                   else if ($2.type==TDouble) $$.setDouble(-$2.getDouble());
962                                   else $$=$2;
963                                  }
964                             else
965                                  {
966                                  $$.constant=0; SString t="-"; t+=$2.getString(); $$.setString(t);
967                                  trctx.out->printf("mul -1,s0\n");
968                                  }
969                          }
970
971     | '(' expr ')'    { trctx.emitLine(); $$ = $2; $$.assign=$2.assign?(!$2.parens):0; $$.parens=1; $$.ident=0; }
972
973     | OBJNAME '.' member {
974                        trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
975                        if ($3.constant)
976                                {
977                                trctx.out->printf("push %s.%s\n",str($1),str($3)); trstack.adjust(-1);
978                                }
979                        else
980                                {
981                                trctx.out->printf("move s0,m1\nmove %s.[m1],s0\n",str($1));
982                                }
983                        }
984
985     | OBJNAME '.' member assign_op expr
986                  { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
987                  if ($3.constant)
988                          {
989                          handleAssignOp2($$,t.c_str(),$5,assign_op_names[$4.getInt()],0,1);
990                          }
991                  else
992                          {
993                          int sp=($5.constant)?0:1;
994                          t=$1.getString();t+=".[m1]";
995                          trctx.out->printf("move s0,m1\n",str($1));
996                          handleAssignOp2($$,t.c_str(),$5,assign_op_names[$4.getInt()],sp,0);
997                          if (sp) {trctx.out->printf("inc m0\n"); trstack.adjust(1);}
998                          }
999                  }
1000
1001     | plusminus OBJNAME '.' member {
1002                        trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$2.getString(); t+="."; t+=$4.getString(); $$.setString(t);
1003                        if ($4.constant)
1004                                {
1005                                trctx.out->printf("%s %s.%s\npush %s.%s\n",$1.getInt()?"inc":"dec",
1006                                                  str($2),str($4),str($2),str($4));
1007                                trstack.adjust(-1);
1008                                }
1009                        else
1010                                {
1011                                trctx.out->printf("move s0,m1\n%s %s.[m1]\nmove %s.[m1],s0\n",
1012                                                  $1.getInt()?"inc":"dec",str($2),str($2));
1013                                }
1014                        }
1015
1016     | OBJNAME '.' member plusminus {
1017                        trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1018                        if ($3.constant)
1019                                {
1020                                trctx.out->printf("push %s.%s\n%s %s.%s\n",
1021                                                  str($1),str($3),$4.getInt()?"inc":"dec",str($1),str($3));
1022                                trstack.adjust(-1);
1023                                }
1024                        else
1025                                {
1026                                trctx.out->printf("move s0,m1\nmove %s.[m1],s0\n%s %s.[m1]\n",
1027                                                  str($1),$4.getInt()?"inc":"dec",str($1));
1028                                }
1029                        }
1030
1031     | OBJNAME '.' '*'    {
1032                        trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".*"; $$.setString(t);
1033                        trctx.out->printf("push %s.*\n",str($1)); trstack.adjust(-1);
1034                        }
1035
1036
1037     | OBJNAME '.' member '=' expr {
1038                        trctx.emitLine(); $$=$5; $$.assign=1; $$.parens=0; $$.ident=0;
1039                        if ($3.constant)
1040                                {
1041                                if ($$.constant)
1042                                        trctx.out->printf("move %s,%s.%s\n",litstr($5),str($1),str($3));
1043                                else
1044                                        trctx.out->printf("move s0,%s.%s\n",str($1),str($3));
1045                                }
1046                        else
1047                                {
1048                                if ($$.constant)
1049                                        {
1050                                        trctx.out->printf("move m[m0++],m1\nmove %s,%s.[m1]\n",
1051                                                          litstr($5),str($1));
1052                                        trstack.adjust(1);
1053                                        }
1054                                else
1055                                        {
1056                                        trctx.out->printf("move s1,m1\nmove m[m0++],s0\nmove s0,%s.[m1]\n",
1057                                                          str($1));
1058                                        trstack.adjust(1);
1059                                        }
1060                                }
1061                        }
1062
1063     | OBJNAME '.' member '(' arguments ')'
1064                        {
1065                        trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1066                        int adj=0,adj2=0;
1067                        if ($5.getInt()==0)
1068                                {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;}
1069                        if ($3.constant)
1070                                trctx.out->printf("call %s.%s\n",str($1),str($3));
1071                        else
1072                                {
1073                                trctx.out->printf("move s%d,m1\ncall %s.[m1]\n",$5.getInt()+adj,str($1));
1074                                adj2=1;
1075                                }
1076                        adj2+=$5.getInt()-1+adj;
1077                        if (adj2>0)
1078                                {
1079                                trctx.out->printf("add %d,m0\nxmove s%d,s0\n",adj2,-adj2);
1080                                trstack.adjust(adj2);
1081                                }
1082                        }
1083
1084     | CALL expr '(' arguments ')'
1085             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString($2.getString());
1086             short adj=0;
1087             if ($4.getInt()==0)
1088                     {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;}
1089             if ($2.constant)
1090                     trctx.out->printf("call %s\n",litstr($2));
1091             else
1092                     trctx.out->printf("call s%d\n",$4.getInt()+adj);
1093             if (($4.getInt()+adj) > 0)
1094                     {
1095                     trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$4.getInt()+adj,-($4.getInt()+adj));
1096                     trstack.adjust($4.getInt()+adj);
1097                     }
1098             }
1099
1100     | FUNCTION IDENT
1101             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=":"; t+=$1.getString(); $$.setString(t);
1102             trctx.out->printf("push :%s\n",$2.getString().c_str());
1103             trstack.adjust(-1);
1104             }
1105
1106     | stackexpr '.' member
1107             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1108             if ($3.constant)
1109                     trctx.out->printf("move s0,m1\nmove [m1].%s,s0\n",str($3));
1110             else
1111//                   trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n");
1112                     {trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n");trstack.adjust(1);}
1113             }
1114
1115     | stackexpr ARROW IDENT       /* shortcut: expr.get("ident") */
1116             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="->"; t+=$3.getString(); $$.setString(t);
1117             trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n",litstr($3));
1118             }
1119
1120     | OBJNAME ARROW IDENT       /* shortcut: StaticObject.get("ident") */
1121             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="->"; t+=$3.getString(); $$.setString(t);
1122             trctx.out->printf("dec m0\ncall %s.\"get\",%s\n",$1.getString().c_str(),litstr($3)); trstack.adjust(-1);
1123             }
1124
1125     | plusminus stackexpr ARROW IDENT       /* shortcut: expr.set("ident",expr.get("ident")+/-1) */
1126             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1127             trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1128                               "move s0,m2\n%s m2\n"
1129                               "call [m1].\"set\",%s,m2\nmove m2,s0\n",
1130                               litstr($4),$1.getInt()?"inc":"dec",litstr($4));
1131             }
1132
1133     | stackexpr ARROW IDENT plusminus       /* shortcut: expr.set("ident",expr.get("ident")+/-1) */
1134             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1135             trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1136                               "move s0,m2\n%s s0\n"
1137                               "call [m1].\"set\",%s,s0\nmove m2,s0\n",
1138                               litstr($3),$4.getInt()?"inc":"dec",litstr($3));
1139             }
1140
1141     | stackexpr ARROW IDENT assign_op expr    /* shortcut: expr1.set("ident",expr1.get("ident") +*-/ expr2) */
1142             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1143             if ($5.constant)
1144                     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1145                                       "move s0,m2\n%s %s,m2\n"
1146                                       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
1147                                       litstr($3),assign_op_names[$4.getInt()],litstr($5),litstr($3));
1148             else
1149                     {
1150                     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",%s\n"
1151                                       "move s0,m2\n%s m3,m2\n"
1152                                       "call [m1].\"set\",%s,m2\ninc m0\nmove m2,s0\n",
1153                                       litstr($3),assign_op_names[$4.getInt()],litstr($3));
1154                     trstack.adjust(1);
1155                     }
1156             }
1157
1158     | plusminus stackexpr '.' member
1159             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$2.getString(); t+="."; t+=$4.getString(); $$.setString(t);
1160             if ($4.constant)
1161                     trctx.out->printf("move s0,m1\n%s [m1].%s\nmove [m1].%s,s0\n",
1162                                       $1.getInt()?"inc":"dec",str($4),str($4));
1163             else
1164//                   trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n");
1165                     {trctx.out->printf("move s1,m1\nmove m[m0++],m2\n%s [m1].[m2]\nmove [m1].[m2],s0\n",
1166                                        $1.getInt()?"inc":"dec");trstack.adjust(1);}
1167             }
1168
1169     | stackexpr '.' member plusminus
1170             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1171             if ($3.constant)
1172                     trctx.out->printf("move s0,m1\nmove [m1].%s,s0\n%s [m1].%s\n",
1173                                       str($3),$4.getInt()?"inc":"dec",str($3));
1174             else
1175//                   trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n");
1176                     {trctx.out->printf("move s1,m1\nmove m[m0++],m2\nmove [m1].[m2],s0\n%s [m1].[m2]\n",
1177                                        $4.getInt()?"inc":"dec");trstack.adjust(1);}
1178             }
1179
1180     | stackexpr '.' member assign_op expr
1181             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1182             if ($3.constant)
1183                     {
1184                     int sp;
1185                     if ($5.constant)
1186                             {sp=0; trctx.out->printf("move s0,m1\n");}
1187                     else
1188                             {sp=1; trctx.out->printf("move s1,m1\n");}
1189                     t="[m1]."; t+=str($3);
1190                     handleAssignOp2($$,t.c_str(),$5,assign_op_names[$4.getInt()],sp,0);
1191                     if (sp) {trctx.out->printf("inc m0\n");trstack.adjust(1);}
1192                     }
1193             else
1194                     {
1195                     int sp;
1196                     char *t;
1197                     if ($5.constant)
1198                             {sp=1; t="move s1,m1\nmove s0,m2\n";}
1199                     else
1200                             {sp=2; t="move s2,m1\nmove s1,m2\n";}
1201                     trctx.out->printf(t);
1202                     handleAssignOp2($$,"[m1].[m2]",$5,assign_op_names[$4.getInt()],sp,0);
1203                     trctx.out->printf("add %d,m0\n",sp);
1204                     trstack.adjust(sp);
1205                     }
1206             }
1207
1208     | stackexpr '.' member '=' stackexpr
1209             { trctx.emitLine(); $$=$5; $$.assign=1; $$.parens=0; $$.ident=0;
1210             if ($3.constant)
1211                     {
1212                     trctx.out->printf("move s1,m1\nmove m[m0++],s0\nmove s0,[m1].%s\n",str($3));
1213                     trstack.adjust(1);
1214                     }
1215             else
1216                     {
1217                     trctx.out->printf("move s2,m1\nmove s1,m2\nmove s0,[m1].[m2]\nadd 2,m0\nmove s-2,s0\n");
1218                     trstack.adjust(2);
1219                     }
1220             }
1221
1222     | stackexpr '.' member '(' arguments ')'
1223             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="."; t+=$3.getString(); $$.setString(t);
1224             int adj=0;
1225             if ($5.getInt()==0)
1226                     {trctx.out->printf("dec m0\n");trstack.adjust(-1);adj=1;}
1227             if ($3.constant)
1228                     {
1229                     trctx.out->printf("move s%d,m1\ncall [m1].%s\n",$5.getInt()+adj,str($3));
1230                     adj+=1;
1231                     }
1232             else
1233                     {
1234                     trctx.out->printf("move s%d,m2\nmove s%d,m1\ncall [m2].[m1]\n",
1235                                       $5.getInt()+adj+1,$5.getInt()+adj);
1236                     adj+=2;
1237                     }
1238             if (($5.getInt()+adj) > 1)
1239                     {
1240                     trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$5.getInt()-1+adj,-($5.getInt()-1+adj));
1241                     trstack.adjust($5.getInt()-1+adj);
1242                     }
1243             }
1244
1245      | stackexpr '[' expr ']' '=' expr    // shortcut: expr.set(expr,expr)
1246             { trctx.emitLine(); $$=$6; $$.assign=1; $$.parens=0; $$.ident=0;
1247             if ($3.constant)
1248                     {
1249                     if ($6.constant)
1250                             {trctx.out->printf("move s0,m1\ncall [m1].\"set\",%s,%s\ninc m0\n",litstr($3),litstr($6));trstack.adjust(+1);}
1251                     else
1252                             {trctx.out->printf("move s1,m1\npush s0\npush s0\nmove %s,s1\ncall [m1].\"set\"\nadd 3,m0\nmove s-1,s0\n",litstr($3));trstack.adjust(+1);}
1253                     }
1254             else
1255                     {
1256                     if ($6.constant)
1257                             {trctx.out->printf("move s1,m1\npush %s\ncall [m1].\"set\"\nadd 3,m0\n",litstr($6)); trstack.adjust(+2);}
1258                     else
1259                             {trctx.out->printf("move s2,m1\npush s1\npush s1\ncall [m1].\"set\"\nadd 4,m0\nmove s-2,s0\n"); trstack.adjust(+2);}
1260                     }
1261             }
1262
1263      | plusminus stackexpr '[' expr ']'  /* shortcut: o.set(index,o.get(index)+/-1) */
1264             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1265             if ($4.constant)
1266                     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1267                                       "move s0,m2\n%s m2\n"
1268                                       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
1269                                       litstr($4),$1.getInt()?"inc":"dec",litstr($4));
1270             else
1271                     {
1272                     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
1273                                       "move s0,m2\n%s m2\n"
1274                                       "call [m1].\"set\",m3,m2\ninc m0\nmove m2,s0\n",
1275                                       $1.getInt()?"inc":"dec");
1276                     trstack.adjust(1);
1277                     }
1278             }
1279
1280      | stackexpr '[' expr ']' plusminus  /* shortcut: o.set(index,o.get(index)+/-1) */
1281             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1282             if ($3.constant)
1283                     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1284                                       "move s0,m2\n%s s0\n"
1285                                       "call [m1].\"set\",%s,s0\nmove m2,s0\n",
1286                                       litstr($3),$5.getInt()?"inc":"dec",litstr($3));
1287             else
1288                     {
1289                     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
1290                                       "move s0,m2\n%s s0\n"
1291                                       "call [m1].\"set\",m3,s0\ninc m0\nmove m2,s0\n",
1292                                       $5.getInt()?"inc":"dec");
1293                     trstack.adjust(1);
1294                     }
1295             }
1296
1297      | stackexpr '[' expr ']' assign_op expr /* shortcut: o.set(index,o.get(index) +*-/ expr) */
1298             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
1299             if ($6.constant)
1300                     {
1301                     if ($3.constant)
1302                             trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
1303                                               "move s0,m2\n%s %s,m2\n"
1304                                               "call [m1].\"set\",%s,m2\nmove m2,s0\n",
1305                                               litstr($3),assign_op_names[$5.getInt()],litstr($6),litstr($3));
1306                     else
1307                             {
1308                             trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
1309                                               "move s0,m2\n%s %s,m2\n"
1310                                               "call [m1].\"set\",m3,m2\ninc m0\nmove m2,s0\n",
1311                                               assign_op_names[$5.getInt()],litstr($6));
1312                             trstack.adjust(1);
1313                             }
1314                     }
1315             else
1316                     {
1317                     if ($3.constant)
1318                             {
1319                             trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",%s\n"
1320                                               "move s0,m2\n%s m3,m2\n"
1321                                               "call [m1].\"set\",%s,m2\ninc m0\nmove m2,s0\n",
1322                                               litstr($3),assign_op_names[$5.getInt()],litstr($3));
1323                             trstack.adjust(1);
1324                             }
1325                     else
1326                             {
1327                             trctx.out->printf("move s0,m3\nmove s1,m4\nmove s2,m1\ncall [m1].\"get\",m4\n"
1328                                               "move s0,m2\n%s m3,m2\n"
1329                                               "call [m1].\"set\",m4,m2\nadd 2,m0\nmove m2,s0\n",
1330                                               assign_op_names[$5.getInt()]);
1331                             trstack.adjust(2);
1332                             }
1333                     }
1334             }
1335
1336
1337     | stackexpr ARROW IDENT '=' expr       /* shortcut: expr.set("ident",expr) */
1338             { trctx.emitLine(); $$=$5; $$.assign=1; $$.ident=0; $$.parens=0;
1339                     if ($5.constant)
1340                             {trctx.out->printf("move s0,m1\ncall [m1].\"set\",%s,%s\ninc m0\n",litstr($3),litstr($5));$$=$5;trstack.adjust(+1);}
1341                     else
1342                             {trctx.out->printf("move s1,m1\npush s0\npush s0\nmove %s,s1\ncall [m1].\"set\"\nadd 3,m0\nmove s-1,s0\n",litstr($3));trstack.adjust(+1);}
1343             }
1344
1345      | stackexpr '[' expr ']'    /* shortcut: expr.get(expr) */
1346             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".get"; $$.setString(t);
1347             if ($3.constant)
1348                     {
1349                     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n",litstr($3));
1350                     }
1351             else
1352                     {
1353                     trctx.out->printf("move s1,m1\ncall [m1].\"get\"\ninc m0\nmove s-1,s0\n");
1354                     trstack.adjust(+1);
1355                     }
1356             }
1357
1358     | IDENT '=' expr { trctx.emitLine(); $$=$3; $$.assign=1; $$.ident=0;
1359                        int loc=trstack.getVariableLocation($1.getString());
1360                        if (loc!=TranslatorStack::NOTFOUND)
1361                            {
1362                            if ($3.constant)
1363                              trctx.out->printf("move %s,s%d\n",litstr($3),loc-trstack.currentPos());
1364                            else
1365                              trctx.out->printf("move s0,s%d\n",loc-trstack.currentPos());
1366                            }
1367                        else if (globalOk($1)) { $$=$3; $$.ident=0; $$.assign=1;
1368                          if ($3.constant) trctx.out->printf("move %s,@%s\n",litstr($3),str($1));
1369                          else trctx.out->printf("move s0,@%s\n",str($1));}
1370                        else {trctx.err->printf("undefined variable: '%s'\n",str($1)); return 1;}
1371                      }
1372
1373      | OBJNAME '[' expr ']'    /* shortcut: OBJNAME.get(expr) */
1374             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+=".get"; $$.setString(t);
1375             if ($3.constant)
1376                     {
1377                     trctx.out->printf("dec m0\ncall %s.get,%s\n",str($1),litstr($3));
1378                     trstack.adjust(-1);
1379                     }
1380             else
1381                     {
1382                     trctx.out->printf("call %s.get\n",str($1));
1383                     }
1384             }
1385
1386      | IDENT '(' arguments ')'
1387{
1388trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("function call");
1389if ($3.getInt()==0)
1390        {trctx.out->printf("dec m0\n");trstack.adjust(-1);}
1391trctx.out->printf("call :%s\n",str($1));
1392if ($3.getInt()>1)
1393        {
1394        trctx.out->printf("add %d,m0\nxmove s%d,s0\n",$3.getInt()-1,-($3.getInt()-1));
1395        trstack.adjust($3.getInt()-1);
1396        }
1397}
1398
1399| '[' {trctx.emitLine(); $$.ident=0; trctx.out->printf("add -2,m0\ncall Vector.new\nmove s0,s1\n");trstack.adjust(-2);} // s1=vector, s0=nieuzywane ale zarezerwowane zeby nie przesuwac stosu przy kazdym elemencie (trafia tu wartosc zwracana przez add/set)
1400        v_elements ']'
1401        {$$.constant=0; trctx.out->printf("inc m0\n");trstack.adjust(1);}
1402
1403| '{' {trctx.emitLine(); $$.ident=0; trctx.out->printf("add -2,m0\ncall Dictionary.new\nmove s0,s1\n");trstack.adjust(-2);} // s1=dict, s0=nieuzywane ale zarezerwowane zeby nie przesuwac stosu przy kazdym elemencie (trafia tu wartosc zwracana przez add/set)
1404        d_elements '}'
1405        {$$.constant=0; trctx.out->printf("inc m0\n"); trstack.adjust(1);}
1406
1407//      | '&' stackexpr {trctx.out->printf("call Ref.new\n");}
1408
1409      | '&' IDENT {
1410        trctx.emitLine(); $$.ident=0;
1411        int loc=trstack.getVariableLocation($2.getString());
1412        if (loc!=TranslatorStack::NOTFOUND)
1413                {
1414                trctx.out->printf("push &%d\n",loc-trstack.currentPos());trstack.adjust(-1);
1415                }
1416        else if (globalOk($2))
1417                {
1418                trctx.out->printf("gpush &@%s\ncall Ref.newO\ninc m0\nmove s-1,s0\n",str($2));
1419                trstack.adjust(-1);
1420                }
1421        else {trctx.err->printf("undefined variable: '%s'\n",str($1)); return 1;}
1422            }
1423
1424      | '&' OBJNAME '.' member {
1425      trctx.emitLine(); $$.ident=0;
1426      if ($4.constant)
1427              {
1428              trctx.out->printf("dec m0\ncall Ref.newO,%s.*,%s:%s\n",str($2),str($2),str($4));
1429              trstack.adjust(-1);
1430              }
1431      else
1432              {
1433              trctx.out->printf("call Ref.newO,%s.*,s0\n",str($2));
1434              }
1435      }
1436
1437      | '&' '(' stackexpr ')' '.' member {
1438      trctx.emitLine(); $$.ident=0;
1439      if ($6.constant)
1440              {
1441              trctx.out->printf("call Ref.newO,s0,%s\n",litstr($6));
1442              }
1443      else
1444              {
1445              trctx.out->printf("call Ref.newO,s1,s0\ninc m0\nmove s-1,s0\n");
1446              trstack.adjust(1);
1447              }
1448      }
1449
1450      | '(' stackexpr ',' stackexpr ',' stackexpr ')' {
1451      trctx.emitLine(); $$.ident=0;
1452      trctx.out->printf("call XYZ.new\nadd 2,m0\nmove s-2,s0\n");trstack.adjust(2);}
1453;
1454
1455v_elements: /* empty */
1456      | v_element
1457      | v_elements ',' v_element
1458;
1459
1460d_elements: /* empty */
1461      | d_element
1462      | d_elements ',' d_element
1463;
1464
1465v_element: expr
1466{
1467if ($1.constant)
1468        trctx.out->printf("move s1,m1\ncall [m1].Vector:add,%s\n",litstr($1));
1469else
1470        {trctx.out->printf("move s2,m1\ncall [m1].Vector:add\ninc m0\n");trstack.adjust(1);}
1471}
1472;
1473
1474d_element: expr ':' expr
1475{
1476if ($1.constant)
1477        {
1478        if ($3.constant)
1479                trctx.out->printf("move s1,m1\ncall [m1].Dictionary:set,%s,%s\n",litstr($1),litstr($3));
1480        else
1481                {trctx.out->printf("move s2,m1\nmove %s,s1\ncall [m1].Dictionary:set\ninc m0\n",litstr($1));trstack.adjust(1);}
1482        }
1483else
1484        {
1485        if ($3.constant)
1486                {trctx.out->printf("move s2,m1\nmove s0,s1\nmove %s,s0\ncall [m1].Dictionary:set\ninc m0\n",litstr($3));trstack.adjust(1);}
1487        else
1488                {trctx.out->printf("move s3,m1\ncall [m1].Dictionary:set\nadd 2,m0\n");trstack.adjust(2);}
1489        }
1490}
1491;
1492
1493member:    IDENT { $$=$1; $$.constant=1;}
1494         | OBJNAME ':' IDENT { SString t=$1.getString();t+=":";t+=$3.getString();
1495                               $$.setString(t);$$.constant=1;}
1496         | '[' stackexpr ']' { SString t="["; t+=$2.getString(); t+="]";
1497                               $$.setString(t); $$.constant=0;}
1498
1499arguments: /* empty */         { $$.setInt(0); }
1500         |  stackexpr               { $$.setInt(1); }
1501         |  arguments ',' stackexpr  {$$.setInt($1.getInt()+1);}
1502;
1503
1504%%
1505
1506SString makeLitString(const ExtValue& val)
1507{
1508if (val.type!=TString)
1509        return val.getString();
1510SString s=val.getString();
1511int len=s.len();
1512SString ret((len*11)/10+10);
1513ret+='\"';
1514const char*t=s.c_str();
1515while(len>0)
1516        {
1517        switch(*t)
1518                {
1519                case '\n': ret+="\\n"; break;
1520                case '\r': ret+="\\r"; break;
1521                case '\t': ret+="\\t"; break;
1522                default: ret+=*t;
1523                }
1524        t++; len--;
1525        }
1526ret+='\"';
1527return ret;
1528}
1529
1530static void yyprint (FILE *file,int type,YYSTYPE value)
1531{
1532fprintf(file,"(%s)%s",str(value),value.constant?"c":"");
1533}
1534
1535int yyerror (const char *s)  /* Called by yyparse on error */
1536{
1537trctx.err->printf ("%s\n",s);
1538return 0; // w przykladach do bisona tez nie dali returna...
1539}
1540
1541void handleTwoArg(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2,
1542                  int optoken,const char* opname,bool negarg2,bool uniq)
1543{
1544trctx.emitLine();
1545result.ident=false;
1546if (arg1.constant && arg2.constant)
1547        {
1548        result=arg1;
1549        switch(optoken)
1550                {
1551                case '+': result+=arg2; break;
1552                case '-': result-=arg2; break;
1553                case '*': result*=arg2; break;
1554                case '/': result/=arg2; break;
1555                case '%': result%=arg2; break;
1556                case '&': result.setInt(arg1.getInt() & arg2.getInt()); break;
1557                case '|': result.setInt(arg1.getInt() | arg2.getInt()); break;
1558                case LSHIFT: result.setInt(arg1.getInt() << arg2.getInt()); break;
1559                case RSHIFT: result.setInt(arg1.getInt() >> arg2.getInt()); break;
1560                }
1561        }
1562else
1563        {
1564        //TODO: prawie kazde uzycie uniq jest niepotrzebne bo typem rzadko bedzie vector, ale w wiekszosci miejsc okreslenie typu wartosci
1565        // byloby bardzo trudne lub niemozliwe. mozna byloby natomiast zapamietywac przy parsowaniu czy dana wartosc na stosie jest
1566        // skopiowana ze zmiennej/pola czy jest wynikiem wczesniejszej operacji co pozwoliloby na likwidacje bardzo wielu uniq
1567        result.constant=0;
1568        result.assign=arg1.assign || arg2.assign;
1569        result.parens=0;
1570        result.setString(opname);
1571        if (arg1.constant)
1572                trctx.out->printf("move %s,m1\n%s%s s0,m1\nmove m1,s0\n",litstr(arg1),
1573                                  negarg2?"neg s0\n":"",
1574                                  opname);
1575        else if (arg2.constant)
1576                {
1577                if (negarg2)
1578                        trctx.out->printf("%s %d,s0\n",opname,-arg2.getInt());
1579                else
1580                        trctx.out->printf("%s%s %s,s0\n",(uniq?"uniq s0\n":""),opname,litstr(arg2));
1581                }
1582        else
1583                {
1584                trctx.out->printf("%s%s%s s0,s1\ninc m0\n",
1585                                  uniq?"uniq s1\n":"",
1586                                  negarg2?"neg s0\n":"",
1587                                  opname);
1588                trstack.adjust(+1);
1589                }
1590        }
1591}
1592
1593bool handleAssignOp(YYSTYPE& result,const YYSTYPE& var,const YYSTYPE& arg,const char* opname)
1594{
1595int loc; if (variableOk(result,var,loc))
1596        {
1597        loc-=trstack.currentPos();
1598        if (arg.constant)
1599                {
1600                trctx.out->printf("%s %s,s%d\npush s%d\n",opname,litstr(arg),loc,loc);
1601                trstack.adjust(-1);
1602                }
1603        else
1604                trctx.out->printf("%s s0,s%d\nmove s%d,s0\n",opname,loc,loc);
1605        return 1;
1606        }
1607return 0;
1608}
1609
1610bool handleAssignOp2(YYSTYPE& result,const char *var,const YYSTYPE& arg,const char* opname,int stackpos,bool push)
1611{
1612if (arg.constant)
1613        {
1614        trctx.out->printf("%s %s,%s\n",opname,litstr(arg),var);
1615        if (!push)
1616                trctx.out->printf("move %s,s%d\n",var,stackpos);
1617        else
1618                {
1619                trctx.out->printf("push %s\n",var);
1620                trstack.adjust(-1);
1621                }
1622        }
1623else
1624        trctx.out->printf("%s s0,%s\nmove %s,s%d\n",opname,var,var,stackpos);
1625return 1;
1626}
1627
1628bool handleCompare(YYSTYPE& result,const YYSTYPE& arg1,const YYSTYPE& arg2,ExtValue::CmpOperator op,const char* opname)
1629{
1630trctx.emitLine();
1631result.ident=0;
1632if (arg1.constant && arg2.constant)
1633        {
1634        result.constant=1;
1635        ExtValue::CompareResult cmp=arg1.compare(arg2);
1636        ExtValue::CmpContext context;
1637        context.v1=&arg1;
1638        context.v2=&arg2;
1639        int ret=ExtValue::interpretCompare(op,cmp,&context);
1640        if (ret<0)
1641                result.setEmpty();//return false;
1642        else
1643                result.setInt(ret);
1644        return true;
1645        }
1646else
1647        {
1648        result.constant=0;
1649        result.assign=arg1.assign || arg2.assign;
1650        result.parens=0;
1651        result.setString(opname);
1652        if (arg1.constant)
1653                trctx.out->printf("setif %s,%s,s0,s0\n",litstr(arg1),opname);
1654        else if (arg2.constant)
1655                trctx.out->printf("setif s0,%s,%s,s0\n",opname,litstr(arg2));
1656        else
1657                {
1658                trctx.out->printf("setif s1,%s,s0,s1\ninc m0\n",opname);
1659                trstack.adjust(+1);
1660                }
1661        return true;
1662        }
1663}
1664
1665static bool resultIsRelaxedEqual(ExtValue::CompareResult res)
1666{
1667return (res==ExtValue::ResultEqual)||(res==ExtValue::ResultEqualUnordered)||(res==ExtValue::ResultUnequal_RelaxedEqual);
1668}
1669
1670bool canAddName(const SString &name,NameKind kind)
1671{
1672ExtValue dummy;
1673NameKind found=NameNotFound;
1674if (globalNameOk(name)) found=GlobalName;
1675else if (trstack.getConstant(name,dummy)) found=ConstName;
1676else if (kind!=VariableName) { if (variableNameOk(name)!=TranslatorStack::NOTFOUND) found=VariableName; }
1677if (found!=NameNotFound)
1678        { trctx.err->printf("Can't define '%s %s' (previously defined as %s)",name_kind_names[kind],name.c_str(),name_kind_names[found]); return false; }
1679return true;
1680}
1681
1682int variableNameOk(const SString &name)
1683{
1684int loc=trstack.getVariableLocation(name);
1685if (loc != TranslatorStack::NOTFOUND)
1686        {
1687        if ((trctx.functionstackpos!=TranslatorContext::NOT_IN_FUNCTION)
1688            && (loc>=trctx.beforefunctionstackpos))
1689                return TranslatorStack::NOTFOUND;
1690        return loc;
1691        }
1692return TranslatorStack::NOTFOUND;
1693}
1694
1695bool variableOk(TokenValue &tok, const TokenValue& var,int &loc)
1696{
1697loc=variableNameOk(var.getString());
1698if (loc != TranslatorStack::NOTFOUND)
1699        {
1700        tok.setInt(loc); tok.constant=0;
1701        return true;
1702        }
1703return false;
1704}
1705
1706bool globalOk(const TokenValue& var)
1707{
1708return globalNameOk(var.getString());
1709}
1710
1711bool globalNameOk(const SString& name)
1712{
1713SymTabEntry* found=trstack.globals.find(name);
1714if (found) return true;
1715return framscriptIsGlobalName(name.c_str());
1716}
1717
1718void badVariable(TokenValue &tok, const TokenValue& var)
1719{
1720tok=var; tok.constant=1;
1721trctx.err->printf("undefined variable '%s'\n",str(var));
1722}
1723
1724bool doBreak(int level)
1725{
1726if (trstack.loops.size()<level)
1727        {trctx.err->printf("invalid 'break'\n"); return 0;}
1728LoopInfo* li=trstack.loops.getLoop(level-1);
1729if (li->location != trstack.currentPos())
1730        trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos());
1731trctx.out->printf("jump :_loop_end_%d\n",li->id);
1732return 1;
1733}
1734
1735bool doContinue(int level)
1736{
1737if (trstack.loops.size()<level)
1738        {trctx.err->printf("invalid 'continue'\n"); return 0;}
1739LoopInfo* li=trstack.loops.getLoop(level-1);
1740if (li->location != trstack.currentPos())
1741        trctx.out->printf("add %d,m0\n",li->location-trstack.currentPos());
1742trctx.out->printf("jump :_loop_%d\n",li->id);
1743return 1;
1744}
1745
1746int lookupToken(char *s)
1747{
1748int len=strlen(s);
1749int i;
1750const char *t;
1751for (i = 0; i < YYNTOKENS; i++)
1752        {
1753        t=yytname[i];
1754        if (t && (t[0]=='"')
1755           && (!strncmp(t+1,s,len))
1756           && (t[len+1]=='"')
1757           && (t[len+2] == 0))
1758                return yytoknum[i];
1759        }
1760return -1;
1761}
1762
1763void warnTruthValue(const TokenValue& t)
1764{
1765if (t.assign && (!t.parens))
1766        logPrintf("FramScriptCompiler","translate",LOG_WARN,"Assignment used as truth value, use ((double parens)) if you really mean it");
1767}
1768
1769void outFunName(const TokenValue& t)
1770{
1771if (trctx.functiontmplabel<0)
1772        {
1773        trctx.functiontmplabel=trctx.labelcounter++;
1774        trctx.out->printf("jump :_skipfun_%d\n",trctx.functiontmplabel);
1775        }
1776trctx.out->printf(":%s\n",str(t));
1777}
1778
1779bool evalVariable(TokenValue &tok,const TokenValue &var)
1780{
1781int loc;
1782if (variableOk(tok,var,loc))
1783        {
1784        trctx.out->printf("push s%d\n",loc-trstack.currentPos());
1785        trstack.adjust(-1);
1786        return true;
1787        }
1788else if (globalOk(var))
1789        {
1790        trctx.out->printf("push @%s\n",var.getString().c_str());
1791        trstack.adjust(-1);
1792        return true;
1793        }
1794else
1795        {
1796        badVariable(tok,var); return false;
1797        }
1798}
Note: See TracBrowser for help on using the repository browser.