Index: /cpp/frams/vm/framscript.l
===================================================================
--- /cpp/frams/vm/framscript.l	(revision 476)
+++ /cpp/frams/vm/framscript.l	(revision 477)
@@ -45,4 +45,6 @@
                             }
                        }
+
+"->"               return ARROW;
 "=="               return EQUAL;
 "<>"               return NOT_EQUAL;
Index: /cpp/frams/vm/framscript.y
===================================================================
--- /cpp/frams/vm/framscript.y	(revision 476)
+++ /cpp/frams/vm/framscript.y	(revision 477)
@@ -12,4 +12,7 @@
 #define YYERROR_VERBOSE
 #define YYPRINT(file,type,value) yyprint (file,type,value)
+
+enum NameKind { NameNotFound, VariableName, GlobalName, ConstName };
+static const char* name_kind_names[]={"","var","global","const"};
 
 static void yyprint (FILE *file,int type,YYSTYPE value);
@@ -20,6 +23,9 @@
 bool handleAssignOp(YYSTYPE& result,const YYSTYPE& var,const YYSTYPE& arg,const char* opname);
 bool handleAssignOp2(YYSTYPE& result,const char *var,const YYSTYPE& arg,const char* opname,int stackpos,bool push);
+bool canAddName(const SString &name,NameKind kind);
 bool variableOk(TokenValue &tok, const TokenValue& var,int &loc);
+int variableNameOk(const SString &name);
 bool globalOk(const TokenValue& var);
+bool globalNameOk(const SString& name);
 void badVariable(TokenValue &tok, const TokenValue &var);
 bool evalVariable(TokenValue &tok, const TokenValue &var);
@@ -78,8 +84,11 @@
               
 %token VAR      "var"
+%token CONSTDEF "const"
 %token GLOBAL   "global"
 %token FUNCTION "function"
 
 %token CALL    "call"
+
+%token ARROW
 
 %token ASSIGN
@@ -117,4 +126,5 @@
 statement: ';'
       | VAR vardeflist ';'
+      | CONSTDEF constdeflist ';'
       | GLOBAL globaldeflist ';'
       | IDENT ':'     {trctx.out->printf(":%s\n",str($1));}
@@ -203,6 +213,13 @@
 ;
 
-vardef: IDENT               { trctx.emitLine(); trstack.addVariable($1.getString()); trctx.out->printf("push invalid\n");}
-      | IDENT '=' stackexpr { trctx.emitLine(); trstack.adjust(1); trstack.addVariable($1.getString());}
+vardef: IDENT               { trctx.emitLine(); if (!canAddName($1.getString(),VariableName)) return 1; trstack.addVariable($1.getString()); trctx.out->printf("push invalid\n"); }
+      | IDENT '=' stackexpr { trctx.emitLine(); if (!canAddName($1.getString(),VariableName)) return 1; trstack.adjust(1); trstack.addVariable($1.getString());}
+;
+
+constdeflist: constdef
+          | constdeflist ',' constdef
+;
+
+constdef: 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); }
 ;
 
@@ -211,6 +228,5 @@
 ;
 
-globaldef: IDENT     {trstack.globals.add($1.getString(),0);
-                      trctx.out->printf("global %s\n",str($1));}
+globaldef: IDENT     { if (!canAddName($1.getString(),GlobalName)) return 1; trstack.globals.add($1.getString(),0); trctx.out->printf("global %s\n",str($1));}
 ;
 
@@ -468,12 +484,15 @@
 	   trctx.emitLine();
 	   if ($4.constant) 
-		   {trctx.err->printf("%s can't be iterated\n",str($4)); return 1;}
+		   {
+		   logPrintf("", "", LOG_WARN, "%s can't be iterated",str($4));
+		   trctx.out->printf("jump :_loop_end_%d\n",$1.counter);
+		   }
+	   trstack.adjust(-1);
+	   trstack.loops.addLoop($1.counter,trstack.currentPos());
 	   if ($4.objname) 
-		   trctx.out->printf("move %s.iterator,m[--m0]\n",$4.getString().c_str());
+		   trctx.out->printf("dec m0\nmove %s.iterator,m[m0]\n",$4.getString().c_str());
 	   else
-		   trctx.out->printf("move s%d,m1\nmove [m1].\"iterator\",m[--m0]\n",0);
-	   trstack.adjust(-1);
+		   trctx.out->printf("move s%d,m1\ndec m0\nif m1,==,null,:_loop_end_%d\nmove [m1].\"iterator\",m[m0]\n",0,$1.counter);
 	   // s0=iterator s1=obj (=obj.iterator)
-	   trstack.loops.addLoop($1.counter,trstack.currentPos());
 	   trctx.out->printf(":_loop1_%d\n",$1.counter);
 	   trctx.out->printf(":_loop_%d\n",$1.counter);
@@ -662,5 +681,11 @@
 expr_special_ident:    CONSTANT             { $$=$1; $$.constant=1; $$.ident=0; }
 
-       | IDENT                { $$.ident=true; $$.setString($1.getString()); }
+       | IDENT                {
+                              ExtValue c;
+                              if (trstack.getConstant($1.getString(),c))
+				      { $$=c; $$.constant=1; $$.ident=0; }
+			      else
+				      { $$.ident=true; $$.setString($1.getString()); }
+                              }
 
        | OBJNAME ':' IDENT    {$$.constant=0; $$.ident=0;
@@ -1024,4 +1049,47 @@
 	     }
 
+     | stackexpr ARROW IDENT       /* shortcut: expr.get("ident") */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="->"; t+=$3.getString(); $$.setString(t);
+	     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n",litstr($3));
+	     }
+
+     | OBJNAME ARROW IDENT       /* shortcut: StaticObject.get("ident") */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$1.getString(); t+="->"; t+=$3.getString(); $$.setString(t);
+	     trctx.out->printf("dec m0\ncall %s.\"get\",%s\n",$1.getString().c_str(),litstr($3)); trstack.adjust(-1);
+	     }
+
+     | plusminus stackexpr ARROW IDENT       /* shortcut: expr.set("ident",expr.get("ident")+/-1) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+			       "move s0,m2\n%s m2\n"
+			       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
+			       litstr($4),$1.getInt()?"inc":"dec",litstr($4));
+	     }
+
+     | stackexpr ARROW IDENT plusminus       /* shortcut: expr.set("ident",expr.get("ident")+/-1) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+			       "move s0,m2\n%s s0\n"
+			       "call [m1].\"set\",%s,s0\nmove m2,s0\n",
+			       litstr($3),$4.getInt()?"inc":"dec",litstr($3));
+	     }
+
+     | stackexpr ARROW IDENT assign_op expr    /* shortcut: expr1.set("ident",expr1.get("ident") +*-/ expr2) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     if ($5.constant)
+		     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+				       "move s0,m2\n%s %s,m2\n"
+				       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
+				       litstr($3),assign_op_names[$4.getInt()],litstr($5),litstr($3));
+	     else
+		     {
+		     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",%s\n"
+				       "move s0,m2\n%s m3,m2\n"
+				       "call [m1].\"set\",%s,m2\ninc m0\nmove m2,s0\n",
+				       litstr($3),assign_op_names[$4.getInt()],litstr($3));
+		     trstack.adjust(1);
+		     }
+	     }
+
      | plusminus stackexpr '.' member
              { trctx.emitLine(); $$.constant=0; $$.ident=0; SString t=$2.getString(); t+="."; t+=$4.getString(); $$.setString(t);
@@ -1127,4 +1195,86 @@
 			     {trctx.out->printf("move s2,m1\ncall [m1].\"set\"\nadd 2,m0\nmove s-2,s0\n"); trstack.adjust(+2);}
 		     }
+	     }
+
+      | plusminus stackexpr '[' expr ']'  /* shortcut: o.set(index,o.get(index)+/-1) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     if ($4.constant)
+		     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+				       "move s0,m2\n%s m2\n"
+				       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
+				       litstr($4),$1.getInt()?"inc":"dec",litstr($4));
+	     else
+		     {
+		     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
+				       "move s0,m2\n%s m2\n"
+				       "call [m1].\"set\",m3,m2\ninc m0\nmove m2,s0\n",
+				       $1.getInt()?"inc":"dec");
+		     trstack.adjust(1);
+		     }
+	     }
+
+      | stackexpr '[' expr ']' plusminus  /* shortcut: o.set(index,o.get(index)+/-1) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     if ($3.constant)
+		     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+				       "move s0,m2\n%s s0\n"
+				       "call [m1].\"set\",%s,s0\nmove m2,s0\n",
+				       litstr($3),$5.getInt()?"inc":"dec",litstr($3));
+	     else
+		     {
+		     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
+				       "move s0,m2\n%s s0\n"
+				       "call [m1].\"set\",m3,s0\ninc m0\nmove m2,s0\n",
+				       $5.getInt()?"inc":"dec");
+		     trstack.adjust(1);
+		     }
+	     }
+
+      | stackexpr '[' expr ']' assign_op expr /* shortcut: o.set(index,o.get(index) +*-/ expr) */
+             { trctx.emitLine(); $$.constant=0; $$.ident=0; $$.setString("");
+	     if ($6.constant)
+		     {
+		     if ($3.constant)
+			     trctx.out->printf("move s0,m1\ncall [m1].\"get\",%s\n"
+					       "move s0,m2\n%s %s,m2\n"
+					       "call [m1].\"set\",%s,m2\nmove m2,s0\n",
+					       litstr($3),assign_op_names[$5.getInt()],litstr($6),litstr($3));
+		     else
+			     {
+			     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",m3\n"
+					       "move s0,m2\n%s %s,m2\n"
+					       "call [m1].\"set\",m3,m2\ninc m0\nmove m2,s0\n",
+					       assign_op_names[$5.getInt()],litstr($6));
+			     trstack.adjust(1);
+			     }
+		     }
+	     else
+		     {
+		     if ($3.constant)
+			     {
+			     trctx.out->printf("move s0,m3\nmove s1,m1\ncall [m1].\"get\",%s\n"
+					       "move s0,m2\n%s m3,m2\n"
+					       "call [m1].\"set\",%s,m2\ninc m0\nmove m2,s0\n",
+					       litstr($3),assign_op_names[$5.getInt()],litstr($3));
+			     trstack.adjust(1);
+			     }
+		     else
+			     {
+			     trctx.out->printf("move s0,m3\nmove s1,m4\nmove s2,m1\ncall [m1].\"get\",m4\n"
+					       "move s0,m2\n%s m3,m2\n"
+					       "call [m1].\"set\",m4,m2\nadd 2,m0\nmove m2,s0\n",
+					       assign_op_names[$5.getInt()]);
+			     trstack.adjust(2);
+			     }
+		     }
+	     }
+
+
+     | stackexpr ARROW IDENT '=' expr       /* shortcut: expr.set("ident",expr) */
+	     { trctx.emitLine(); $$=$5; $$.assign=1; $$.ident=0; $$.parens=0; 
+		     if ($5.constant)
+			     {trctx.out->printf("move s0,m1\ncall [m1].\"set\",%s,%s\ninc m0\n",litstr($3),litstr($5));$$=$5;trstack.adjust(+1);}
+		     else
+			     {trctx.out->printf("move s1,m1\npush s0\nmove %s,s1\ncall [m1].\"set\"\nadd 2,m0\nmove s-2,s0\n",litstr($3));trstack.adjust(+1);}
 	     }
 
@@ -1454,23 +1604,50 @@
 }
 
-bool variableOk(TokenValue &tok, const TokenValue& var,int &loc)
-{
-loc=trstack.getVariableLocation(var.getString());
+bool canAddName(const SString &name,NameKind kind)
+{
+ExtValue dummy;
+NameKind found=NameNotFound;
+if (globalNameOk(name)) found=GlobalName;
+else if (trstack.getConstant(name,dummy)) found=ConstName;
+else if (kind!=VariableName) { if (variableNameOk(name)!=TranslatorStack::NOTFOUND) found=VariableName; }
+if (found!=NameNotFound)
+	{ 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; }
+return true;
+}
+
+int variableNameOk(const SString &name)
+{
+int loc=trstack.getVariableLocation(name);
 if (loc != TranslatorStack::NOTFOUND)
 	{
 	if ((trctx.functionstackpos!=TranslatorContext::NOT_IN_FUNCTION)
 	    && (loc>=trctx.beforefunctionstackpos))
-		return 0;
+		return TranslatorStack::NOTFOUND;
+	return loc;
+	}
+return TranslatorStack::NOTFOUND;
+}
+
+bool variableOk(TokenValue &tok, const TokenValue& var,int &loc)
+{
+loc=variableNameOk(var.getString());
+if (loc != TranslatorStack::NOTFOUND)
+	{
 	tok.setInt(loc); tok.constant=0;
-	return 1;
-	}
-return 0;
+	return true;
+	}
+return false;
 }
 
 bool globalOk(const TokenValue& var)
 {
-SymTabEntry* found=trstack.globals.find(var.getString());
+return globalNameOk(var.getString());
+}
+
+bool globalNameOk(const SString& name)
+{
+SymTabEntry* found=trstack.globals.find(name);
 if (found) return true;
-return framscriptIsGlobalName(var.getString().c_str());
+return framscriptIsGlobalName(name.c_str());
 }
 
