source: cpp/frams/vm/framscript.y @ 1029

Last change on this file since 1029 was 973, checked in by Maciej Komosinski, 4 years ago

Increased SString and std::string compatibility: introduced length(), size(), and capacity(), and removed legacy methods that have std::string equivalents

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