Browse Source

make possible designate procedure call result in Eberon. E.g.: proc().field()()

Vladislav Folts 11 years ago
parent
commit
8815864eaf

BIN
bin/compiled.zip


+ 52 - 102
src/context.js

@@ -330,8 +330,12 @@ exports.Designator = ChainedContext.extend({
                                      + (length - 1)
                                      + ", got " + value );
         }
-        this.__currentType = index.type;
-        this.__info = index.info;
+        return index;
+    },
+    _advance: function(type, info, code){
+        this.__currentType = type;
+        this.__info = info;
+        this.__code = code;
     },
     _indexSequence: function(type, info){
         var isArray = type instanceof Type.Array;
@@ -349,16 +353,16 @@ exports.Designator = ChainedContext.extend({
     },
     handleLiteral: function(s){
         if (s == "]" || s == ","){
-            this.__handleIndexExpression();
+            var index = this.__handleIndexExpression();
             var indexCode = Code.derefExpression(this.__indexExpression).code();
             this.__propCode = indexCode;
             var code = this.__derefCode + "[" + indexCode + "]";
-            if (this.__currentType == basicTypes.ch){
+            if (index.type == basicTypes.ch){
                 this.__lval = code;
                 code = this.__derefCode + ".charCodeAt(" + indexCode + ")";
             }
-            this.__code += code;
-            }
+            this._advance(index.type, index.info, this.__code + code);
+        }
         if (s == "[" || s == ","){
             this.__derefCode = this.__code;
             this.__code = "";
@@ -996,8 +1000,17 @@ exports.Term = ChainedContext.extend({
     },
     type: function(){return this.__expression.type();},
     setDesignator: function(d){
-        var value;
         var info = d.info();
+        if (info instanceof Type.ProcedureId){
+            var proc = Type.procedureType(info);
+            if (proc instanceof Procedure.Std)
+                throw new Errors.Error(proc.description() + " cannot be referenced");
+            var scope = d.scope();
+            if (scope instanceof Scope.Procedure)
+                throw new Errors.Error("local procedure '" + d.code() + "' cannot be referenced");
+        }
+
+        var value;
         if (info instanceof Type.Const)
             value = Type.constValue(info);
         this.handleExpression(
@@ -1013,9 +1026,6 @@ exports.Term = ChainedContext.extend({
             code, type, undefined, value));
     },
     handleFactor: function(e){
-        var type = e.type();
-        if (!type)
-            throw new Errors.Error("procedure returning no result cannot be used in an expression");
         this.handleExpression(e);
     },
     endParse: function(){this.parent().handleTerm(this.__expression);},
@@ -1237,6 +1247,10 @@ exports.Expression = ChainedContext.extend({
     },
     codeGenerator: function(){return nullCodeGenerator;},
     endParse: function(){
+        var type = this.__expression.type();
+        if (!type)
+            throw new Errors.Error("procedure returning no result cannot be used in an expression");
+
         var parent = this.parent();
         parent.codeGenerator().write(this.__expression.code());
         parent.handleExpression(this.__expression);
@@ -1528,20 +1542,6 @@ exports.CheckAssignment = ChainedContext.extend({
     }
 });
 
-exports.Assignment = ChainedContext.extend({
-    init: function AssignmentContext(context){
-        ChainedContext.prototype.init.call(this, context);
-        this.__left = undefined;
-    },
-    codeGenerator: function(){return nullCodeGenerator;},
-    setDesignator: function(d){
-        this.__left = Code.makeExpression(d.code(), d.type(), d);
-    },
-    handleExpression: function(e){
-        this.parent().codeGenerator().write(op.assign(this.__left, e, this.language()));
-    }
-});
-
 exports.ConstDecl = ChainedContext.extend({
     init: function ConstDeclContext(context){
         ChainedContext.prototype.init.call(this, context);
@@ -1631,11 +1631,10 @@ exports.FieldListDeclaration = HandleSymbolAsType.extend({
     }
 });
 
-function assertProcType(d){
-    var type = d.type();
+function assertProcType(type, info){
     var unexpected;
     if ( !type )
-        unexpected = d.info().idType();
+        unexpected = info.idType();
     else if (!(type instanceof Type.Procedure) && !(type instanceof Module.AnyType))
         unexpected = type.description();
     if (unexpected)
@@ -1643,86 +1642,22 @@ function assertProcType(d){
     return type;
 }
 
+function assertProcStatementResult(type){
+    if (type && !(type instanceof Module.AnyType))
+        throw new Errors.Error("procedure returning a result cannot be used as a statement");
+}
+
+function beginCallMsg(){}
+function endCallMsg(){}
+
 exports.ActualParameters = ChainedContext.extend({
     init: function ActualParametersContext(context){
         ChainedContext.prototype.init.call(this, context);
-        this.parent().hasActualParameters();
-    }
-});
-
-var ProcedureCall = ChainedContext.extend({
-    init: function ProcedureCallContext(context){
-        ChainedContext.prototype.init.call(this, context);
-        this.__type = undefined;
-        this.__procCall = undefined;
-        this.__code = Code.makeSimpleGenerator();
-    },
-    setDesignator: function(d){
-        this.__type = assertProcType(d);
-        var l = this.language();
-        this.__procCall = this.__type.callGenerator(
-              { 
-                types: l.types
-              , rtl: l.rtl
-              , qualifyScope: this.qualifyScope.bind(this)
-              }
-            , d.code());
-        this.__callExpression = undefined;
-    },
-    codeGenerator: function(){return this.__code;},
-    type: function(){return this.__type;},
-    hasActualParameters: function(){},
-    handleExpression: function(e){this.__procCall.handleArgument(e);},
-    callExpression: function(){return this.__callExpression;},
-    endParse: function(){this.__callExpression = this.__procCall.end();}
-});
-
-exports.StatementProcedureCall = ProcedureCall.extend({
-    init: function StatementProcedureCallContext(context){
-        ProcedureCall.prototype.init.call(this, context);
+        this.handleMessage(beginCallMsg);
     },
+    handleLiteral: function(){}, // do not propagate ","
     endParse: function(){
-        ProcedureCall.prototype.endParse.call(this);
-        var e = this.callExpression();
-        var type = e.type();
-        if  (type && !(type instanceof Module.AnyType ))
-            throw new Errors.Error("procedure returning a result cannot be used as a statement");
-        this.parent().codeGenerator().write(e.code());
-    }
-});
-
-exports.ExpressionProcedureCall = ProcedureCall.extend({
-    init: function ExpressionProcedureCall(context){
-        ProcedureCall.prototype.init.call(this, context);
-        this.__designator = undefined;
-        this.__hasActualParameters = false;
-    },
-    setDesignator: function(d){
-        this.__designator = d;
-    },
-    hasActualParameters: function(){
-        ProcedureCall.prototype.setDesignator.call(this, this.__designator);
-        this.__hasActualParameters = true;
-    },
-    endParse: function(){
-        var parent = this.parent();
-        if (this.__hasActualParameters){
-            ProcedureCall.prototype.endParse.call(this);
-            parent.handleFactor(this.callExpression());
-        }
-        else{
-            var d = this.__designator;
-            var info = d.info();
-            if (info instanceof Type.ProcedureId){
-                var proc = Type.procedureType(info);
-                if (proc instanceof Procedure.Std)
-                    throw new Errors.Error(proc.description() + " cannot be referenced");
-                var scope = d.scope();
-                if (scope instanceof Scope.Procedure)
-                    throw new Errors.Error("local procedure '" + d.code() + "' cannot be referenced");
-            }
-            parent.setDesignator(d);
-        }
+        this.handleMessage(endCallMsg);
     }
 });
 
@@ -2009,10 +1944,25 @@ exports.Context = Class.extend({
     }
 });
 
+function makeProcCall(context, type, info, code){
+    assertProcType(type, info);
+    var l = context.language();
+    return type.callGenerator(
+        { types: l.types, 
+          rtl: l.rtl, 
+          qualifyScope: context.qualifyScope.bind(context)
+        }, 
+        code);
+}
+
 exports.AddArgumentMsg = AddArgumentMsg;
+exports.assertProcStatementResult = assertProcStatementResult;
+exports.beginCallMsg = beginCallMsg;
+exports.endCallMsg = endCallMsg;
 exports.Chained = ChainedContext;
 exports.endParametersMsg = endParametersMsg;
 exports.getSymbolAndScope = getSymbolAndScope;
+exports.makeProcCall = makeProcCall;
 exports.unwrapType = unwrapType;
 exports.IdentdefInfo = IdentdefInfo;
 exports.RelationOps = RelationOps;

+ 15 - 3
src/eberon/EberonCast.ob

@@ -1,13 +1,25 @@
 MODULE EberonCast;
 IMPORT Cast, EberonString, Types;
 
+PROCEDURE isOpenCharArray(type: Types.PType): BOOLEAN;
+VAR
+    result: BOOLEAN;
+    array: Types.PArray;
+BEGIN
+    IF type IS Types.PArray THEN
+        array := type(Types.PArray);
+        result := (Types.arrayElementsType(array^) = Types.basic.ch)
+                & (Types.arrayLength(array^) = Types.openArrayLength);
+    END;
+    RETURN result
+END isOpenCharArray;
+
 PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER;
 VAR
     result: INTEGER;
 BEGIN
-    IF   ((from = EberonString.string) 
-            & ((to IS Types.PString) OR (to IS Types.PArray) & (Types.arrayLength(to(Types.PArray)^) = Types.openArrayLength)))
-      OR ((from IS Types.PString) & (to = EberonString.string)) THEN
+    IF   ((from = EberonString.string) & ((to IS Types.PString) OR isOpenCharArray(to))
+      OR ((from IS Types.PString) & (to = EberonString.string))) THEN
         IF toVar THEN 
             result := Cast.errVarParameter;
         ELSE

+ 84 - 1
src/eberon/eberon_context.js

@@ -95,9 +95,20 @@ var MethodVariable = Type.Variable.extend({
     idType: function(){return "method";}
 });
 
+var ResultVariable = Type.Variable.extend({
+    init: function(e){
+        this.__e = e;
+    },
+    expression: function(){return this.__e;},
+    type: function(){return this.__e.type();},
+    isReadOnly: function(){return true;},
+    idType: function(){return "procedure call " + (this.type() ? "result" : "statement");}
+});
+
 var Designator = Context.Designator.extend({
     init: function EberonContext$Designator(parent){
         Context.Designator.prototype.init.call(this, parent);
+        this.__procCall = undefined;
     },
     _indexSequence: function(type, info){
         if (type == EberonString.string()){
@@ -114,6 +125,19 @@ var Designator = Context.Designator.extend({
             ? new MethodVariable(type)
             : Context.Designator.prototype._makeDenoteVar(type, isReadOnly);
     },
+    handleMessage: function(msg){
+        if (msg == Context.beginCallMsg)
+            return this.__beginCall();
+        if (msg == Context.endCallMsg)
+            return this.__endCall();
+        return Context.Designator.prototype.handleMessage.call(this, msg);
+    },
+    handleExpression: function(e){
+        if (this.__procCall)
+            this.__procCall.handleArgument(e);
+        else
+            Context.Designator.prototype.handleExpression.call(this, e);
+    },
     handleLiteral: function(s){
         if (s == "SELF")
             this.handleSymbol(Symbol.makeFound(this.handleMessage(getMethodSelf)), "this");
@@ -121,8 +145,65 @@ var Designator = Context.Designator.extend({
             var ms = this.handleMessage(getMethodSuper);
             this.handleSymbol(Symbol.makeFound(ms.symbol), ms.code);
         }
-        else
+        else 
             Context.Designator.prototype.handleLiteral.call(this, s);
+    },
+    __beginCall: function(){
+        this.__procCall = Context.makeProcCall(this, this.__currentType, this.__info, this.__code);
+    },
+    __endCall: function(){
+        var e = this.__procCall.end();
+        this._advance(e.type(), new ResultVariable(e), e.code());
+        this.__procCall = undefined;
+    }
+});
+
+var ExpressionProcedureCall = Context.Chained.extend({
+    init: function EberonContext$init(context){
+        Context.Chained.prototype.init.call(this, context);
+    },
+    setDesignator: function(d){
+        var info = d.info();
+        var parent = this.parent();
+        if (info instanceof ResultVariable)
+            parent.handleExpression(info.expression());
+        else
+            parent.setDesignator(d);
+    }
+});
+
+var AssignmentOrProcedureCall = Context.Chained.extend({
+    init: function EberonContext$init(context){
+        Context.Chained.prototype.init.call(this, context);
+        this.__left = undefined;
+        this.__right = undefined;
+    },
+    setDesignator: function(d){
+        this.__left = d;
+    },
+    handleExpression: function(e){
+        this.__right = e;
+    },
+    codeGenerator: function(){return Code.nullGenerator();},
+    endParse: function(){
+        var d = this.__left;
+        var code;
+        if (this.__right){
+            var left = Code.makeExpression(d.code(), d.type(), d);
+            code = op.assign(left, this.__right, this.language());
+        }
+        else if (!(d.info() instanceof ResultVariable)){
+            var procCall = Context.makeProcCall(this, d.type(), d.info(), d.code());
+            var result = procCall.end();
+            Context.assertProcStatementResult(result.type());
+            code = result.code();
+        }
+        else{
+            Context.assertProcStatementResult(d.type());
+            code = d.code();
+        }
+    
+    this.parent().codeGenerator().write(code);
     }
 });
 
@@ -453,7 +534,9 @@ var Expression = Context.Expression.extend({
 exports.AddOperator = AddOperator;
 exports.Designator = Designator;
 exports.Expression = Expression;
+exports.ExpressionProcedureCall = ExpressionProcedureCall;
 exports.MethodHeading = MethodHeading;
+exports.AssignmentOrProcedureCall = AssignmentOrProcedureCall;
 exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;
 exports.RecordDecl = RecordDecl;

+ 14 - 3
src/eberon/eberon_grammar.js

@@ -20,9 +20,20 @@ function makeProcedureHeading(ident, identdef, formalParameters){
                );
 }
 
-function makeDesignator(qualident, selector){
-    return context(
-        and(or("SELF", "SUPER", qualident), repeat(selector)), EbContext.Designator);
+function makeAssignmentOrProcedureCall(designator, assignment){
+    return context(and(designator, optional(assignment)),
+                   EbContext.AssignmentOrProcedureCall);
+}
+
+function makeDesignator(qualident, selector, actualParameters){
+    var designator = context(
+        and(or("SELF", "SUPER", qualident), repeat(or(selector, actualParameters))), EbContext.Designator);
+    return { 
+        factor: context(designator, EbContext.ExpressionProcedureCall),
+        assignmentOrProcedureCall: function(assignment){
+            return makeAssignmentOrProcedureCall(designator, assignment);
+        }
+    };
 }
 
 function makeProcedureDeclaration(ident, procedureHeading, procedureBody){

+ 10 - 13
src/grammar.js

@@ -55,7 +55,12 @@ var selector = or(and(point, ident)
                 , "^"
                 , context(and("(", qualident, ")"), Context.TypeCast)
                 );
-var designator = makeDesignator(qualident, selector);
+var designator = makeDesignator(
+        qualident, 
+        selector,
+        // break recursive declaration of actualParameters
+        function(stream, context){return actualParameters(stream, context);}
+        );
 var type = or(context(qualident, Context.Type),
               function(stream, context){return strucType(stream, context);} // break recursive declaration of strucType
              );
@@ -77,11 +82,7 @@ var string = or(context(Lexer.string, Context.String)
 var factor = context(
     or(string, number, "NIL", "TRUE", "FALSE"
      , function(stream, context){return set(stream, context);} // break recursive declaration of set
-     , context(and(designator
-                 // break recursive declaration of actualParameters
-                 , optional(function(stream, context){return actualParameters(stream, context);})
-                  )
-             , Context.ExpressionProcedureCall)
+     , designator.factor
      , and("(", function(stream, context){return expression(stream, context);}
          , required(")", "no matched ')'"))
      , and("~", function(stream, context){
@@ -108,16 +109,12 @@ var set = and("{", context(optional(and(element, repeat(and(",", element)))), Co
 
 var expList = and(expression, repeat(and(",", expression)));
 var actualParameters = and("(", context(optional(expList), Context.ActualParameters), ")");
-var procedureCall = context(and(designator, optional(actualParameters))
-                          , Context.StatementProcedureCall);
 
-var assignment = context(and(designator, context(or(":=", "="), Context.CheckAssignment)
-                       , required(expression, "expression expected"))
-                       , Context.Assignment);
+var assignment = and(context(or(":=", "="), Context.CheckAssignment),
+                     required(expression, "expression expected"));
 
 var statement = optional(or(
-                   emit(assignment, Context.emitEndStatement)
-                 , emit(procedureCall, Context.emitEndStatement)
+                   emit(designator.assignmentOrProcedureCall(assignment), Context.emitEndStatement)
                    // break recursive declaration of ifStatement/caseStatement/whileStatement/repeatStatement
                  , function(stream, context){return ifStatement(stream, context);}
                  , function(stream, context){return caseStatement(stream, context);}

+ 85 - 0
src/oberon/oberon_context.js

@@ -1,7 +1,9 @@
 "use strict";
 
+var Code = require("js/Code.js");
 var Context = require("context.js");
 var Errors = require("js/Errors.js");
+var op = require("js/Operator.js");
 var Type = require("js/Types.js");
 
 var RecordDecl = Context.RecordDecl.extend({
@@ -21,5 +23,88 @@ var VariableDeclaration = Context.VariableDeclaration.extend({
     }
 });
 
+var ProcedureCall = Context.Chained.extend({
+    init: function ProcedureCallContext(context){
+        Context.Chained.prototype.init.call(this, context);
+        this.__type = undefined;
+        this.__procCall = undefined;
+        this.__code = Code.makeSimpleGenerator();
+    },
+    setDesignator: function(d){
+        this.__type = d.type();
+        this.__procCall = Context.makeProcCall(this, this.__type, d.info(), d.code());
+        this.__callExpression = undefined;
+    },
+    codeGenerator: function(){return this.__code;},
+    type: function(){return this.__type;},
+    handleMessage: function(msg){
+        if (msg == Context.beginCallMsg || msg == Context.endCallMsg)
+            return undefined;
+        return Context.Chained.prototype.handleMessage.call(this, msg);
+    },
+    handleExpression: function(e){this.__procCall.handleArgument(e);},
+    callExpression: function(){return this.__callExpression;},
+    endParse: function(){this.__callExpression = this.__procCall.end();}
+});
+
+var StatementProcedureCall = ProcedureCall.extend({
+    init: function StatementProcedureCallContext(context){
+        ProcedureCall.prototype.init.call(this, context);
+    },
+    endParse: function(){
+        ProcedureCall.prototype.endParse.call(this);
+        var e = this.callExpression();
+        Context.assertProcStatementResult(e.type());
+        this.parent().codeGenerator().write(e.code());
+    }
+});
+
+var ExpressionProcedureCall = ProcedureCall.extend({
+    init: function ExpressionProcedureCall(context){
+        ProcedureCall.prototype.init.call(this, context);
+        this.__designator = undefined;
+        this.__hasActualParameters = false;
+    },
+    setDesignator: function(d){
+        this.__designator = d;
+    },
+    handleMessage: function(msg){
+        if (msg == Context.beginCallMsg){
+            ProcedureCall.prototype.setDesignator.call(this, this.__designator);
+            this.__hasActualParameters = true;
+            return undefined;
+        }
+        return ProcedureCall.prototype.handleMessage.call(this, msg);
+    },
+    endParse: function(){
+        var parent = this.parent();
+        if (this.__hasActualParameters){
+            ProcedureCall.prototype.endParse.call(this);
+            parent.handleFactor(this.callExpression());
+        }
+        else{
+            var d = this.__designator;
+            parent.setDesignator(d);
+        }
+    }
+});
+
+var Assignment = Context.Chained.extend({
+    init: function AssignmentContext(context){
+        Context.Chained.prototype.init.call(this, context);
+        this.__left = undefined;
+    },
+    codeGenerator: function(){return Code.nullGenerator();},
+    setDesignator: function(d){
+        this.__left = Code.makeExpression(d.code(), d.type(), d);
+    },
+    handleExpression: function(e){
+        this.parent().codeGenerator().write(op.assign(this.__left, e, this.language()));
+    }
+});
+
+exports.Assignment = Assignment;
+exports.ExpressionProcedureCall = ExpressionProcedureCall;
 exports.RecordDecl = RecordDecl;
+exports.StatementProcedureCall = StatementProcedureCall;
 exports.VariableDeclaration = VariableDeclaration;

+ 17 - 2
src/oberon/oberon_grammar.js

@@ -10,6 +10,7 @@ var Symbols = require("js/OberonSymbols.js");
 var and = Parser.and;
 var context = Parser.context;
 var optional = Parser.optional;
+var or = Parser.or;
 var repeat = Parser.repeat;
 
 function makeProcedureHeading(ident, identdef, formalParameters){
@@ -18,8 +19,22 @@ function makeProcedureHeading(ident, identdef, formalParameters){
              , context(optional(formalParameters), Context.FormalParametersProcDecl));
 }
 
-function makeDesignator(qualident, selector){
-    return context(and(qualident, repeat(selector)), Context.Designator);
+function makeAssignmentOrProcedureCall(designator, actualParameters, assignment){
+    return or(context(and(designator, assignment), 
+                      ObContext.Assignment),
+              context(and(designator, optional(actualParameters)), 
+                      ObContext.StatementProcedureCall)
+              );
+}
+
+function makeDesignator(qualident, selector, actualParameters){
+    var designator = context(and(qualident, repeat(selector)), Context.Designator);
+    return { 
+        factor: context(and(designator, optional(actualParameters)), ObContext.ExpressionProcedureCall),
+        assignmentOrProcedureCall: function(assignment){
+            return makeAssignmentOrProcedureCall(designator, actualParameters, assignment);
+        }
+    };
 }
 
 function makeProcedureDeclaration(ident, procedureHeading, procedureBody){

+ 58 - 0
test/expected/eberon/designator.js

@@ -0,0 +1,58 @@
+var RTL$ = {
+    extend: function extend(methods){
+        function Type(){
+            for(var m in methods)
+                this[m] = methods[m];
+        }
+        Type.prototype = this.prototype;
+
+        var result = methods.init;
+        result.prototype = new Type(); // inherit this.prototype
+        result.prototype.constructor = result; // to see constructor name in diagnostic
+        
+        result.extend = extend;
+        return result;
+    },
+    makeRef: function (obj, prop){
+        return {set: function(v){ obj[prop] = v; },
+                get: function(){ return obj[prop]; }};
+    }
+};
+var m = function (){
+var TP = RTL$.extend({
+	init: function TP(){
+		this.i = 0;
+		this.proc = null;
+		this.procT = null;
+	}
+});
+
+function proc(){
+}
+
+function makeT(){
+	var result = null;
+	result = new TP();
+	result.proc = proc;
+	result.procT = makeT;
+	return result;
+}
+
+function makeProc(){
+	return proc;
+}
+
+function int(i/*INTEGER*/){
+}
+
+function intVar(i/*VAR INTEGER*/){
+}
+int(makeT().i);
+intVar(RTL$.makeRef(makeT(), "i"));
+intVar(RTL$.makeRef(makeT().procT(), "i"));
+makeT().proc();
+makeT().proc();
+makeT().procT().proc();
+makeT().procT().proc();
+makeProc()();
+}();

+ 44 - 0
test/input/eberon/designator.ob

@@ -0,0 +1,44 @@
+MODULE m;
+TYPE
+    Proc = PROCEDURE;
+    TP = POINTER TO RECORD
+		i: INTEGER;
+        proc: Proc;
+        procT: PROCEDURE(): TP
+	END;
+
+PROCEDURE proc();
+END proc;
+
+PROCEDURE makeT(): TP;
+VAR
+    result: TP;
+BEGIN
+    NEW(result);
+    result.proc := proc;
+    result.procT := makeT;
+    RETURN result
+END makeT;
+
+PROCEDURE makeProc(): Proc;
+    RETURN proc
+END makeProc;
+
+PROCEDURE int(i: INTEGER);
+END int;
+
+PROCEDURE intVar(VAR i: INTEGER);
+END intVar;
+
+BEGIN
+    int(makeT().i);
+    intVar(makeT().i);
+    intVar(makeT().procT().i);
+
+    makeT().proc();
+    makeT().proc;
+    makeT().procT().proc();
+    makeT().procT().proc;
+
+    makeProc()();
+END m.

+ 0 - 2
test/test_unit.js

@@ -68,7 +68,6 @@ return {
          "p2()"),
     fail(["", "not parsed"],
          ["12a", "not parsed"],
-         ["p2()()", "not parsed"],
          ["noResult()", "procedure returning no result cannot be used in an expression"]
          )
     ),
@@ -800,7 +799,6 @@ return {
          ["ptr()", "PROCEDURE expected, got 'POINTER TO anonymous RECORD'"],
          ["p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"],
          ["p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'"],
-         ["p()()", "not parsed"],
          ["p3", "procedure returning a result cannot be used as a statement"],
          ["p3()", "procedure returning a result cannot be used as a statement"]
          )

+ 36 - 1
test/test_unit_eberon.js

@@ -252,10 +252,12 @@ exports.suite = {
             + "PROCEDURE pArray(a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END pArray;"
             + "PROCEDURE pString(s: STRING): BOOLEAN; RETURN FALSE END pString;"
             + "PROCEDURE pVar(VAR a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END pVar;"
+            + "PROCEDURE pIntArray(a: ARRAY OF INTEGER): BOOLEAN; RETURN FALSE END pIntArray;"
             ),
     pass("pArray(s)"),
     fail(["pVar(s)", "type mismatch for argument 1: cannot pass 'STRING' as VAR parameter of type 'ARRAY OF CHAR'"],
-         ["pString(a)", "type mismatch for argument 1: 'ARRAY 10 OF CHAR' cannot be converted to 'STRING'"]
+         ["pString(a)", "type mismatch for argument 1: 'ARRAY 10 OF CHAR' cannot be converted to 'STRING'"],
+         ["pIntArray(s)", "type mismatch for argument 1: 'STRING' cannot be converted to 'ARRAY OF INTEGER'"]
         )
     ),
 "STRING LEN": testWithContext(
@@ -272,5 +274,38 @@ exports.suite = {
     fail(["s[-1]", "index is negative: -1"],
          ["pCharByVar(s[0])", "string element cannot be used as VAR parameter"]
          )
+    ),
+"designate call result in expression": testWithContext(
+    context(grammar.expression,
+            "TYPE PT = POINTER TO RECORD field: INTEGER END;"
+            + "VAR p: PT;"
+            + "PROCEDURE proc(): PT; RETURN p END proc;"
+            + "PROCEDURE int(): INTEGER; RETURN 0 END int;"
+            + "PROCEDURE intVar(VAR i: INTEGER): INTEGER; RETURN i END intVar;"),
+    pass("proc().field",
+         "intVar(proc().field)"),
+    fail(["intVar(int())", "expression cannot be used as VAR parameter"])
+    ),
+"designate call result in statement": testWithContext(
+    context(grammar.statement,
+            "TYPE PT = POINTER TO RECORD field: INTEGER; proc: PROCEDURE END;"
+            + "ProcType = PROCEDURE;"
+            + "VAR p: PT;"
+            + "PROCEDURE procVoid(); END procVoid;"
+            + "PROCEDURE proc(): PT; RETURN p END proc;"
+            + "PROCEDURE int(): INTEGER; RETURN 0 END int;"
+            + "PROCEDURE intVar(VAR i: INTEGER); END intVar;"
+            + "PROCEDURE returnProc(): ProcType; RETURN procVoid END returnProc;"
+           ),
+    pass("proc().field := 0",
+         "proc().proc()",
+         "proc().proc"
+        ),
+    fail(["int() := 0", "cannot assign to procedure call result"],
+         ["intVar(int())", "expression cannot be used as VAR parameter"],
+         ["procVoid()()", "PROCEDURE expected, got 'procedure call statement'"],
+         ["int()()", "PROCEDURE expected, got 'INTEGER'"],
+         ["returnProc()", "procedure returning a result cannot be used as a statement"] // call is not applied implicitly to result
+        )
     )
 };

+ 18 - 0
test/test_unit_oberon.js

@@ -42,5 +42,23 @@ exports.suite = {
     grammar.variableDeclaration,
     pass(),
     fail(["s: STRING", "undeclared identifier: 'STRING'"])
+    ),
+"cannot designate call result in expression": testWithContext(
+    context(grammar.expression,
+            "TYPE PT = POINTER TO RECORD field: INTEGER END;"
+            + "ProcType = PROCEDURE(): INTEGER;"
+            + "VAR p: PT;"
+            + "PROCEDURE proc(): PT; RETURN p END proc;"
+            + "PROCEDURE p1(): INTEGER; RETURN 1 END p1;"
+            + "PROCEDURE p2(): ProcType; RETURN p1 END p2;"),
+    pass(),
+    fail(["proc().field", "not parsed"],
+         ["p2()()", "not parsed"])
+    ),
+"cannot designate call result in statement": testWithContext(
+    context(grammar.statement,
+            "PROCEDURE p; END p;"),
+    pass(),
+    fail(["p()()", "not parsed"])
     )
 };