浏览代码

operator scope for temporary variables

Vladislav Folts 11 年之前
父节点
当前提交
c707a14dc1
共有 7 个文件被更改,包括 172 次插入60 次删除
  1. 二进制
      bin/compiled.zip
  2. 43 29
      src/context.js
  3. 89 15
      src/eberon/eberon_context.js
  4. 5 1
      src/eberon/eberon_grammar.js
  5. 15 11
      src/grammar.js
  6. 5 1
      src/oberon/oberon_grammar.js
  7. 15 3
      test/test_unit_eberon.js

二进制
bin/compiled.zip


+ 43 - 29
src/context.js

@@ -28,6 +28,10 @@ function getSymbolAndScope(context, id){
     return s;
     return s;
 }
 }
 
 
+function getQIdSymbolAndScope(context, q){
+    return getSymbolAndScope(q.module ? q.module : context, q.id);
+}
+
 function getSymbol(context, id){
 function getSymbol(context, id){
     return getSymbolAndScope(context, id).symbol();
     return getSymbolAndScope(context, id).symbol();
 }
 }
@@ -225,7 +229,8 @@ exports.BaseType = ChainedContext.extend({
     init: function BaseTypeContext(context){
     init: function BaseTypeContext(context){
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
     },
     },
-    handleSymbol: function(s){
+    handleQIdent: function(q){
+        var s = getQIdSymbolAndScope(this, q);
         this.parent().setBaseType(unwrapType(s.symbol().info()));
         this.parent().setBaseType(unwrapType(s.symbol().info()));
     }
     }
 });
 });
@@ -249,11 +254,12 @@ exports.QualifiedIdentificator = ChainedContext.extend({
         return undefined;
         return undefined;
     },
     },
     endParse: function(){
     endParse: function(){
-        var s = getSymbolAndScope(this.__module ? this.__module : this, this.__id);
         var code = this.__code + this.__id;
         var code = this.__code + this.__id;
-        if (this.__module && s.symbol().isVariable())
-            code += "()";
-        this.parent().handleSymbol(s, code);
+        this.parent().handleQIdent({
+            module: this.__module,
+            id: this.__id,
+            code: code
+        });
     }
     }
 });
 });
 
 
@@ -299,7 +305,8 @@ exports.Designator = ChainedContext.extend({
         this.__derefCode = undefined;
         this.__derefCode = undefined;
         this.__propCode = undefined;
         this.__propCode = undefined;
     },
     },
-    handleSymbol: function(found, code){
+    handleQIdent: function(q){
+        var found = getQIdSymbolAndScope(this, q);
         this.__scope = found.scope();
         this.__scope = found.scope();
         var s = found.symbol();
         var s = found.symbol();
         var info = s.info();
         var info = s.info();
@@ -311,8 +318,12 @@ exports.Designator = ChainedContext.extend({
             this.__currentType = info.type();
             this.__currentType = info.type();
         else if (s.isProcedure())
         else if (s.isProcedure())
             this.__currentType = Type.procedureType(info);
             this.__currentType = Type.procedureType(info);
+        
         this.__info = info;
         this.__info = info;
-        this.__code += code;
+        this.__code += q.code;
+        
+        if (q.module && s.isVariable())
+            this.__code += "()";
     },
     },
     handleIdent: function(id){
     handleIdent: function(id){
         var t = this.__currentType;
         var t = this.__currentType;
@@ -450,8 +461,8 @@ exports.Type = ChainedContext.extend({
     init: function Context$Type(context){
     init: function Context$Type(context){
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
     },
     },
-    handleSymbol: function(s){
-        this.parent().handleSymbol(s);
+    handleQIdent: function(q){
+        this.parent().handleQIdent(q);
     }
     }
 });
 });
 
 
@@ -459,7 +470,8 @@ var HandleSymbolAsType = ChainedContext.extend({
     init: function Context$HandleSymbolAsType(context){
     init: function Context$HandleSymbolAsType(context){
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
     },
     },
-    handleSymbol: function(s){
+    handleQIdent: function(q){
+        var s = getQIdSymbolAndScope(this, q);
         this.setType(unwrapType(s.symbol().info()));
         this.setType(unwrapType(s.symbol().info()));
     }
     }
 });
 });
@@ -519,7 +531,8 @@ exports.FormalParameters = ChainedContext.extend({
         }
         }
         return ChainedContext.prototype.handleMessage.call(this, msg);
         return ChainedContext.prototype.handleMessage.call(this, msg);
     },
     },
-    handleSymbol: function(s){
+    handleQIdent: function(q){
+        var s = getQIdSymbolAndScope(this, q);
         var resultType = unwrapType(s.symbol().info());
         var resultType = unwrapType(s.symbol().info());
         if (resultType instanceof Type.Array)
         if (resultType instanceof Type.Array)
             throw new Errors.Error("the result type of a procedure cannot be an ARRAY");
             throw new Errors.Error("the result type of a procedure cannot be an ARRAY");
@@ -668,8 +681,23 @@ exports.PointerDecl = ChainedContext.extend({
     init: function Context$PointerDecl(context){
     init: function Context$PointerDecl(context){
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
     },
     },
-    handleSymbol: function(s){
-        var typeId = unwrapTypeId(s.symbol().info());
+    handleQIdent: function(q){
+        var id = q.id;
+        var s = q.module
+              ? getQIdSymbolAndScope(this, q)
+              : this.findSymbol(id);
+        
+        var info;
+        if (s)
+            info = s.symbol().info();
+        else {
+            var scope = this.currentScope();
+            Scope.addUnresolved(scope, id);
+            var resolve = function(){return getSymbol(this, id).info().type();}.bind(this);
+
+            info = Type.makeForwardTypeId(resolve);
+        }
+        var typeId = unwrapTypeId(info);
         this.__setTypeId(typeId);
         this.__setTypeId(typeId);
     },
     },
     __setTypeId: function(typeId){
     __setTypeId: function(typeId){
@@ -692,21 +720,6 @@ exports.PointerDecl = ChainedContext.extend({
         this.currentScope().addFinalizer(function(){typeId.strip();});
         this.currentScope().addFinalizer(function(){typeId.strip();});
         this.__setTypeId(typeId);
         this.__setTypeId(typeId);
     },
     },
-    findSymbol: function(id){
-        var parent = this.parent();
-        var existing = parent.findSymbol(id);
-        if (existing)
-            return existing;
-
-        var scope = this.currentScope();
-        Scope.addUnresolved(scope, id);
-        var resolve = function(){return getSymbol(parent, id).info().type();};
-
-        return Symbol.makeFound(
-            Symbol.makeSymbol(id, Type.makeForwardTypeId(resolve)),
-            scope
-            );
-    },
     isAnonymousDeclaration: function(){return true;},
     isAnonymousDeclaration: function(){return true;},
     exportField: function(field){
     exportField: function(field){
         throw new Errors.Error( "cannot export anonymous RECORD field: '" + field + "'");
         throw new Errors.Error( "cannot export anonymous RECORD field: '" + field + "'");
@@ -1795,7 +1808,8 @@ exports.TypeCast = ChainedContext.extend({
         ChainedContext.prototype.init.call(this, context);
         ChainedContext.prototype.init.call(this, context);
         this.__type = undefined;
         this.__type = undefined;
     },
     },
-    handleSymbol: function(s){
+    handleQIdent: function(q){
+        var s = getQIdSymbolAndScope(this, q);
         s = s.symbol();
         s = s.symbol();
         if (!s.isType())
         if (!s.isType())
             return; // this is not a type cast, may be procedure call
             return; // this is not a type cast, may be procedure call

+ 89 - 15
src/eberon/eberon_context.js

@@ -170,11 +170,13 @@ var Designator = Context.Designator.extend({
             Context.Designator.prototype.handleExpression.call(this, e);
             Context.Designator.prototype.handleExpression.call(this, e);
     },
     },
     handleLiteral: function(s){
     handleLiteral: function(s){
-        if (s == "SELF")
-            this.handleSymbol(Symbol.makeFound(this.handleMessage(getMethodSelf)), "this");
+        if (s == "SELF"){
+            var type = this.handleMessage(getMethodSelf);
+            this._advance(type, type, "this");
+        }
         else if (s == "SUPER"){
         else if (s == "SUPER"){
             var ms = this.handleMessage(getMethodSuper);
             var ms = this.handleMessage(getMethodSuper);
-            this.handleSymbol(Symbol.makeFound(ms.symbol), ms.code);
+            this._advance(ms.info.type, ms.info, ms.code);
         }
         }
         else 
         else 
             Context.Designator.prototype.handleLiteral.call(this, s);
             Context.Designator.prototype.handleLiteral.call(this, s);
@@ -479,7 +481,6 @@ var RecordDecl = Context.RecordDecl.extend({
 var ProcOrMethodDecl = Context.ProcDecl.extend({
 var ProcOrMethodDecl = Context.ProcDecl.extend({
     init: function EberonContext$ProcOrMethodDecl(parent, stdSymbols){
     init: function EberonContext$ProcOrMethodDecl(parent, stdSymbols){
         Context.ProcDecl.prototype.init.call(this, parent, stdSymbols);
         Context.ProcDecl.prototype.init.call(this, parent, stdSymbols);
-        this.__selfSymbol = undefined;
         this.__methodId = undefined;
         this.__methodId = undefined;
         this.__methodType = undefined;
         this.__methodType = undefined;
         this.__boundType = undefined;
         this.__boundType = undefined;
@@ -489,7 +490,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         if (msg == getMethodSelf){
         if (msg == getMethodSelf){
             if (!this.__boundType)
             if (!this.__boundType)
                 throw new Errors.Error("SELF can be used only in methods");
                 throw new Errors.Error("SELF can be used only in methods");
-            return this.__selfSymbol;
+            return this.__boundType;
         }
         }
         if (msg == getMethodSuper)
         if (msg == getMethodSuper)
             return this.__handleSuperCall();
             return this.__handleSuperCall();
@@ -497,7 +498,6 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             var id = msg.id;
             var id = msg.id;
             var type = msg.type;
             var type = msg.type;
             if (type){
             if (type){
-                this.__selfSymbol = Symbol.makeSymbol("SELF", type);
                 this.__methodId = id;
                 this.__methodId = id;
                 this.__boundType = type;
                 this.__boundType = type;
             }
             }
@@ -523,7 +523,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             this.__methodType = new MethodType(this.__methodId.id(), type, methodCallGenerator);
             this.__methodType = new MethodType(this.__methodId.id(), type, methodCallGenerator);
     },
     },
     handleIdent: function(id){
     handleIdent: function(id){
-        if (this.__selfSymbol){
+        if (this.__boundType){
             if (!this.__endingId)
             if (!this.__endingId)
                 this.__endingId = id + ".";
                 this.__endingId = id + ".";
             else {
             else {
@@ -535,13 +535,12 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
             Context.ProcDecl.prototype.handleIdent.call(this, id);
             Context.ProcDecl.prototype.handleIdent.call(this, id);
     },
     },
     endParse: function(){
     endParse: function(){
-        if (this.__selfSymbol){
+        if (this.__boundType){
             if (this.__endingId)
             if (this.__endingId)
                 // should throw
                 // should throw
                 Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId);
                 Context.ProcDecl.prototype.handleIdent.call(this, this.__endingId);
 
 
-            var boundType = this.__selfSymbol.info();
-            boundType.defineMethod(this.__methodId, this.__methodType);
+            this.__boundType.defineMethod(this.__methodId, this.__methodType);
         }
         }
         Context.ProcDecl.prototype.endParse.call(this);
         Context.ProcDecl.prototype.endParse.call(this);
     },
     },
@@ -558,9 +557,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
         var id = this.__methodId.id();
         var id = this.__methodId.id();
         baseType.requireMethodDefinition(id, "cannot use abstract method(s) in SUPER calls");
         baseType.requireMethodDefinition(id, "cannot use abstract method(s) in SUPER calls");
         return {
         return {
-            symbol: Symbol.makeSymbol(
-                "method",  
-                Type.makeProcedure(new MethodType(id, this.__methodType.procType(), superMethodCallGenerator))),
+            info: Type.makeProcedure(new MethodType(id, this.__methodType.procType(), superMethodCallGenerator)),
             code: this.qualifyScope(Type.recordScope(baseType))
             code: this.qualifyScope(Type.recordScope(baseType))
                 + Type.typeName(baseType) + ".prototype." + id + ".call"
                 + Type.typeName(baseType) + ".prototype." + id + ".call"
         };
         };
@@ -626,26 +623,103 @@ var Expression = Context.Expression.extend({
 var While = Context.While.extend({
 var While = Context.While.extend({
     init: function EberonContext$While(context){
     init: function EberonContext$While(context){
         Context.While.prototype.init.call(this, context);
         Context.While.prototype.init.call(this, context);
+        var scope = EberonScope.makeOperator(
+            this.parent().currentScope(),
+            this.language().stdSymbols);
+        this.pushScope(scope);
+    },
+    endParse: function(){
+        this.popScope();
+        Context.While.prototype.endParse.call(this);
+    }
+});
+
+var If = Context.If.extend({
+    init: function EberonContext$If(context){
+        Context.If.prototype.init.call(this, context);
+        this.__scope = undefined;
+        this.__newScope();
+    },
+    handleLiteral: function(s){
+        Context.If.prototype.handleLiteral.call(this, s);
+        if (s == "ELSIF" || s == "ELSE")
+            this.__newScope();
+    },
+    __newScope: function(){
+        if (this.__scope)
+            this.popScope();
         this.__scope = EberonScope.makeOperator(
         this.__scope = EberonScope.makeOperator(
             this.parent().currentScope(),
             this.parent().currentScope(),
             this.language().stdSymbols);
             this.language().stdSymbols);
+        this.pushScope(this.__scope);
+    },
+    endParse: function(){
+        this.popScope();
+        Context.If.prototype.endParse.call(this);
+    }
+});
+
+var CaseLabel = Context.CaseLabel.extend({
+    init: function EberonContext$CaseLabel(context){
+        Context.CaseLabel.prototype.init.call(this, context);
+    },
+    handleLiteral: function(s){
+        if (s == ':'){ // statement sequence is expected now
+            var scope = EberonScope.makeOperator(
+                this.parent().currentScope(),
+                this.language().stdSymbols);
+            this.pushScope(scope);
+        }
+    },
+    endParse: function(){
+        this.popScope();
+        Context.CaseLabel.prototype.endParse.call(this);
+    }
+});
+
+var Repeat = Context.Repeat.extend({
+    init: function EberonContext$Repeat(context){
+        Context.Repeat.prototype.init.call(this, context);
+        var scope = EberonScope.makeOperator(
+            this.parent().currentScope(),
+            this.language().stdSymbols);
+        this.pushScope(scope);
     },
     },
-    currentScope: function EberonContext$While(){
-        return this.__scope;
+    endParse: function(){
+        this.popScope();
+        //Context.Repeat.prototype.endParse.call(this);
+    }
+});
+
+var For = Context.For.extend({
+    init: function EberonContext$Repeat(context){
+        Context.For.prototype.init.call(this, context);
+        var scope = EberonScope.makeOperator(
+            this.parent().currentScope(),
+            this.language().stdSymbols);
+        this.pushScope(scope);
+    },
+    endParse: function(){
+        this.popScope();
+        Context.For.prototype.endParse.call(this);
     }
     }
 });
 });
 
 
 exports.AddOperator = AddOperator;
 exports.AddOperator = AddOperator;
+exports.CaseLabel = CaseLabel;
 exports.ConstDecl = ConstDecl;
 exports.ConstDecl = ConstDecl;
 exports.Designator = Designator;
 exports.Designator = Designator;
 exports.Expression = Expression;
 exports.Expression = Expression;
 exports.ExpressionProcedureCall = ExpressionProcedureCall;
 exports.ExpressionProcedureCall = ExpressionProcedureCall;
+exports.For = For;
 exports.Identdef = Identdef;
 exports.Identdef = Identdef;
+exports.If = If;
 exports.MethodHeading = MethodHeading;
 exports.MethodHeading = MethodHeading;
 exports.AssignmentOrProcedureCall = AssignmentOrProcedureCall;
 exports.AssignmentOrProcedureCall = AssignmentOrProcedureCall;
 exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;
 exports.RecordDecl = RecordDecl;
 exports.RecordDecl = RecordDecl;
+exports.Repeat = Repeat;
 exports.TemplValueInit = TemplValueInit;
 exports.TemplValueInit = TemplValueInit;
 exports.TypeDeclaration = TypeDeclaration;
 exports.TypeDeclaration = TypeDeclaration;
 exports.VariableDeclaration = VariableDeclaration;
 exports.VariableDeclaration = VariableDeclaration;

+ 5 - 1
src/eberon/eberon_grammar.js

@@ -79,7 +79,11 @@ exports.language = {
             variableDeclaration: EbContext.VariableDeclaration,
             variableDeclaration: EbContext.VariableDeclaration,
             addOperator:        EbContext.AddOperator,
             addOperator:        EbContext.AddOperator,
             expression:         EbContext.Expression,
             expression:         EbContext.Expression,
-            whileContext:       EbContext.While
+            For:                EbContext.For,
+            While:              EbContext.While,
+            If:                 EbContext.If,
+            CaseLabel:          EbContext.CaseLabel,
+            Repeat:             EbContext.Repeat
         },
         },
         Grammar.reservedWords + " SELF SUPER"
         Grammar.reservedWords + " SELF SUPER"
         ),
         ),

+ 15 - 11
src/grammar.js

@@ -126,12 +126,12 @@ var ifStatement = and("IF", context(and(expression, required("THEN", "THEN expec
                                         repeat(and("ELSIF", expression, required("THEN", "THEN expected"), statementSequence)),
                                         repeat(and("ELSIF", expression, required("THEN", "THEN expected"), statementSequence)),
                                         optional(and("ELSE", statementSequence)),
                                         optional(and("ELSE", statementSequence)),
                                         "END"), 
                                         "END"), 
-                                    Context.If));
+                                    contexts.If));
 
 
 var label = or(integer, string, ident);
 var label = or(integer, string, ident);
 var labelRange = context(and(label, optional(and("..", label))), Context.CaseRange);
 var labelRange = context(and(label, optional(and("..", label))), Context.CaseRange);
 var caseLabelList = context(and(labelRange, repeat(and(",", labelRange))), Context.CaseLabelList);
 var caseLabelList = context(and(labelRange, repeat(and(",", labelRange))), Context.CaseLabelList);
-var caseParser = optional(context(and(caseLabelList, ":", statementSequence), Context.CaseLabel));
+var caseParser = optional(context(and(caseLabelList, ":", statementSequence), contexts.CaseLabel));
 var caseStatement = and("CASE", context(and(expression
 var caseStatement = and("CASE", context(and(expression
                       , "OF", caseParser, repeat(and("|", caseParser)), "END")
                       , "OF", caseParser, repeat(and("|", caseParser)), "END")
                       , Context.Case));
                       , Context.Case));
@@ -140,15 +140,19 @@ var whileStatement = and("WHILE",
                          context(and(expression, "DO", statementSequence, 
                          context(and(expression, "DO", statementSequence, 
                                      repeat(and("ELSIF", expression, "DO", statementSequence)),
                                      repeat(and("ELSIF", expression, "DO", statementSequence)),
                                      "END"),
                                      "END"),
-                                 contexts.whileContext));
-var repeatStatement = and("REPEAT", context(statementSequence, Context.Repeat)
-                        , "UNTIL", context(expression, Context.Until));
-
-var forStatement = context(and("FOR", ident, ":=", expression, "TO", expression
-                             , optional(and("BY", constExpression))
-                             , emit("DO", Context.emitForBegin)
-                             , statementSequence, required("END", "END expected (FOR)"))
-                         , Context.For);
+                                 contexts.While));
+var repeatStatement = and("REPEAT", 
+                          context(and(statementSequence, 
+                                      "UNTIL", 
+                                      context(expression, Context.Until)), 
+                                  contexts.Repeat));
+
+var forStatement = and("FOR", 
+                       context(and(ident, ":=", expression, "TO", expression
+                                 , optional(and("BY", constExpression))
+                                 , emit("DO", Context.emitForBegin)
+                                 , statementSequence, required("END", "END expected (FOR)"))
+                             , contexts.For));
 
 
 var fieldList = makeFieldList(
 var fieldList = makeFieldList(
         identdef,
         identdef,

+ 5 - 1
src/oberon/oberon_grammar.js

@@ -66,7 +66,11 @@ exports.language = {
             variableDeclaration: ObContext.VariableDeclaration,
             variableDeclaration: ObContext.VariableDeclaration,
             addOperator:        Context.AddOperator,
             addOperator:        Context.AddOperator,
             expression:         Context.Expression,
             expression:         Context.Expression,
-            whileContext:       Context.While
+            For:                Context.For,
+            While:              Context.While,
+            If:                 Context.If,
+            CaseLabel:          Context.CaseLabel,
+            Repeat:             Context.Repeat
         },
         },
         Grammar.reservedWords
         Grammar.reservedWords
         ),
         ),

+ 15 - 3
test/test_unit_eberon.js

@@ -362,13 +362,25 @@ exports.suite = {
         context(grammar.declarationSequence,
         context(grammar.declarationSequence,
                 "VAR i: INTEGER;"),
                 "VAR i: INTEGER;"),
         pass("PROCEDURE p(); BEGIN v1 <- 0; v2 <-0; END p;",
         pass("PROCEDURE p(); BEGIN v1 <- 0; v2 <-0; END p;",
-             "PROCEDURE p(); BEGIN i <- 0; END p;",
-             "PROCEDURE p(); BEGIN WHILE FALSE DO v <- 0; END; WHILE FALSE DO v <- 0; END; END p;"
+             "PROCEDURE p(); BEGIN i <- 0; ASSERT(i = 0); END p;",
+             "PROCEDURE p(); BEGIN WHILE FALSE DO v <- 0; ASSERT(v = 0); END; WHILE FALSE DO v <- 0; END; END p;",
+             "PROCEDURE p(); BEGIN WHILE FALSE DO i1 <- 0; WHILE FALSE DO i2 <- 0; ASSERT(i1 = 0); ASSERT(i2 = 0); END; END; END p;",
+             "PROCEDURE p(); BEGIN IF FALSE THEN v <- 0; ASSERT(v = 0); END; IF FALSE THEN v <- 0; END; END p;",
+             "PROCEDURE p(); BEGIN IF FALSE THEN v <- 0; END; IF FALSE THEN v <- 0; END; END p;",
+             "PROCEDURE p(); BEGIN IF FALSE THEN v <- 0; ELSIF FALSE THEN v <- 0; ELSE v <- 0; END; END p;",
+             "PROCEDURE p(); BEGIN CASE i OF 0: v <- 0 | 1: v <- 1; ; ASSERT(v = 1); END; END p;",
+             "PROCEDURE p(); BEGIN REPEAT v <- 0; UNTIL FALSE; REPEAT v <- 0; UNTIL FALSE; END p;",
+             "PROCEDURE p(); BEGIN REPEAT v <- 0; ASSERT(v = 0); UNTIL v # 0; END p;",
+             "PROCEDURE p(); BEGIN FOR i := 0 TO 10 DO v <- 0; END; FOR i := 0 TO 10 DO v <- 0; END; END p;"
              ),
              ),
         fail(["PROCEDURE p(); BEGIN v <- 0; v <-0; END p;", "'v' already declared"],
         fail(["PROCEDURE p(); BEGIN v <- 0; v <-0; END p;", "'v' already declared"],
              ["PROCEDURE p(); VAR v: INTEGER; BEGIN v <- 0; END p;", "'v' already declared"],
              ["PROCEDURE p(); VAR v: INTEGER; BEGIN v <- 0; END p;", "'v' already declared"],
              ["PROCEDURE p(); BEGIN v <- 0; WHILE FALSE DO v <- 0; END; END p;", 
              ["PROCEDURE p(); BEGIN v <- 0; WHILE FALSE DO v <- 0; END; END p;", 
-              "'v' already declared in procedure scope"]
+              "'v' already declared in procedure scope"],
+             ["PROCEDURE p(); BEGIN i <- 0; IF FALSE THEN i <- 0; END; END p;",
+              "'i' already declared in procedure scope"],
+             ["PROCEDURE p(); BEGIN WHILE FALSE DO i <- 0; WHILE FALSE DO i <- 0; END; END; END p;",
+              "'i' already declared in procedure scope"]
             )
             )
         ),
         ),
     "read-only": testWithContext(
     "read-only": testWithContext(