Prechádzať zdrojové kódy

fix string relation/ordering operations

Vladislav Folts 11 rokov pred
rodič
commit
0ed6ae841b
8 zmenil súbory, kde vykonal 121 pridanie a 16 odobranie
  1. 15 10
      src/context.js
  2. 25 0
      src/operator.js
  3. 9 0
      src/rtl.js
  4. 8 1
      src/type.js
  5. 22 1
      test/expected/string.js
  6. 16 0
      test/input/run/string.ob
  7. 18 2
      test/input/string.ob
  8. 8 2
      test/test_unit.js

+ 15 - 10
src/context.js

@@ -729,12 +729,12 @@ var orderOpTypeCheck = {
                 basicTypes.real,
                 basicTypes.ch
                ].indexOf(t) != -1
-            || (t instanceof Type.Array && t.elementsType() == basicTypes.ch);
+            || Type.isString(t);
     }
 };
 
 var equalOpTypeCheck = {
-    expect: "numeric type or SET or BOOLEAN OR CHAR or character array or POINTER or PROCEDURE",
+    expect: "numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE",
     check: function(t){
         return [basicTypes.integer,
                 basicTypes.uint8,
@@ -743,7 +743,7 @@ var equalOpTypeCheck = {
                 basicTypes.bool,
                 basicTypes.ch
                ].indexOf(t) != -1
-            || (t instanceof Type.Array && t.elementsType() == basicTypes.ch)
+            || Type.isString(t)
             || t instanceof Type.Pointer
             || t instanceof Type.Procedure
             || t == Type.nil;
@@ -777,7 +777,12 @@ function useTypeInRelation(leftType, rightType){
         if (type)
             return type;
     }
-    checkTypeMatch(rightType, leftType);
+
+    // special case for strings
+    var isStrings = Type.isString(leftType) && Type.isString(rightType);
+    if (!isStrings)
+        checkTypeMatch(rightType, leftType);
+
     return leftType;
 }
 
@@ -787,26 +792,26 @@ function relationOp(leftType, rightType, literal){
     var type = useTypeInRelation(leftType, rightType);
     switch (literal){
         case "=":
-            o = op.equal;
+            o = Type.isString(type) ? op.equalStr : op.equal;
             check = equalOpTypeCheck;
             break;
         case "#":
-            o = op.notEqual;
+            o = Type.isString(type) ? op.notEqualStr : op.notEqual;
             check = equalOpTypeCheck;
             break;
         case "<":
-            o = op.less;
+            o = Type.isString(type) ? op.lessStr : op.less;
             check = orderOpTypeCheck;
             break;
         case ">":
-            o = op.greater;
+            o = Type.isString(type) ? op.greaterStr : op.greater;
             check = orderOpTypeCheck;
             break;
         case "<=":
             if (type == basicTypes.set)
                 o = op.setInclL;
             else {
-                o = op.eqLess;
+                o = Type.isString(type) ? op.eqLessStr : op.eqLess;
                 check = orderOpTypeCheck;
             }
             break;
@@ -814,7 +819,7 @@ function relationOp(leftType, rightType, literal){
             if (type == basicTypes.set)
                 o = op.setInclR;
             else {
-                o = op.eqGreater;
+                o = Type.isString(type) ? op.eqGreaterStr : op.eqGreater;
                 check = orderOpTypeCheck;
             }
             break;

+ 25 - 0
src/operator.js

@@ -53,6 +53,25 @@ function makeUnary(op, code){
 var mul = makeBinary(function(x, y){return x * y;}, " * ", precedence.mulDivMod);
 var div = makeBinary(function(x, y){return x / y;}, " / ", precedence.mulDivMod);
 
+var openArrayChar = new Type.Array(undefined, undefined, Type.basic.ch);
+
+function castToStr(e, context){
+    var type = e.type();
+    var opCast = Cast.implicit(type, openArrayChar);
+    return opCast(context, e).code();
+}
+
+function makeStrCmp(op){
+    return function(left, right, context){
+        return new Code.Expression(
+            context.rtl().strCmp(castToStr(left, context),
+                                 castToStr(right, context))
+                + op + "0",
+            Type.basic.bool
+        );
+    };
+}
+
 function pow2(e){
     return new Code.Expression("Math.pow(2, " + e.deref().code() + ")",
                                Type.basic.real);
@@ -165,11 +184,17 @@ var operators = {
     and:        makeBinary(function(x, y){return x && y;}, " && ", precedence.and),
 
     equal:      makeBinary(function(x, y){return x == y;}, " == ", precedence.equal),
+    equalStr:   makeStrCmp(" == "),
     notEqual:   makeBinary(function(x, y){return x != y;}, " != ", precedence.equal),
+    notEqualStr: makeStrCmp(" != "),
     less:       makeBinary(function(x, y){return x < y;}, " < ", precedence.relational),
+    lessStr:    makeStrCmp(" < "),
     greater:    makeBinary(function(x, y){return x > y;}, " > ", precedence.relational),
+    greaterStr: makeStrCmp(" > "),
     eqLess:     makeBinary(function(x, y){return x <= y;}, " <= ", precedence.relational),
+    eqLessStr:  makeStrCmp(" <= "),
     eqGreater:  makeBinary(function(x, y){return x >= y;}, " >= ", precedence.relational),
+    eqGreaterStr: makeStrCmp(" >= "),
 
     not:        makeUnary(function(x){return !x;}, "!"),
     negate:     promoteToWideIfNeeded(makeUnary(function(x){return -x;}, "-")),

+ 9 - 0
src/rtl.js

@@ -96,6 +96,15 @@ var impl = {
             result[i] = s.charCodeAt(i);
         return result;
     },
+    strCmp: function(s1, s2){
+        var cmp = 0;
+        var i = 0;
+        while (!cmp && i < s1.length && i < s2.length){
+            cmp = s1[i] - s2[i];
+            ++i;
+        }
+        return cmp ? cmp : s1.length - s2.length;
+    },
     copy: function(from, to){
         for(var prop in to){
             if (to.hasOwnProperty(prop)){

+ 8 - 1
src/type.js

@@ -170,10 +170,17 @@ var basic = {
 exports.basic = basic;
 exports.numeric = [basic.integer, basic.uint8, basic.real];
 
-exports.isInt = function(type){return type == basic.integer || type == basic.uint8;};
+exports.isInt = function(type){
+    return type == basic.integer || type == basic.uint8;
+};
 
 exports.intsDescription = function(){return "'INTEGER' or 'BYTE'";};
 
+exports.isString = function(type){
+    return (type instanceof ArrayType && type.elementsType() == basic.ch)
+            || type instanceof exports.String;
+};
+
 exports.nil = new NilType();
 
 exports.Const = Id.extend({

+ 22 - 1
test/expected/string.js

@@ -34,6 +34,15 @@ var RTL$ = {
         if (!condition)
             throw new Error("assertion failed"
                           + ((code !== undefined) ? " with code " + code : ""));
+    },
+    strCmp: function (s1, s2){
+        var cmp = 0;
+        var i = 0;
+        while (!cmp && i < s1.length && i < s2.length){
+            cmp = s1[i] - s2[i];
+            ++i;
+        }
+        return cmp ? cmp : s1.length - s2.length;
     }
 };
 var m = function (){
@@ -47,6 +56,7 @@ var s7 = "\t";
 var s8 = "\f";
 var s9 = "\\";
 var ch1 = 0;
+var a1 = RTL$.makeArray(15, 0);
 var a2 = RTL$.makeArray(3, 0);
 
 function p1(s/*ARRAY OF CHAR*/){
@@ -55,11 +65,22 @@ function p1(s/*ARRAY OF CHAR*/){
 function p2(c/*CHAR*/){
 }
 ch1 = 34;
-RTL$.assignArrayFromString(a2, s1);
+RTL$.assignArrayFromString(a1, s1);
 RTL$.assignArrayFromString(a2, s2);
+RTL$.assignArrayFromString(a1, s2);
 p1(RTL$.strToArray(s1));
 p1(RTL$.strToArray(s2));
 p2(34);
 RTL$.assert(ch1 == 34);
 RTL$.assert(34 == ch1);
+RTL$.assert(RTL$.strCmp(RTL$.strToArray("abc"), RTL$.strToArray("abc")) == 0);
+RTL$.assert(RTL$.strCmp(a1, a2) == 0);
+RTL$.assert(RTL$.strCmp(a1, a2) != 0);
+RTL$.assert(RTL$.strCmp(a1, a2) > 0);
+RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) > 0);
+RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) >= 0);
+RTL$.assert(RTL$.strCmp(a1, RTL$.strToArray(s1)) != 0);
+RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) < 0);
+RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) <= 0);
+RTL$.assert(RTL$.strCmp(RTL$.strToArray(s1), a1) != 0);
 }();

+ 16 - 0
test/input/run/string.ob

@@ -0,0 +1,16 @@
+MODULE m;
+
+VAR
+	a3: ARRAY 3 OF CHAR;
+	a4: ARRAY 4 OF CHAR;
+
+BEGIN
+	ASSERT("abc" = "abc");
+	ASSERT("abd" > "abc");
+	ASSERT("abcd" > "abc");
+
+	a3 := "abc";
+	a4 := "abc";
+	ASSERT(a3 < a4); (*a4 has extra 0*)
+
+END m.

+ 18 - 2
test/input/string.ob

@@ -12,6 +12,7 @@ CONST
 	s9 = "\";
 VAR
     ch1: CHAR;
+	a1: ARRAY 15 OF CHAR;
 	a2: ARRAY 3 OF CHAR;
 
 PROCEDURE p1(s: ARRAY OF CHAR);
@@ -22,13 +23,28 @@ END p2;
 
 BEGIN
     ch1 := s1;
-	a2 := s1;
+	a1 := s1;
 	a2 := s2;
+	a1 := s2;
 
 	p1(s1);
 	p1(s2);
     p2(s1);
 
     ASSERT(ch1 = s1);
-	ASSERT(s1 = ch1)
+	ASSERT(s1 = ch1);
+
+	ASSERT("abc" = "abc");
+
+	ASSERT(a1 = a2);
+	ASSERT(a1 # a2);
+	ASSERT(a1 > a2);
+	
+	ASSERT(a1 > s1);
+	ASSERT(a1 >= s1);
+	ASSERT(a1 # s1);
+	ASSERT(s1 < a1);
+	ASSERT(s1 <= a1);
+	ASSERT(s1 # a1);
+
 END m.

+ 8 - 2
test/test_unit.js

@@ -666,7 +666,11 @@ var testSuite = {
 "array expression": testWithGrammar(
     Grammar.procedureBody,
     pass("VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1 END",
-         "VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1; a[1] := a[0] END"),
+         "VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := 1; a[1] := a[0] END",
+         "VAR a1, a2: ARRAY 3 OF CHAR; BEGIN ASSERT(a1 = a2); END",
+         "VAR a1: ARRAY 2 OF CHAR; a2: ARRAY 3 OF CHAR; BEGIN ASSERT(a1 = a2); END",
+         "CONST cs = \"a\"; VAR a: ARRAY 3 OF CHAR; BEGIN ASSERT(a = cs); ASSERT(cs # a); ASSERT(a < cs); ASSERT(cs > a); END"
+         ),
     fail(["VAR a: ARRAY 10 OF INTEGER; BEGIN a[0] := TRUE END",
           "type mismatch: 'a[0]' is 'INTEGER' and cannot be assigned to 'BOOLEAN' expression"],
          ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[TRUE] := 1 END",
@@ -686,7 +690,9 @@ var testSuite = {
          ["VAR a: ARRAY 10 OF INTEGER; BEGIN a[10] := 0 END",
           "index out of bounds: maximum possible index is 9, got 10"],
          ["CONST c1 = 5; VAR a: ARRAY 10 OF INTEGER; BEGIN a[10 + c1] := 0 END",
-          "index out of bounds: maximum possible index is 9, got 15"])
+          "index out of bounds: maximum possible index is 9, got 15"],
+         ["VAR a1, a2: ARRAY 3 OF INTEGER; BEGIN ASSERT(a1 = a2); END",
+          "operator '=' type mismatch: numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE expected, got 'ARRAY 3 OF INTEGER'"])
     ),
 "multi-dimensional array expression": testWithGrammar(
     Grammar.procedureBody,