Browse Source

eberon methods: implement super calls

Vladislav Folts 11 years ago
parent
commit
16e0e5183d

+ 38 - 5
src/eberon/eberon_context.js

@@ -7,17 +7,37 @@ var Symbol = require("symbol.js");
 var Procedure = require("procedure.js");
 var Type = require("type.js");
 
+function methodCallGenerator(context, id, type){
+    return new Procedure.CallGenerator(context, id, type);
+}
+
+var SuperCallGenerator = Procedure.CallGenerator.extend({
+    init: function(context, id, type){
+        Procedure.CallGenerator.prototype.init.call(this, context, id, type);
+    },
+    prolog: function(){
+        return Procedure.CallGenerator.prototype.prolog.call(this) + "this";
+    },
+    writeArgumentCode: function(e, pos, isVar, convert){
+        Procedure.CallGenerator.prototype.writeArgumentCode.call(this, e, pos + 1, isVar, convert);
+    }
+});
+
+function superMethodCallGenerator(context, id, type){
+    return new SuperCallGenerator(context, id, type);
+}
+
 var MethodType = Type.Procedure.extend({
-    init: function EberonContext$MethodType(type){
+    init: function EberonContext$MethodType(type, callGenerator){
         Type.Procedure.prototype.init.call(this);
         this.__type = type;
+        this.__callGenerator = callGenerator;
     },
+    procType: function(){return this.__type;},
     args: function(){return this.__type.args();},
     result: function(){return this.__type.result();},
     description: function(){return this.__type.description();},
-    callGenerator: function(context, id){
-        return new Procedure.CallGenerator(context, id, this);
-    }
+    callGenerator: function(context, id){return this.__callGenerator(context, id, this);}
 });
 
 var ProcOrMethodId = Context.Chained.extend({
@@ -41,6 +61,7 @@ var ProcOrMethodId = Context.Chained.extend({
 });
 
 function getMethodSelf(){}
+function getMethodSuper(){}
 
 var Designator = Context.Designator.extend({
     init: function EberonContext$Designator(parent){
@@ -49,6 +70,10 @@ var Designator = Context.Designator.extend({
     handleLiteral: function(s){
         if (s == "SELF")
             this.handleSymbol(new Symbol.Found(this.handleMessage(getMethodSelf)), "this");
+        else if (s == "SUPER"){
+            var ms = this.handleMessage(getMethodSuper);
+            this.handleSymbol(new Symbol.Found(ms.symbol), ms.code);
+        }
         else
             Context.Designator.prototype.handleLiteral.call(this, s);
     }
@@ -67,6 +92,14 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     handleMessage: function(msg){
         if (msg == getMethodSelf)
             return this.__selfSymbol;
+        if (msg == getMethodSuper){
+            if (this.__isNew)
+                throw new Errors.Error("there is no super method in base type(s)");
+            return {
+                symbol: new Symbol.Symbol("method", new MethodType(this.__methodType.procType(), superMethodCallGenerator)),
+                code: this.__boundType.baseType().name() + ".prototype." + this.__methodId.id() + ".call"
+            };
+        }
         return Context.ProcDecl.prototype.handleMessage.call(this, msg);
     },
     handleMethodOrProc: function(id, type){
@@ -90,7 +123,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     },
     setType: function(type){
         Context.ProcDecl.prototype.setType.call(this, type);
-        this.__methodType = new MethodType(type);
+        this.__methodType = new MethodType(type, methodCallGenerator);
     },
     handleLiteral: function(s){
         if (s == "NEW"){

+ 1 - 1
src/eberon/eberon_grammar.js

@@ -24,7 +24,7 @@ function makeProcedureHeading(formalParameters){
 
 function makeDesignator(selector){
     return context(
-        and(or("SELF", Grammar.qualident), repeat(selector)), EbContext.Designator);
+        and(or("SELF", "SUPER", Grammar.qualident), repeat(selector)), EbContext.Designator);
 }
 
 function makeProcedureDeclaration(formalParameters, procedureBody){

+ 14 - 0
test/expected/eberon/method.js

@@ -20,7 +20,21 @@ var T = RTL$.extend({
 		this.i = 0;
 	}
 });
+var D = T.extend({
+	init: function D(){
+		T.prototype.init.call(this);
+	}
+});
 T.prototype.p = function(){
 	this.i = 123;
 }
+T.prototype.p2 = function(i/*INTEGER*/){
+	return i;
+}
+D.prototype.p = function(){
+	T.prototype.p.call(this);
+}
+D.prototype.p2 = function(i/*INTEGER*/){
+	return T.prototype.p2.call(this, i);
+}
 }();

+ 14 - 0
test/input/eberon/method.ob

@@ -3,10 +3,24 @@ TYPE
     T = RECORD
 		i: INTEGER
 	END;
+    D = RECORD(T) END;
 
 PROCEDURE T.p(), NEW;
 BEGIN
 	SELF.i := 123;
 END T.p;
 
+PROCEDURE T.p2(i: INTEGER): INTEGER, NEW;
+    RETURN i
+END T.p2;
+
+PROCEDURE D.p();
+BEGIN
+    SUPER()
+END D.p;
+
+PROCEDURE D.p2(i: INTEGER): INTEGER;
+    RETURN SUPER(i)
+END D.p2;
+
 END m.

+ 14 - 0
test/input/eberon/run/method.ob

@@ -6,6 +6,7 @@ TYPE
 VAR
     pCalled: BOOLEAN;
     pDerivedCalled: BOOLEAN;
+    pSuperCalled: INTEGER;
 	r: T;
 	rd: TD;
 
@@ -14,11 +15,21 @@ BEGIN
 	pCalled := TRUE;
 END T.p;
 
+PROCEDURE T.pSuper(i: INTEGER), NEW;
+BEGIN
+	pSuperCalled := i;
+END T.pSuper;
+
 PROCEDURE TD.p();
 BEGIN
 	pDerivedCalled := TRUE;
 END TD.p;
 
+PROCEDURE TD.pSuper(i: INTEGER);
+BEGIN
+	SUPER(i);
+END TD.pSuper;
+
 BEGIN
 	ASSERT(~pCalled);
 	ASSERT(~pDerivedCalled);
@@ -30,4 +41,7 @@ BEGIN
 	rd.p();
 	ASSERT(~pCalled);
 	ASSERT(pDerivedCalled);
+
+	rd.pSuper(123);
+	ASSERT(pSuperCalled = 123);
 END m.

+ 9 - 0
test/test_unit_eberon.js

@@ -64,5 +64,14 @@ exports.suite = {
             ),
     pass("o.f()"),
     fail(["o.p()", "procedure returning no result cannot be used in an expression"])
+    ),
+"method super call": testWithContext(
+    context(grammar.declarationSequence,
+              "TYPE T = RECORD END; D = RECORD(T) END;"
+            + "PROCEDURE T.p(), NEW; END T.p;"
+            ),
+    pass("PROCEDURE D.p(); BEGIN SUPER() END D.p;"),
+    fail(["PROCEDURE D.pNoSuper(), NEW; BEGIN SUPER() END D.pNoSuper;",
+          "there is no super method in base type(s)"])
     )
 };