浏览代码

Fix code generation for putting record to MAP.

Vladislav Folts 10 年之前
父节点
当前提交
b928473f0b

二进制
bin/compiled.zip


+ 3 - 4
src/context.js

@@ -18,8 +18,6 @@ var basicTypes = Type.basic();
 var nullCodeGenerator = CodeGenerator.nullGenerator();
 var nullCodeGenerator = CodeGenerator.nullGenerator();
 var nilType = Type.nil();
 var nilType = Type.nil();
 
 
-var castOperations = op.castOperations();
-
 function log(s){
 function log(s){
     console.info(s);
     console.info(s);
 }
 }
@@ -68,7 +66,7 @@ function checkTypeMatch(from, to){
 
 
 function checkImplicitCast(types, from, to){
 function checkImplicitCast(types, from, to){
     var op;
     var op;
-    if (types.implicitCast(from, to, false, castOperations, {set: function(v){op = v;}, get:function(){return op;}}))
+    if (types.implicitCast(from, to, false, {set: function(v){op = v;}, get:function(){return op;}}))
         throwTypeMismatch(from, to);
         throwTypeMismatch(from, to);
 }
 }
 
 
@@ -433,6 +431,7 @@ exports.Designator = ChainedContext.extend({
         this.__currentType = Type.pointerBase(this.__currentType);
         this.__currentType = Type.pointerBase(this.__currentType);
         if (this.__currentType instanceof Type.NonExportedRecord)
         if (this.__currentType instanceof Type.NonExportedRecord)
             throw new Errors.Error("POINTER TO non-exported RECORD type cannot be dereferenced");
             throw new Errors.Error("POINTER TO non-exported RECORD type cannot be dereferenced");
+        this.__lval = undefined;
     },
     },
     handleTypeCast: function(type){
     handleTypeCast: function(type){
         checkTypeCast(this.__info, this.__currentType, type, "type cast");
         checkTypeCast(this.__info, this.__currentType, type, "type cast");
@@ -637,7 +636,7 @@ exports.ProcDecl = ChainedContext.extend({
         
         
         var language = this.language();
         var language = this.language();
         var op;
         var op;
-        if (language.types.implicitCast(type, result, false, castOperations, {set: function(v){op = v;}, get:function(){return op;}}))
+        if (language.types.implicitCast(type, result, false, {set: function(v){op = v;}, get:function(){return op;}}))
             throw new Errors.Error(
             throw new Errors.Error(
                 "RETURN '" + result.description() + "' expected, got '"
                 "RETURN '" + result.description() + "' expected, got '"
                 + type.description() + "'");
                 + type.description() + "'");

+ 2 - 2
src/eberon/EberonConstructor.ob

@@ -1,5 +1,5 @@
 MODULE EberonConstructor;
 MODULE EberonConstructor;
-IMPORT Cast, Code, EberonCast, EberonRecord, Errors, LanguageContext, Operator, Procedure, Stream, Types;
+IMPORT Cast, Code, EberonCast, EberonRecord, Errors, LanguageContext, Procedure, Stream, Types;
 TYPE
 TYPE
     ConstructorCall = RECORD(Procedure.StdCall)
     ConstructorCall = RECORD(Procedure.StdCall)
         recordType: EberonRecord.PRecord;
         recordType: EberonRecord.PRecord;
@@ -81,7 +81,7 @@ BEGIN
         raiseSingleArgumentException(SELF);
         raiseSingleArgumentException(SELF);
     END;
     END;
 
 
-    IF SELF.cx.types.implicitCast(e.type(), SELF.type, FALSE, Operator.castOperations, op)
+    IF SELF.cx.types.implicitCast(e.type(), SELF.type, FALSE, op)
         # Cast.errNo THEN
         # Cast.errNo THEN
             Errors.raise("type mismatch: field '" + SELF.field + "' is '" + SELF.type.description()
             Errors.raise("type mismatch: field '" + SELF.field + "' is '" + SELF.type.description()
                          + "' and cannot be initialized using '" + e.type().description() + "' expression");
                          + "' and cannot be initialized using '" + e.type().description() + "' expression");

+ 28 - 1
src/eberon/EberonOperator.ob

@@ -1,5 +1,10 @@
 MODULE EberonOperator;
 MODULE EberonOperator;
-IMPORT Code, CodePrecedence, OberonRtl, Operator, Types;
+IMPORT Cast, Code, CodePrecedence, OberonRtl, Operator, Types;
+TYPE
+    CastOpRecord = RECORD(Cast.CastOpRecord)
+    END;
+VAR
+    castOperations*: Cast.Operations;
 
 
 PROCEDURE opAddStr(left, right: Code.PConst): Code.PConst;
 PROCEDURE opAddStr(left, right: Code.PConst): Code.PConst;
     RETURN Code.makeStringConst(left^(Code.StringConst).value
     RETURN Code.makeStringConst(left^(Code.StringConst).value
@@ -69,4 +74,26 @@ PROCEDURE inMap*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExp
                                      Types.basic.bool);
                                      Types.basic.bool);
 END;
 END;
 
 
+PROCEDURE CastOpRecord.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
+VAR
+    result: STRING;
+BEGIN
+    leftCode <- left.code();
+    leftLval <- left.lval();
+    IF leftCode # leftLval THEN (* TRUE for putting to MAP *)
+        (* optimize the case when the temporary is used - do not clone it *)
+        IF right.designator() = NIL THEN
+            result := leftLval + " = " + right.code();
+        ELSE
+            result := leftLval + " = " + rtl.clone(right.code(), Types.generateTypeInfo(left.type()));
+        END;
+    ELSE
+        result := SUPER(rtl, left, right);
+    END;
+    RETURN result;
+END;
+
+BEGIN
+    castOperations.castToUint8 := NEW Operator.CastToUint8();
+    castOperations.castToRecord := NEW CastOpRecord();
 END EberonOperator.
 END EberonOperator.

+ 1 - 1
src/eberon/eberon_context.js

@@ -367,7 +367,7 @@ var InPlaceVariableInit = Context.Chained.extend({
           
           
             var language = this.language();
             var language = this.language();
             var cloneOp;
             var cloneOp;
-            language.types.implicitCast(type, type, false, op.castOperations(), {set: function(v){cloneOp = v;}, get:function(){return cloneOp;}});
+            language.types.implicitCast(type, type, false, {set: function(v){cloneOp = v;}, get:function(){return cloneOp;}});
             this._code += cloneOp.clone(language.rtl, e);
             this._code += cloneOp.clone(language.rtl, e);
         }
         }
     },
     },

+ 4 - 1
src/eberon/eberon_grammar.js

@@ -8,6 +8,7 @@ var EbContext = require("eberon/eberon_context.js");
 var Grammar = require("grammar.js");
 var Grammar = require("grammar.js");
 var EbRtl = require("js/EberonRtl.js");
 var EbRtl = require("js/EberonRtl.js");
 var EbRtlCode = require("eberon/eberon_rtl.js");
 var EbRtlCode = require("eberon/eberon_rtl.js");
+var EbOperator = require("js/EberonOperator.js");
 var Parser = require("parser.js");
 var Parser = require("parser.js");
 var Symbols = require("js/EberonSymbols.js");
 var Symbols = require("js/EberonSymbols.js");
 
 
@@ -161,7 +162,9 @@ exports.language = {
         ),
         ),
     stdSymbols: Symbols.makeStd(),
     stdSymbols: Symbols.makeStd(),
     types: {
     types: {
-        implicitCast: Cast.implicit,
+        implicitCast: function(from, to, toVar, op){
+            return Cast.implicit(from, to, toVar, EbOperator.castOperations(), op);
+        },
         StaticArray: EbArray.StaticArray,
         StaticArray: EbArray.StaticArray,
         OpenArray: EbArray.OpenArray
         OpenArray: EbArray.OpenArray
     },
     },

+ 5 - 6
src/ob/Cast.ob

@@ -19,13 +19,14 @@ TYPE
     CastOpArray* = RECORD (CastOpDoNothing)
     CastOpArray* = RECORD (CastOpDoNothing)
     END;
     END;
 
 
-    CastOpRecord = RECORD (CastOpDoNothing)
+    CastOpRecord* = RECORD (CastOpDoNothing)
     END;
     END;
 
 
     CastOpStrToChar = RECORD (CastOpDoNothing)
     CastOpStrToChar = RECORD (CastOpDoNothing)
     END;
     END;
 
 
     Operations* = RECORD
     Operations* = RECORD
+        castToRecord*: PCastOp;
         castToUint8*: PCastOp
         castToUint8*: PCastOp
     END;
     END;
 
 
@@ -36,7 +37,6 @@ VAR
     doNothing*: POINTER TO CastOpDoNothing;
     doNothing*: POINTER TO CastOpDoNothing;
     castOpStrToChar: POINTER TO CastOpStrToChar;
     castOpStrToChar: POINTER TO CastOpStrToChar;
     castOpArray: POINTER TO CastOpArray;
     castOpArray: POINTER TO CastOpArray;
-    castOpRecord: POINTER TO CastOpRecord;
 
 
 PROCEDURE findBaseType(base: Types.PRecord; type: Types.PRecord): Types.PRecord;
 PROCEDURE findBaseType(base: Types.PRecord; type: Types.PRecord): Types.PRecord;
 BEGIN
 BEGIN
@@ -170,7 +170,7 @@ PROCEDURE CastOpArray.clone(rtl: OberonRtl.PType; e: Code.PExpression): STRING;
 END;
 END;
 
 
 PROCEDURE CastOpRecord.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
 PROCEDURE CastOpRecord.assign(rtl: OberonRtl.PType; left, right: Code.PExpression): STRING;
-    RETURN rtl.copy(right.code(), left.code(), Types.generateTypeInfo(left.type()))
+    RETURN rtl.copy(right.code(), left.lval(), Types.generateTypeInfo(left.type()));
 END;
 END;
 
 
 PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
 PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
@@ -190,7 +190,7 @@ BEGIN
     op := NIL;
     op := NIL;
     IF from = to THEN
     IF from = to THEN
         IF from IS Types.PRecord THEN
         IF from IS Types.PRecord THEN
-            op := castOpRecord;
+            op := ops.castToRecord;
         ELSIF from IS Types.PArray THEN
         ELSIF from IS Types.PArray THEN
             op := castOpArray;
             op := castOpArray;
         END;
         END;
@@ -239,7 +239,7 @@ BEGIN
         END;
         END;
     ELSIF (from IS Types.PRecord) & (to IS Types.PRecord) THEN
     ELSIF (from IS Types.PRecord) & (to IS Types.PRecord) THEN
         IF findBaseType(to, from) # NIL THEN
         IF findBaseType(to, from) # NIL THEN
-            op := castOpRecord;
+            op := ops.castToRecord;
             result := errNo;
             result := errNo;
         END;
         END;
     ELSIF (from = Types.nil) & matchesToNIL(to^) THEN
     ELSIF (from = Types.nil) & matchesToNIL(to^) THEN
@@ -260,6 +260,5 @@ BEGIN
     areTypesExactlyMatch := areTypesExactlyMatchImpl;
     areTypesExactlyMatch := areTypesExactlyMatchImpl;
     NEW(doNothing);
     NEW(doNothing);
     NEW(castOpArray);
     NEW(castOpArray);
-    NEW(castOpRecord);
     NEW(castOpStrToChar);
     NEW(castOpStrToChar);
 END Cast.
 END Cast.

+ 1 - 6
src/ob/Language.ob

@@ -2,12 +2,7 @@ MODULE Language;
 IMPORT Cast, T := Types;
 IMPORT Cast, T := Types;
 TYPE
 TYPE
     Types* = RECORD         
     Types* = RECORD         
-        PROCEDURE implicitCast*(from, to: T.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER
+        PROCEDURE implicitCast*(from, to: T.PType; toVar: BOOLEAN; VAR op: Cast.PCastOp): INTEGER
     END;
     END;
     PTypes* = POINTER TO Types;
     PTypes* = POINTER TO Types;
-(*)
-    Type* = RECORD
-        types*: PTypes;
-        rtl*:   PRtl
-    END;*)
 END Language.
 END Language.

+ 3 - 2
src/ob/Operator.ob

@@ -37,7 +37,7 @@ TYPE
         pred: CodePredicate
         pred: CodePredicate
     END;
     END;
 
 
-    CastToUint8 = RECORD (Cast.CastOpDoNothing)
+    CastToUint8* = RECORD (Cast.CastOpDoNothing)
     END;
     END;
 
 
 VAR
 VAR
@@ -492,7 +492,7 @@ BEGIN
         & (rightType IS Types.PString) THEN
         & (rightType IS Types.PString) THEN
         result := assignArrayFromString(leftType(Types.PArray)^, rightType^);
         result := assignArrayFromString(leftType(Types.PArray)^, rightType^);
     ELSE
     ELSE
-        IF cx.types.implicitCast(rightType, leftType, FALSE, castOperations, castOperation)
+        IF cx.types.implicitCast(rightType, leftType, FALSE, castOperation)
             # Cast.errNo THEN
             # Cast.errNo THEN
             Errors.raise("type mismatch: '" + left.code() + "' is '" + leftType.description()
             Errors.raise("type mismatch: '" + left.code() + "' is '" + leftType.description()
                          + "' and cannot be assigned to '" + rightType.description() + "' expression");
                          + "' and cannot be assigned to '" + rightType.description() + "' expression");
@@ -772,4 +772,5 @@ END;
 BEGIN
 BEGIN
     openArrayChar := NEW Types.OpenArray(Types.basic.ch);
     openArrayChar := NEW Types.OpenArray(Types.basic.ch);
     castOperations.castToUint8 := NEW CastToUint8();
     castOperations.castToUint8 := NEW CastToUint8();
+    castOperations.castToRecord := NEW Cast.CastOpRecord();
 END Operator.
 END Operator.

+ 1 - 1
src/ob/Procedure.ob

@@ -94,7 +94,7 @@ BEGIN
     expectType := expected.type; (* can be NIL for predefined functions (like NEW), dont check it in this case *)
     expectType := expected.type; (* can be NIL for predefined functions (like NEW), dont check it in this case *)
     IF expectType # NIL THEN
     IF expectType # NIL THEN
         actualType := actual.type();
         actualType := actual.type();
-        castErr := types.implicitCast(actualType, expectType, expected.isVar, Operator.castOperations, result);
+        castErr := types.implicitCast(actualType, expectType, expected.isVar, result);
         IF castErr = Cast.errVarParameter THEN
         IF castErr = Cast.errVarParameter THEN
             Errors.raise("type mismatch for argument " + String.fromInt(pos + 1)
             Errors.raise("type mismatch for argument " + String.fromInt(pos + 1)
                          + ": cannot pass '" + actualType.description()
                          + ": cannot pass '" + actualType.description()

+ 4 - 1
src/oberon/oberon_grammar.js

@@ -7,6 +7,7 @@ var Grammar = require("grammar.js");
 var ObContext = require("oberon/oberon_context.js");
 var ObContext = require("oberon/oberon_context.js");
 var ObRtl = require("js/OberonRtl.js");
 var ObRtl = require("js/OberonRtl.js");
 var ObRtlCode = require("rtl.js");
 var ObRtlCode = require("rtl.js");
+var Operator = require("js/Operator.js");
 var Parser = require("parser.js");
 var Parser = require("parser.js");
 var Symbols = require("js/OberonSymbols.js");
 var Symbols = require("js/OberonSymbols.js");
 var Types = require("js/Types.js");
 var Types = require("js/Types.js");
@@ -128,7 +129,9 @@ exports.language = {
         ),
         ),
     stdSymbols: Symbols.makeStd(),
     stdSymbols: Symbols.makeStd(),
     types: {
     types: {
-        implicitCast: Cast.implicit,
+        implicitCast: function(from, to, toVar, op){
+            return Cast.implicit(from, to, toVar, Operator.castOperations(), op);
+        },
         StaticArray: Types.StaticArray,
         StaticArray: Types.StaticArray,
         OpenArray: Types.OpenArray
         OpenArray: Types.OpenArray
     },
     },

+ 40 - 23
test/expected/eberon/map.js

@@ -35,6 +35,12 @@ var RTL$ = {
         if (!condition)
         if (!condition)
             throw new Error("assertion failed");
             throw new Error("assertion failed");
     },
     },
+    extend: function (cons, base){
+        function Type(){}
+        Type.prototype = base.prototype;
+        cons.prototype = new Type();
+        cons.prototype.constructor = cons;
+    },
     makeCharArray: function (/*dimensions*/){
     makeCharArray: function (/*dimensions*/){
         var forward = Array.prototype.slice.call(arguments);
         var forward = Array.prototype.slice.call(arguments);
         var length = forward.pop();
         var length = forward.pop();
@@ -82,6 +88,29 @@ var RTL$ = {
             throw new Error("invalid key: " + key);
             throw new Error("invalid key: " + key);
         return map[key];
         return map[key];
     },
     },
+    clone: function (from, type){
+        var result;
+        var r = type.record;
+        if (r){
+            var Ctr = from.constructor;
+            result = new Ctr();
+            this.copy(from, result, type);
+            return result;
+        }
+        var a = type.array;
+        if (a !== undefined ){
+            if (a === null)
+                // shallow clone
+                return from.slice();
+
+            // deep clone
+            var length = from.length;
+            result = new Array(length);
+            for(var i = 0; i < length; ++i)
+                result[i] = this.clone(from[i], a);
+            return result;
+        }
+    },
     copy: function (from, to, type){
     copy: function (from, to, type){
         var r = type.record;
         var r = type.record;
         if (r){
         if (r){
@@ -111,29 +140,6 @@ var RTL$ = {
                     to.push(this.clone(from[i], a));
                     to.push(this.clone(from[i], a));
             }
             }
         }
         }
-    },
-    clone: function (from, type){
-        var result;
-        var r = type.record;
-        if (r){
-            var Ctr = from.constructor;
-            result = new Ctr();
-            this.copy(from, result, type);
-            return result;
-        }
-        var a = type.array;
-        if (a !== undefined ){
-            if (a === null)
-                // shallow clone
-                return from.slice();
-
-            // deep clone
-            var length = from.length;
-            result = new Array(length);
-            for(var i = 0; i < length; ++i)
-                result[i] = this.clone(from[i], a);
-            return result;
-        }
     }
     }
 };
 };
 var test = function (){
 var test = function (){
@@ -204,9 +210,15 @@ function put(){
 	function T(){
 	function T(){
 		this.field = 0;
 		this.field = 0;
 	}
 	}
+	function Derived(){
+		T.call(this);
+	}
+	RTL$.extend(Derived, T);
 	var m = {};
 	var m = {};
 	var s = '';
 	var s = '';
 	var a = RTL$.makeCharArray(3);
 	var a = RTL$.makeCharArray(3);
+	var r = new T();
+	var d = new Derived();
 	var mapOfMap = {};
 	var mapOfMap = {};
 	var mapOfRecord = {};
 	var mapOfRecord = {};
 	var mapOfPointer = {};
 	var mapOfPointer = {};
@@ -215,8 +227,13 @@ function put(){
 	m[s] = 3;
 	m[s] = 3;
 	m[a] = 4;
 	m[a] = 4;
 	RTL$.getMappedValue(mapOfMap, "abc")["cde"] = 5;
 	RTL$.getMappedValue(mapOfMap, "abc")["cde"] = 5;
+	mapOfRecord["abc"] = RTL$.clone(r, {record: {field: null}});
+	mapOfRecord["abc"] = new T();
 	RTL$.getMappedValue(mapOfRecord, "abc").field = 6;
 	RTL$.getMappedValue(mapOfRecord, "abc").field = 6;
+	mapOfPointer["abc"] = new T();
 	RTL$.copy(new T(), RTL$.getMappedValue(mapOfPointer, "abc"), {record: {field: null}});
 	RTL$.copy(new T(), RTL$.getMappedValue(mapOfPointer, "abc"), {record: {field: null}});
+	mapOfPointer["abc"] = new Derived();
+	RTL$.copy(new Derived(), RTL$.getMappedValue(mapOfPointer, "abc"), {record: {field: null}});
 }
 }
 
 
 function in$(){
 function in$(){

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

@@ -66,10 +66,14 @@ TYPE
     T = RECORD 
     T = RECORD 
         field: INTEGER;
         field: INTEGER;
     END;
     END;
+    Derived = RECORD(T)
+    END;
 VAR
 VAR
     m: MapOfInteger;
     m: MapOfInteger;
     s: STRING;
     s: STRING;
     a: ARRAY 3 OF CHAR;
     a: ARRAY 3 OF CHAR;
+    r: T;
+    d: Derived;
     mapOfMap: MAP OF MapOfInteger;
     mapOfMap: MAP OF MapOfInteger;
     mapOfRecord: MAP OF T;
     mapOfRecord: MAP OF T;
     mapOfPointer: MAP OF POINTER TO T;
     mapOfPointer: MAP OF POINTER TO T;
@@ -79,8 +83,18 @@ BEGIN
     m[s] := 3;
     m[s] := 3;
     m[a] := 4;
     m[a] := 4;
     mapOfMap["abc"]["cde"] := 5;
     mapOfMap["abc"]["cde"] := 5;
+
+    mapOfRecord["abc"] := r;
+    (*mapOfRecord["abc"] := d;*)
+    mapOfRecord["abc"] := T();
+    (*mapOfRecord["abc"] := Derived();*)
     mapOfRecord["abc"].field := 6;
     mapOfRecord["abc"].field := 6;
+    
+    mapOfPointer["abc"] := NEW T();
     mapOfPointer["abc"]^ := T();
     mapOfPointer["abc"]^ := T();
+
+    mapOfPointer["abc"] := NEW Derived();
+    mapOfPointer["abc"]^ := Derived();
 END;
 END;
 
 
 PROCEDURE in();
 PROCEDURE in();

+ 25 - 0
test/input/eberon/run/map.ob

@@ -66,10 +66,35 @@ BEGIN
     ASSERT(~("cde" IN m));
     ASSERT(~("cde" IN m));
 END;
 END;
 
 
+PROCEDURE testCopy();
+TYPE
+    T = RECORD
+        i: INTEGER;
+    END;
+VAR
+    m1, m2: MAP OF INTEGER;
+    mr1, mr2: MAP OF T;
+BEGIN
+    m1["a"] := 1;
+    m2 := m1;
+    ASSERT(m2["a"] = 1);
+    m2["a"] := 2;
+    ASSERT(m1["a"] = 1);
+(*
+    mr1["a"] := T();
+    mr1["a"].i := 1;
+    mr2 := mr1;
+    ASSERT(mr2["a"].i = 1);
+    mr1["a"].i := 2;
+    ASSERT(mr1["a"].i = 1);
+    *)
+END;
+
 BEGIN
 BEGIN
     testEmptyForEach();
     testEmptyForEach();
     testForEach();
     testForEach();
     testPutGet();
     testPutGet();
     testIn();
     testIn();
     testRemove();
     testRemove();
+    testCopy();
 END test.
 END test.