Explorar el Código

local procedures

Vladislav Folts hace 12 años
padre
commit
7383465879
Se han modificado 4 ficheros con 174 adiciones y 55 borrados
  1. 90 54
      src/context.js
  2. 40 0
      test/expected/proc_local.js
  3. 29 0
      test/input/proc_local.ob
  4. 15 1
      test/test_unit.js

+ 90 - 54
src/context.js

@@ -18,13 +18,36 @@ var Class = ImportRTL.Class;
 var basicTypes = Type.basic;
 var Symbol = Type.Symbol;
 
-function getSymbol(context, id){
+var FoundSymbol = Class.extend({
+    init: function(symbol, scope){
+        this.__symbol = symbol;
+        this.__scope = scope;
+    },
+    symbol: function(){return this.__symbol;},
+    scope: function(){return this.__scope;}
+});
+
+function getSymbolAndScope(context, id){
     var s = context.findSymbol(id);
     if (!s)
         throw new Errors.Error("undeclared identifier: '" + id + "'");
     return s;
 }
 
+function getSymbol(context, id){
+    return getSymbolAndScope(context, id).symbol();
+}
+
+function unwrapType(type){
+    if (!(type instanceof Type.TypeId))
+        throw new Errors.Error("type name expected");
+    return type.type();
+}
+
+function getTypeSymbol(context, id){
+    return unwrapType(getSymbol(context, id).info());
+}
+
 function throwTypeMismatch(from, to){
     throw new Errors.Error("type mismatch: expected '" + to.description() +
                            "', got '" + from.description() + "'");
@@ -83,7 +106,7 @@ var ChainedContext = Class.extend({
     findSymbol: function(id){return this.__parent.findSymbol(id);},
     addSymbol: function(s){this.__parent.addSymbol(s);},
     currentScope: function(s){return this.__parent.currentScope();},
-    pushScope: function(){this.__parent.pushScope();},
+    pushScope: function(id){this.__parent.pushScope(id);},
     popScope: function(){this.__parent.popScope();},
     setType: function(type){this.__parent.setType(type);},
     setDesignator: function(d){this.__parent.setDesignator(d);},
@@ -178,16 +201,18 @@ exports.BaseType = ChainedContext.extend({
 });
 
 var DesignatorInfo = Class.extend({
-    init: function(code, refCode, type, info){
+    init: function(code, refCode, type, info, scope){
         this.__code = code;
         this.__refCode = refCode;
         this.__type = type;
         this.__info = info;
+        this.__scope = scope;
     },
     code: function(){return this.__code;},
     refCode: function(){return this.__refCode(this.__code);},
     type: function(){return this.__type;},
-    info: function(){return this.__info;}
+    info: function(){return this.__info;},
+    scope: function(){return this.__scope;}
 });
 
 exports.Designator = ChainedContext.extend({
@@ -195,15 +220,19 @@ exports.Designator = ChainedContext.extend({
         ChainedContext.prototype.init.bind(this)(context);
         this.__currentType = undefined;
         this.__info = undefined;
+        this.__scope = undefined;
         this.__code = new Code.SimpleGenerator();
         this.__indexExpression = undefined;
         this.__derefCode = undefined;
         this.__propCode = undefined;
     },
     setIdent: function(id){
+        var scope;
         var t = this.__currentType;
         if ( t === undefined){
-            var s = getSymbol(this.parent(), id);
+            var found = getSymbolAndScope(this.parent(), id);
+            scope = found.scope();
+            var s = found.symbol();
             var info = s.info();
             if (info instanceof Type.Type || s.isType() || s.isProcedure())
                 this.__currentType = info;
@@ -215,13 +244,14 @@ exports.Designator = ChainedContext.extend({
             this.__handleDeref();
             this.__denote(id);
         }
-        else if (!(t instanceof Type.Record)
-              && !(t instanceof Module.Type)
-              && !(t instanceof Module.AnyType))
-            throw new Errors.Error("cannot designate '" + t.description() + "'");
-        else
+        else if (t instanceof Type.Record
+              || t instanceof Module.Type
+              || t instanceof Module.AnyType)
             this.__denote(id);
+        else
+            throw new Errors.Error("cannot designate '" + t.description() + "'");
 
+        this.__scope = scope;
         this.__code.write(id);
     },
     codeGenerator: function(){return this.__code;},
@@ -291,8 +321,9 @@ exports.Designator = ChainedContext.extend({
     },
     endParse: function(){
         var code = this.__code.result();
+        var refCode = this.__makeRefCode.bind(this);
         this.parent().setDesignator(
-            new DesignatorInfo(code, this.__makeRefCode.bind(this), this.__currentType, this.__info));
+            new DesignatorInfo(code, refCode, this.__currentType, this.__info, this.__scope));
     },
     __makeRefCode: function(code){
         if (this.__derefCode)
@@ -305,20 +336,11 @@ exports.Designator = ChainedContext.extend({
     }
 });
 
-function unwrapType(type){
-    if (!(type instanceof Type.TypeId))
-        throw new Errors.Error("type name expected");
-    return type.type();
-}
-
 exports.Type = ChainedContext.extend({
-    init: function TypeContext(context){ChainedContext.prototype.init.bind(this)(context);},
-    setIdent: function(id){
-        var s = this.findSymbol(id);
-        if (!s)
-            throw new Errors.Error("undeclared type: '" + id + "'");
-        this.setType(unwrapType(s.info()));
-    }
+    init: function TypeContext(context){
+        ChainedContext.prototype.init.bind(this)(context);
+    },
+    setIdent: function(id){this.setType(getTypeSymbol(this, id));}
 });
 
 exports.FormalType = exports.Type.extend({
@@ -364,9 +386,7 @@ exports.FormalParameters = ChainedContext.extend({
         this.__arguments.push(arg);
     },
     setIdent: function(id){
-        var parent = this.parent();
-        var s = getSymbol(parent, id);
-        this.__result = unwrapType(s.info());
+        this.__result = getTypeSymbol(this.parent(), id);
     },
     endParse: function(){
         this.__type.define(this.__arguments, this.__result);
@@ -401,7 +421,7 @@ exports.ProcDecl = ChainedContext.extend({
         if (this.__name === undefined){ // first call
             this.__name = id;
             gen.write("\nfunction " + id + "(");
-            this.parent().pushScope();
+            this.parent().pushScope("procedure");
         }
         else if (this.__name === id){
             gen.closeScope();
@@ -509,13 +529,17 @@ exports.PointerDecl = ChainedContext.extend({
         this.__base = type;
     },
     findSymbol: function(id){
-        var existing = this.parent().findSymbol(id);
+        var parent = this.parent();
+        var existing = parent.findSymbol(id);
         if (existing)
             return existing;
 
-        var resolve = function(){return getSymbol(this.__parent, id).info().type();};
-        var type = new Type.ForwardRecord(resolve.bind(this));
-        return new Symbol(id, new Type.TypeId(type));
+        var resolve = function(){return getSymbol(parent, id).info().type();};
+        var type = new Type.ForwardRecord(resolve);
+        return new FoundSymbol(
+            new Symbol(id, new Type.TypeId(type)),
+            this.currentScope()
+            );
     },
     genTypeName: function(){
         return this.__name + "$base";
@@ -1405,8 +1429,13 @@ exports.ExpressionProcedureCall = ProcedureCall.extend({
         else{
             var d = this.__designator;
             var info = d.info();
-            if (info instanceof Procedure.Std)
-                throw new Errors.Error(info.description() + " cannot be referenced");
+            if (info instanceof Type.Procedure){
+                if (info instanceof Procedure.Std)
+                    throw new Errors.Error(info.description() + " cannot be referenced");
+                var scope = d.scope();
+                if (scope && scope.id() == "procedure")
+                    throw new Errors.Error("local procedure '" + d.code() + "' cannot be referenced");
+            }
             parent.setDesignator(d);
         }
     }
@@ -1422,8 +1451,7 @@ exports.RecordDecl = ChainedContext.extend({
     },
     addField: function(name, type) {this.__type.addField(name, type);},
     setBaseType: function(id){
-        var s = getSymbol(this.parent(), id);
-        this.__type.setBaseType(unwrapType(s.info()));
+        this.__type.setBaseType(getTypeSymbol(this.parent(), id));
     },
     endParse: function(){
         var type = this.__type;
@@ -1521,23 +1549,30 @@ var ModuleImport = ChainedContext.extend({
 });
 exports.ModuleImport = ModuleImport;
 
+var stdSymbols = function(){
+    var symbols = {};
+    for(var t in basicTypes){
+        var type = basicTypes[t];
+        symbols[type.name()] = new Symbol(type.name(), new Type.TypeId(type));
+    }
+    symbols["LONGREAL"] = new Symbol("LONGREAL", new Type.TypeId(basicTypes.real));
+    
+    var predefined = Procedure.predefined;
+    for(var i = 0; i < predefined.length; ++i){
+        var s = predefined[i];
+        symbols[s.id()] = s;
+    }
+    return symbols;
+}();
+
 var Scope = Class.extend({
-    init: function Scope(){
-        var symbols = {};
-        for(var t in basicTypes){
-            var type = basicTypes[t];
-            symbols[type.name()] = new Symbol(type.name(), new Type.TypeId(type));
-        }
-        symbols["LONGREAL"] = new Symbol("LONGREAL", new Type.TypeId(basicTypes.real));
-        
-        var predefined = Procedure.predefined;
-        for(var i = 0; i < predefined.length; ++i){
-            var s = predefined[i];
-            symbols[s.id()] = s;
-        }
-            
-        this.__symbols = symbols;
+    init: function Scope(id){
+        this.__id = id;
+        this.__symbols = {};
+        for(var p in stdSymbols)
+            this.__symbols[p] = stdSymbols[p];
     },
+    id: function(){return this.__id;},
     addSymbol: function(symbol){
         var id = symbol.id();
         if (this.findSymbol(id))
@@ -1573,14 +1608,15 @@ exports.Context = Class.extend({
     addSymbol: function(symbol){this.currentScope().addSymbol(symbol);},
     findSymbol: function(ident){
         for(var i = this.__scopes.length; i--;){
-            var s = this.__scopes[i].findSymbol(ident);
+            var scope = this.__scopes[i];
+            var s = scope.findSymbol(ident);
             if (s)
-                return s;
+                return new FoundSymbol(s, scope);
         }
         return undefined;
     },
     currentScope: function(){return this.__scopes[this.__scopes.length - 1];},
-    pushScope: function(){this.__scopes.push(new Scope());},
+    pushScope: function(id){this.__scopes.push(new Scope(id));},
     popScope: function(){this.__scopes.pop();},
     handleExpression: function(){},
     handleLiteral: function(){},

+ 40 - 0
test/expected/proc_local.js

@@ -0,0 +1,40 @@
+var RTL$ = {
+    extend: function extend(methods){
+        methods.__proto__ = this.prototype; // make instanceof work
+
+        // to see constructor name in diagnostic
+        var result = methods.init;
+        methods.constructor = result.prototype.constructor;
+
+        result.prototype = methods;
+        result.extend = extend;
+        return result;
+    }
+};
+var m = function (){
+
+function p1(arg1/*INTEGER*/){
+	var T1 = RTL$.extend({
+		init: function T1(){
+			this.field1 = 0;
+		}
+	});
+	var i1 = 0;var j1 = 0;
+	var t1 = new T1();
+	
+	function p2(arg2/*BOOLEAN*/){
+		var T2 = RTL$.extend({
+			init: function T2(){
+				this.field2 = false;
+			}
+		});
+		var b = false;
+		var t2 = new T2();
+		b = arg2;
+		t1.field1 = i1;
+		t2.field2 = b;
+	}
+	p2(true);
+	p2(false);
+}
+}();

+ 29 - 0
test/input/proc_local.ob

@@ -0,0 +1,29 @@
+MODULE m;
+
+PROCEDURE p1(arg1: INTEGER);
+TYPE
+    T1 = RECORD 
+        field1: INTEGER END;
+VAR 
+    i1, j1: INTEGER; 
+    t1: T1;
+
+    PROCEDURE p2(arg2: BOOLEAN);
+    TYPE
+        T2 = RECORD 
+            field2: BOOLEAN END;
+    VAR
+        b: BOOLEAN;
+        t2: T2;
+    BEGIN
+        b := arg2;
+        t1.field1 := i1;
+        t2.field2 := b;
+    END p2;
+
+BEGIN
+    p2(TRUE);
+    p2(FALSE);
+END p1;
+
+END m.

+ 15 - 1
test/test_unit.js

@@ -116,7 +116,7 @@ comment: function(){
     test.expectError("T = ARRAY10 OF ARRAY 5 OF INTEGER", "not parsed");
     test.expectError("T = ARRAY 10OF ARRAY 5 OF INTEGER", "not parsed");
     test.expectError("T = ARRAY 10 OFARRAY 5 OF INTEGER", "not parsed");
-    test.expectError("T = ARRAY 10 OF ARRAY5 OF INTEGER", "undeclared type: 'ARRAY5'");
+    test.expectError("T = ARRAY 10 OF ARRAY5 OF INTEGER", "undeclared identifier: 'ARRAY5'");
     test.expectError("T = ARRAY 10 OF ARRAY 5OF INTEGER", "not parsed");
     test.expectError("T = ARRAY 10 OF ARRAY 5 OFINTEGER", "not parsed");
 },
@@ -955,6 +955,20 @@ procedure: function(){
          ["p3()", "procedure returning a result cannot be used as a statement"]
          )
 ),
+"local procedure": testWithContext(
+    context(Grammar.procedureDeclaration,
+            "TYPE ProcType = PROCEDURE;" +
+            "VAR procVar: ProcType;" +
+            "PROCEDURE procWithProcArg(p: ProcType); END procWithProcArg;"),
+    pass("PROCEDURE p; PROCEDURE innerP; END innerP; END p",
+         "PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN innerP() END p"),
+    fail(["PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN procVar := innerP END p",
+          "local procedure 'innerP' cannot be referenced"],
+         ["PROCEDURE p; PROCEDURE innerP; END innerP; BEGIN procWithProcArg(innerP) END p",
+          "local procedure 'innerP' cannot be referenced"],
+         ["PROCEDURE p; PROCEDURE innerP; VAR innerV: INTEGER; END innerP; BEGIN innerV := 0 END p",
+          "undeclared identifier: 'innerV'"])
+    ),
 "procedure assignment": function(){
     var test = setupWithContext(
           Grammar.statement