Browse Source

pastojs: for c in string do

git-svn-id: trunk@37589 -
Mattias Gaertner 7 years ago
parent
commit
a808ae29a8

+ 276 - 97
packages/pastojs/src/fppas2js.pp

@@ -55,7 +55,7 @@ Works:
   - read and write char aString[]
   - allow only String, no ShortString, AnsiString, UnicodeString,...
   - allow type casting string to external class name 'String'
-- for loop
+- for int/enum do, for char do, for bool do
 - repeat..until
 - while..do
 - try..finally
@@ -255,12 +255,11 @@ Works:
   - enum, int, char
   - low(), high(), pred(), succ(), ord(),
   - rg(int), int(rg), enum:=rg,
-  - rg:=rg, rg1:=rg2, rg:=enum, =, <>, in
-  - array[rg], low(array), high(array)
+  - rg:=rg, rg1:=rg2, rg:=enum, =, <>,
+  - set of int/enum/char range, in
+  - array[rg], low(array), high(array), length(array)
 
 ToDos:
-- for bool:=
-- "use strict" must be at the beginning of the .js file
 - typecast longint(highprecint) -> (value+0) & $ffffffff
 - static arrays
   - a[] of record
@@ -295,6 +294,7 @@ Not in Version 1.0:
 - record const
 - enums with custom values
 - library
+- constref
 - option typecast checking
 - option verify method calls -CR
 - option range checking -Cr
@@ -317,12 +317,16 @@ Not in Version 1.0:
     Include, Exclude, Inc, Dec, +=, -=, *=, /=
   -O1 replace constant expression with result
   -O1 pass array element by ref: when index is constant, use that directly
-- objects, interfaces, advanced records
+- objects
+- interfaces
+- advanced records
 - class helpers, type helpers, record helpers,
 - generics
 - operator overloading
 - inline
 - anonymous functions
+- extended RTTI
+- attributes
 
 Debugging this unit: -d<x>
    VerbosePas2JS
@@ -455,6 +459,7 @@ type
     pbivnImplementation,
     pbivnLoop,
     pbivnLoopEnd,
+    pbivnLoopIn,
     pbivnModule,
     pbivnModules,
     pbivnPtrClass,
@@ -561,7 +566,8 @@ const
     '$e',
     '$impl',
     '$l',
-    '$le',
+    '$end',
+    '$in',
     '$mod',
     'pas',
     '$class',
@@ -1229,6 +1235,8 @@ type
     Function CreateUsesList(UsesSection: TPasSection; AContext : TConvertContext): TJSArrayLiteral;
     Procedure AddToStatementList(var First, Last: TJSStatementList;
       Add: TJSElement; Src: TPasElement);
+    Procedure AddToVarStatement(VarStat: TJSVariableStatement; Add: TJSElement;
+      Src: TPasElement);
     Function CreateValInit(PasType: TPasType; Expr: TPasExpr; El: TPasElement;
       AContext: TConvertContext): TJSElement; virtual;
     Function CreateVarInit(El: TPasVariable; AContext: TConvertContext): TJSElement; virtual;
@@ -3740,6 +3748,7 @@ begin
   Src:=FunDecl.AFunction.Body.A as TJSSourceElements;
 
   if coUseStrict in Options then
+    // "use strict" must be the first statement in a function
     AddToSourceElements(Src,CreateLiteralString(El,'use strict'));
 
   ImplVarSt:=nil;
@@ -10546,6 +10555,12 @@ function TPasToJSConverter.ConvertForStatement(El: TPasImplForLoop;
 //  The EndExpr must be executed exactly once at beginning.
 //  If the loop is not executed the Variable is not set, aka keeps its old value.
 //  After the loop the variable has the last value.
+type
+  TInKind = (
+    ikNone,
+    ikChar,
+    ikString
+  );
 
   function ConvExpr(Expr: TPasExpr): TJSElement;
   var
@@ -10606,6 +10621,117 @@ function TPasToJSConverter.ConvertForStatement(El: TPasImplForLoop;
       ReleaseEvalValue(OrdValue);
   end;
 
+var
+  ResolvedVar, ResolvedIn: TPasResolverResult;
+  StartValue, EndValue, InValue: TResEvalValue;
+  StartInt, EndInt: MaxPrecInt;
+  HasLoopVar, HasEndVar, HasInVar: Boolean;
+  InKind: TInKind;
+
+  procedure InitWithResolver;
+  begin
+    AContext.Resolver.ComputeElement(El.VariableName,ResolvedVar,[rcNoImplicitProc]);
+    if not (ResolvedVar.IdentEl is TPasVariable) then
+      DoError(20170213214404,nExpectedXButFoundY,sExpectedXButFoundY,['var',
+        AContext.Resolver.GetResolverResultDescription(ResolvedVar)],El.VariableName);
+
+    case El.LoopType of
+    ltNormal,ltDown:
+      begin
+      StartValue:=AContext.Resolver.Eval(El.StartExpr,[],false);
+      StartInt:=GetOrd(StartValue,El.StartExpr);
+      EndValue:=AContext.Resolver.Eval(El.EndExpr,[],false);
+      EndInt:=GetOrd(EndValue,El.EndExpr);
+      end;
+    ltIn:
+      begin
+      HasInVar:=true;
+      AContext.Resolver.ComputeElement(El.StartExpr,ResolvedIn,[]);
+      InValue:=AContext.Resolver.Eval(El.StartExpr,[],false);
+      if InValue<>nil then
+        begin
+        // for in <constant> do
+        case InValue.Kind of
+        revkString,revkUnicodeString:
+          begin
+          // example:
+          //  for c in 'foo' do ;
+          // -> for (var $l1 = 0, $li2 = 'foo'; $l1<=2; $l1++) c = $li2.charAt($l1);
+          InKind:=ikString;
+          StartInt:=0;
+          if InValue.Kind=revkString then
+            EndInt:=length(UTF8Decode(TResEvalString(InValue).S))-1
+          else
+            EndInt:=length(TResEvalUTF16(InValue).S)-1;
+          ReleaseEvalValue(InValue);
+          end;
+        revkRangeInt:
+          begin
+          StartInt:=TResEvalRangeInt(InValue).RangeStart;
+          EndInt:=TResEvalRangeInt(InValue).RangeEnd;
+          HasInVar:=false;
+          case TResEvalRangeInt(InValue).ElKind of
+          revskChar: InKind:=ikChar;
+          else
+            {$IFDEF VerbosePas2JS}
+            writeln('TPasToJSConverter.ConvertForStatement ',GetObjName(El.StartExpr),' InValue=',InValue.AsDebugString);
+            {$ENDIF}
+            RaiseNotSupported(El.StartExpr,AContext,20171113023419);
+          end;
+          end
+        else
+          {$IFDEF VerbosePas2JS}
+          writeln('TPasToJSConverter.ConvertForStatement ',GetObjName(El.StartExpr),' InValue=',InValue.AsDebugString);
+          {$ENDIF}
+          RaiseNotSupported(El.StartExpr,AContext,20171112161527);
+        end;
+        end
+      else if rrfReadable in ResolvedIn.Flags then
+        begin
+        // for v in <variable> do
+        if ResolvedIn.BaseType in btAllStrings then
+          begin
+          StartInt:=0;
+          InKind:=ikString;
+          end
+        else if ResolvedIn.BaseType=btContext then
+          begin
+          {$IFDEF VerbosePas2JS}
+          writeln('TPasToJSConverter.ConvertForStatement ',GetObjName(El.StartExpr),' StartValue=',StartValue.AsDebugString);
+          {$ENDIF}
+          RaiseNotSupported(El.StartExpr,AContext,20171113012226);
+          end;
+        end
+      else
+        begin
+        {$IFDEF VerbosePas2JS}
+        writeln('InitWithResolver ResolvedIn=',GetResolverResultDbg(ResolvedIn));
+        {$ENDIF}
+        RaiseNotSupported(El.StartExpr,AContext,20171112195629);
+        end;
+      end;
+    end;
+
+    if EndValue<>nil then
+      begin
+      HasEndVar:=false;
+      if (StartValue<>nil) then
+        begin
+        if StartInt<=EndInt then
+          begin
+          // loop is always executed
+          if StartValue.Kind in [revkInt,revkUInt,revkEnum] then
+            HasLoopVar:=false; // variable can be used as runner
+          end
+        else
+          begin
+          // loop is never executed
+          if coEliminateDeadCode in Options then exit;
+          end;
+        end;
+      end;
+  end;
+
 Var
   ForSt : TJSForStatement;
   List: TJSStatementList;
@@ -10613,22 +10739,27 @@ Var
   Incr: TJSUNaryExpression;
   BinExp : TJSBinaryExpression;
   VarStat: TJSVariableStatement;
-  CurLoopVarName, CurLoopEndVarName: String;
+  CurLoopVarName, CurEndVarName, CurInVarName: String;
   FuncContext: TConvertContext;
-  ResolvedVar: TPasResolverResult;
-  Comma: TJSCommaExpression;
-  LoopPosEl: TPasElement;
-  StartValue, EndValue: TResEvalValue;
-  NeedLoopVar, NeedLoopEndVar: Boolean;
-  StartInt, EndInt: MaxPrecInt;
+  PosEl: TPasElement;
   Statements, V: TJSElement;
   NotEqual: TJSEqualityExpressionNE;
+  Call: TJSCallExpression;
 begin
   Result:=Nil;
   if AContext.Access<>caRead then
     RaiseInconsistency(20170213213740);
-  if not (El.LoopType in [ltNormal,ltDown]) then
+  case El.LoopType of
+  ltNormal,ltDown: ;
+  ltIn:
+    if AContext.Resolver=nil then
+      RaiseNotSupported(El,AContext,20171112160707);
+  else
+    {$IFDEF VerbosePas2JS}
+    writeln('TPasToJSConverter.ConvertForStatement LoopType=',El.LoopType);
+    {$ENDIF}
     RaiseNotSupported(El,AContext,20171110141937);
+  end;
 
   // get function context
   FuncContext:=AContext;
@@ -10636,60 +10767,42 @@ begin
     FuncContext:=FuncContext.Parent;
 
   StartValue:=nil;
+  StartInt:=0;
   EndValue:=nil;
+  EndInt:=0;
+  InValue:=nil;
+  InKind:=ikNone;
   Statements:=nil;
   try
-    NeedLoopVar:=true;
-    NeedLoopEndVar:=true;
+    HasLoopVar:=true;
+    HasEndVar:=true;
+    HasInVar:=false;
     if AContext.Resolver<>nil then
-      begin
-      AContext.Resolver.ComputeElement(El.VariableName,ResolvedVar,[rcNoImplicitProc]);
-      if not (ResolvedVar.IdentEl is TPasVariable) then
-        DoError(20170213214404,nExpectedXButFoundY,sExpectedXButFoundY,['var',
-          AContext.Resolver.GetResolverResultDescription(ResolvedVar)],El.VariableName);
-      StartValue:=AContext.Resolver.Eval(El.StartExpr,[],false);
-      StartInt:=GetOrd(StartValue,El.StartExpr);
-      EndValue:=AContext.Resolver.Eval(El.EndExpr,[],false);
-      EndInt:=GetOrd(EndValue,El.EndExpr);
-      if EndValue<>nil then
-        begin
-        NeedLoopEndVar:=false;
-        if (StartValue<>nil) then
-          begin
-          if StartInt<=EndInt then
-            begin
-            // loop is always executed
-            if StartValue.Kind in [revkInt,revkUInt,revkEnum] then
-              NeedLoopVar:=false; // variable can be used as runner
-            end
-          else
-            begin
-            // loop is never executed
-            if coEliminateDeadCode in Options then exit;
-            end;
-          end;
-        end;
-      end;
-    // create unique var names $loop and $loopend
-    if NeedLoopVar then
+      InitWithResolver;
+    // create unique var names $l, $end, $in
+    if HasInVar then
+      CurInVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnLoopIn])
+    else
+      CurInVarName:='';
+    if HasLoopVar then
       CurLoopVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnLoop])
     else
       CurLoopVarName:='';
-    if NeedLoopEndVar then
-      CurLoopEndVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnLoopEnd])
+    if HasEndVar then
+      CurEndVarName:=FuncContext.CreateLocalIdentifier(FBuiltInNames[pbivnLoopEnd])
     else
-      CurLoopEndVarName:='';
+      CurEndVarName:='';
 
     // add "for()"
     ForSt:=TJSForStatement(CreateElement(TJSForStatement,El));
     Statements:=ForSt;
 
-    // add  variable=<startexpr>
-    if (not NeedLoopVar) and NeedLoopEndVar then
+    // add in front of for():  variable=<startexpr>
+    if (not HasLoopVar) and (HasEndVar or HasInVar) then
       begin
       // for example:
       //   i:=<startexpr>;
-      //   for (var $le = <endexpr>; $i<$le; $i++)...
+      //   for (var $end = <endexpr>; $i<$end; $i++)...
       List:=TJSStatementList(CreateElement(TJSStatementList,El));
       SimpleAss:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,El.VariableName));
       List.A:=SimpleAss;
@@ -10700,39 +10813,65 @@ begin
         SimpleAss.Expr:=CreateLiteralNumber(El.StartExpr,StartInt)
       else
         SimpleAss.Expr:=ConvertElement(El.StartExpr,AContext);
+      PosEl:=El.StartExpr;
       end;
 
-    if NeedLoopVar or NeedLoopEndVar then
+    if HasLoopVar or HasEndVar or HasInVar then
       begin
       // add "for(var ..."
       VarStat:=TJSVariableStatement(CreateElement(TJSVariableStatement,El));
       ForSt.Init:=VarStat;
-      if NeedLoopVar then
+      if HasInVar then
+        begin
+        // add "$in=<InExpr>"
+        PosEl:=El.StartExpr;
+        if InValue<>nil then
+          V:=ConvertConstValue(InValue,AContext,PosEl)
+        else
+          V:=ConvertElement(El.StartExpr,AContext);
+        V:=CreateVarDecl(CurInVarName,V,PosEl);
+        AddToVarStatement(VarStat,V,PosEl);
+        end;
+      if HasLoopVar then
         begin
-        // add "$loop=<StartExpr>"
+        // add "$l=<StartExpr>"
+        PosEl:=El.StartExpr;
         if StartValue<>nil then
-          V:=CreateLiteralNumber(El.StartExpr,StartInt)
+          V:=CreateLiteralNumber(PosEl,StartInt)
+        else if El.LoopType=ltIn then
+          case InKind of
+          ikChar, ikString: V:=CreateLiteralNumber(PosEl,StartInt);
+          end
         else
           V:=ConvExpr(El.StartExpr);
-        VarStat.A:=CreateVarDecl(CurLoopVarName,V,El.StartExpr);
+        V:=CreateVarDecl(CurLoopVarName,V,PosEl);
+        AddToVarStatement(VarStat,V,PosEl);
         end;
-      if NeedLoopEndVar then
+      if HasEndVar then
         begin
-        // add "$loopend=<EndExpr>"
+        // add "$end=<EndExpr>"
+        PosEl:=El.EndExpr;
+        if El.EndExpr=nil then
+          PosEl:=El.StartExpr;
         if EndValue<>nil then
-          V:=CreateLiteralNumber(El.EndExpr,EndInt)
+          V:=CreateLiteralNumber(PosEl,EndInt)
+        else if El.LoopType=ltIn then
+          case InKind of
+          ikChar: V:=CreateLiteralNumber(PosEl,EndInt);
+          ikString:
+            begin
+            // add "$end=$in.length-1"
+            V:=TJSAdditiveExpressionMinus(CreateElement(TJSAdditiveExpressionMinus,PosEl));
+            TJSAdditiveExpressionMinus(V).A:=CreatePrimitiveDotExpr(CurInVarName+'.length',PosEl);
+            TJSAdditiveExpressionMinus(V).B:=CreateLiteralNumber(PosEl,1);
+            end;
+          else
+            RaiseNotSupported(El.StartExpr,AContext,20171113015445);
+          end
         else
           V:=ConvExpr(El.EndExpr);
-        V:=CreateVarDecl(CurLoopEndVarName,V,El.EndExpr);
-        if VarStat.A=nil then
-          VarStat.A:=V
-        else
-          begin
-          Comma:=TJSCommaExpression(CreateElement(TJSCommaExpression,El.EndExpr));
-          Comma.A:=VarStat.A;
-          Comma.B:=V;
-          VarStat.A:=Comma;
-          end;
+        V:=CreateVarDecl(CurEndVarName,V,PosEl);
+        AddToVarStatement(VarStat,V,PosEl);
         end;
       end
     else
@@ -10746,59 +10885,82 @@ begin
         SimpleAss.Expr:=CreateLiteralNumber(El.StartExpr,StartInt)
       else
         SimpleAss.Expr:=ConvertElement(El.StartExpr,AContext);
+      PosEl:=El.StartExpr;
       end;
 
-    // add "$loop<=$loopend"
+    // add "$l<=$end"
+    if (El.EndExpr<>nil) then
+      PosEl:=El.EndExpr;
     if El.Down then
-      BinExp:=TJSRelationalExpressionGE(CreateElement(TJSRelationalExpressionGE,El.EndExpr))
+      BinExp:=TJSRelationalExpressionGE(CreateElement(TJSRelationalExpressionGE,PosEl))
     else
-      BinExp:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,El.EndExpr));
+      BinExp:=TJSRelationalExpressionLE(CreateElement(TJSRelationalExpressionLE,PosEl));
     ForSt.Cond:=BinExp;
-    if NeedLoopVar then
-      BinExp.A:=CreatePrimitiveDotExpr(CurLoopVarName,El.EndExpr)
+    if HasLoopVar then
+      BinExp.A:=CreatePrimitiveDotExpr(CurLoopVarName,PosEl)
     else
       BinExp.A:=ConvertElement(El.VariableName,AContext);
-    if NeedLoopEndVar then
-      BinExp.B:=CreatePrimitiveDotExpr(CurLoopEndVarName,El.EndExpr)
+    if HasEndVar then
+      BinExp.B:=CreatePrimitiveDotExpr(CurEndVarName,PosEl)
     else
-      BinExp.B:=CreateLiteralNumber(El.EndExpr,EndInt);
+      BinExp.B:=CreateLiteralNumber(PosEl,EndInt);
 
-    // add "$loop++"
+    // add "$l++"
     if El.Down then
-      Incr:=TJSUnaryPostMinusMinusExpression(CreateElement(TJSUnaryPostMinusMinusExpression,El.EndExpr))
+      Incr:=TJSUnaryPostMinusMinusExpression(CreateElement(TJSUnaryPostMinusMinusExpression,PosEl))
     else
-      Incr:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,El.EndExpr));
+      Incr:=TJSUnaryPostPlusPlusExpression(CreateElement(TJSUnaryPostPlusPlusExpression,PosEl));
     ForSt.Incr:=Incr;
-    if NeedLoopVar then
-      Incr.A:=CreatePrimitiveDotExpr(CurLoopVarName,El.EndExpr)
+    if HasLoopVar then
+      Incr.A:=CreatePrimitiveDotExpr(CurLoopVarName,PosEl)
     else
       Incr.A:=ConvertElement(El.VariableName,AContext);
 
-    // add  "VariableName:=$loop;"
-    if NeedLoopVar then
+    // add  "VariableName:=$l;"
+    if HasLoopVar then
       begin
-      LoopPosEl:=El.Body;
-      if LoopPosEl=nil then
-        LoopPosEl:=El;
-      // add  "VariableName:=$loop;"
-      LoopPosEl:=El.VariableName;
-      SimpleAss:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,LoopPosEl));
+      PosEl:=El.Body;
+      if PosEl=nil then
+        PosEl:=El;
+      PosEl:=El.VariableName;
+      SimpleAss:=TJSSimpleAssignStatement(CreateElement(TJSSimpleAssignStatement,PosEl));
       ForSt.Body:=SimpleAss;
       SimpleAss.LHS:=ConvertElement(El.VariableName,AContext);
-      SimpleAss.Expr:=CreatePrimitiveDotExpr(CurLoopVarName,LoopPosEl);
+      SimpleAss.Expr:=CreatePrimitiveDotExpr(CurLoopVarName,PosEl);
       if AContext.Resolver<>nil then
         begin
-        if (ResolvedVar.BaseType in btAllChars)
+        if InKind<>ikNone then
+          case InKind of
+          ikChar:
+            // String.fromCharCode($l)
+            SimpleAss.Expr:=CreateCallFromCharCode(SimpleAss.Expr,PosEl);
+          ikString:
+            begin
+            // $in.charAt($l)
+            Call:=CreateCallExpression(PosEl);
+            Call.Expr:=CreateDotExpression(PosEl,
+              CreatePrimitiveDotExpr(CurInVarName,El.StartExpr),
+              CreatePrimitiveDotExpr('charAt',PosEl));
+            Call.AddArg(SimpleAss.Expr);
+            SimpleAss.Expr:=Call;
+            end
+          else
+            {$IFDEF VerbosePas2JS}
+            writeln('TPasToJSConverter.ConvertForStatement InKind=',InKind);
+            {$ENDIF}
+            RaiseNotSupported(El.StartExpr,AContext,20171113002550);
+          end
+        else if (ResolvedVar.BaseType in btAllChars)
             or ((ResolvedVar.BaseType=btRange) and (ResolvedVar.SubType in btAllChars)) then
           begin
           // convert int to char
-          SimpleAss.Expr:=CreateCallFromCharCode(SimpleAss.Expr,LoopPosEl);
+          SimpleAss.Expr:=CreateCallFromCharCode(SimpleAss.Expr,PosEl);
           end
         else if (ResolvedVar.BaseType in btAllBooleans)
             or ((ResolvedVar.BaseType=btRange) and (ResolvedVar.SubType in btAllBooleans)) then
           begin
-          // convert int to bool  ->  $loop!=0
-          NotEqual:=TJSEqualityExpressionNE(CreateElement(TJSEqualityExpressionNE,LoopPosEl));
+          // convert int to bool  ->  $l!=0
+          NotEqual:=TJSEqualityExpressionNE(CreateElement(TJSEqualityExpressionNE,PosEl));
           NotEqual.A:=SimpleAss.Expr;
           NotEqual.B:=CreateLiteralNumber(El,0);
           SimpleAss.Expr:=NotEqual;
@@ -10824,6 +10986,7 @@ begin
   finally
     ReleaseEvalValue(StartValue);
     ReleaseEvalValue(EndValue);
+    ReleaseEvalValue(InValue);
     if Result=nil then
       Statements.Free;
   end;
@@ -11169,6 +11332,22 @@ begin
     end;
 end;
 
+procedure TPasToJSConverter.AddToVarStatement(VarStat: TJSVariableStatement;
+  Add: TJSElement; Src: TPasElement);
+var
+  Comma: TJSCommaExpression;
+begin
+  if VarStat.A=nil then
+    VarStat.A:=Add
+  else
+    begin
+    Comma:=TJSCommaExpression(CreateElement(TJSCommaExpression,Src));
+    Comma.A:=VarStat.A;
+    Comma.B:=Add;
+    VarStat.A:=Comma;
+    end;
+end;
+
 function TPasToJSConverter.CreateValInit(PasType: TPasType; Expr: TPasExpr;
   El: TPasElement; AContext: TConvertContext): TJSElement;
 var

+ 70 - 14
packages/pastojs/tests/tcmodules.pas

@@ -204,6 +204,7 @@ type
     // numbers
     Procedure TestDouble;
     Procedure TestIntegerRange;
+    Procedure TestForBoolDo;
 
     // strings
     Procedure TestCharConst;
@@ -223,7 +224,7 @@ type
     Procedure TestTypeShortstring_Fail;
     Procedure TestCharSet_Custom;
     Procedure TestForCharDo;
-    Procedure TestForBoolDo;
+    Procedure TestForCharInDo;
 
     // alias types
     Procedure TestAliasTypeRef;
@@ -4111,6 +4112,25 @@ begin
     '']));
 end;
 
+procedure TTestModule.TestForBoolDo;
+begin
+  StartProgram(false);
+  Add([
+  'var b: boolean;',
+  'begin',
+  '  for b:=false to true do ;',
+  '  for b:=b downto false do ;',
+  '']);
+  ConvertProgram;
+  CheckSource('TestForBoolDo',
+    LinesToStr([ // statements
+    'this.b = false;']),
+    LinesToStr([ // this.$main
+    'for (var $l1 = 0; $l1 <= 1; $l1++) $mod.b = $l1 != 0;',
+    'for (var $l2 = +$mod.b; $l2 >= 0; $l2--) $mod.b = $l2 != 0;',
+    '']));
+end;
+
 procedure TTestModule.TestCharConst;
 begin
   StartProgram(false);
@@ -4547,22 +4567,58 @@ begin
     '']));
 end;
 
-procedure TTestModule.TestForBoolDo;
+procedure TTestModule.TestForCharInDo;
 begin
   StartProgram(false);
   Add([
-  'var b: boolean;',
+  'type',
+  '  TSetOfChar = set of char;',
+  '  TCharRg = ''a''..''z'';',
+  '  TSetOfCharRg = set of TCharRg;',
+  'const Foo = ''foo'';',
+  'var',
+  '  c: char;',
+  '  s: string;',
+  '  a1: array of char;',
+  '  a2: array[1..3] of char;',
+  '  a3: array[1..3,4..5] of char;',
+  '  soc: TSetOfChar;',
+  '  socr: TSetOfCharRg;',
+  '  cr: TCharRg;',
   'begin',
-  '  for b:=false to true do ;',
-  '  for b:=b downto false do ;',
+  '  for c in foo do ;',
+  '  for c in s do ;',
+  '  for c in char do ;',
+  //'  for c in a1 do ;',
+  //'  for c in a2 do ;',
+  //'  for c in a3 do ;',
+  //'  for c in [''1''..''3''] do ;',
+  //'  for c in TSetOfChar do ;',
+  //'  for c in TCharRg do ;',
+  //'  for c in soc do ;',
+  //'  for c in TSetOfCharRg do ;',
+  //'  for c in socr do ;',
+  //'  for cr in TCharRg do ;',
+  //'  for cr in TSetOfCharRg do ;',
+  //'  for cr in socr do ;',
   '']);
   ConvertProgram;
-  CheckSource('TestForBoolDo',
+  CheckSource('TestForCharInDo',
     LinesToStr([ // statements
-    'this.b = false;']),
+    'this.Foo = "foo";',
+    'this.c = "";',
+    'this.s = "";',
+    'this.a1 = [];',
+    'this.a2 = rtl.arraySetLength(null, "", 3);',
+    'this.a3 = rtl.arraySetLength(null, "", 3, 2);',
+    'this.soc = {};',
+    'this.socr = {};',
+    'this.cr = "a";',
+    '']),
     LinesToStr([ // this.$main
-    'for (var $l1 = 0; $l1 <= 1; $l1++) $mod.b = $l1 != 0;',
-    'for (var $l2 = +$mod.b; $l2 >= 0; $l2--) $mod.b = $l2 != 0;',
+    'for (var ($in1 = $mod.Foo, $l2 = 0), $end3 = $in1.length - 1; $l2 <= $end3; $l2++) $mod.c = $in1.charAt($l2);',
+    'for (var ($in4 = $mod.s, $l5 = 0), $end6 = $in4.length - 1; $l5 <= $end6; $l5++) $mod.c = $in4.charAt($l5);',
+    'for (var $l7 = 0, $end8 = 65535; $l7 <= $end8; $l7++) $mod.c = String.fromCharCode($l7);',
     '']));
 end;
 
@@ -4693,7 +4749,7 @@ begin
     LinesToStr([ // this.$main
     '  $mod.vJ = 0;',
     '  $mod.vN = 3;',
-    '  for (var $l1 = 1, $le2 = $mod.vN; $l1 <= $le2; $l1++) {',
+    '  for (var $l1 = 1, $end2 = $mod.vN; $l1 <= $end2; $l1++) {',
     '    $mod.vI = $l1;',
     '    $mod.vJ = $mod.vJ + $mod.vI;',
     '  };',
@@ -4723,7 +4779,7 @@ begin
     '  var vI = 0;',
     '  var vJ = 0;',
     '  vJ = 0;',
-    '  for (var $l1 = 1, $le2 = Count; $l1 <= $le2; $l1++) {',
+    '  for (var $l1 = 1, $end2 = Count; $l1 <= $end2; $l1++) {',
     '    vI = $l1;',
     '    vJ = vJ + vI;',
     '  };',
@@ -4781,9 +4837,9 @@ begin
     '  var vJ = 0;',
     '  var vK = 0;',
     '  vK = 0;',
-    '  for (var $l1 = 1, $le2 = Count; $l1 <= $le2; $l1++) {',
+    '  for (var $l1 = 1, $end2 = Count; $l1 <= $end2; $l1++) {',
     '    vI = $l1;',
-    '    for (var $l3 = 1, $le4 = vI; $l3 <= $le4; $l3++) {',
+    '    for (var $l3 = 1, $end4 = vI; $l3 <= $end4; $l3++) {',
     '      vJ = $l3;',
     '      vK = vK + vI;',
     '    };',
@@ -5799,7 +5855,7 @@ begin
     'this.DoIt = function (a) {',
     '  var i = 0;',
     '  var s = "";',
-    '  for (var $l1 = 0, $le2 = rtl.length(a) - 1; $l1 <= $le2; $l1++) {',
+    '  for (var $l1 = 0, $end2 = rtl.length(a) - 1; $l1 <= $end2; $l1++) {',
     '    i = $l1;',
     '    s = a[(rtl.length(a) - i) - 1];',
     '  };',

+ 1 - 1
packages/pastojs/tests/tcsrcmap.pas

@@ -421,7 +421,7 @@ begin
   '    var Runner = 0;',
   '    var j = 0;',
   '    j = 0;',
-  '    for (var $l1 = 3, $le2 = j; $l1 <= $le2; $l1++) {',
+  '    for (var $l1 = 3, $end2 = j; $l1 <= $end2; $l1++) {',
   '      Runner = $l1;',
   '      j += 1;',
   '    };',