Browse Source

ARRAY assigning + ASSERT

Vladislav Folts 12 years ago
parent
commit
09601a6994
10 changed files with 175 additions and 41 deletions
  1. 35 22
      src/context.js
  2. 33 6
      src/procedure.js
  3. 22 2
      src/rtl.js
  4. 11 1
      test/expected/array.js
  5. 11 0
      test/expected/assert.js
  6. 4 2
      test/input/array.ob
  7. 6 0
      test/input/assert.ob
  8. 14 0
      test/input/run/copy.ob
  9. 17 2
      test/test_compile.js
  10. 22 6
      test/test_unit.js

+ 35 - 22
src/context.js

@@ -1239,31 +1239,44 @@ exports.Assignment = ChainedContext.extend({
 			this.__code.write(this.__leftOp + (d_info.isVar() ? ".set(" : " = "));
 	},
 	handleExpression: function(type, value, designator){
-		if (this.__type instanceof Type.Array)
-			if (this.__type.elementsType() == basicTypes.char)
-				if (type instanceof Type.String){
-					if (this.__type.length() === undefined)
-						throw new Errors.Error("string cannot be assigned to open " + this.__type.description());
-					if (type.length() > this.__type.length())
-						throw new Errors.Error(
-							this.__type.length() + "-character ARRAY is too small for " 
-							+ type.length() + "-character string");
-					this.__code = new Code.SimpleGenerator(
-						this.rtl().assignArrayFromString(this.__leftOp, this.__code.result()));
-				}
-				else
-					throw new Errors.Error("'" + this.__leftOp
-										 + "' is '" + this.__type.description()
-								 		 + "' and can be assigned to a string only (got '" + type.description() + "' expression intead)");
-			else
-				throw new Errors.Error("'" + this.__leftOp
-									 + "' is '" + this.__type.description()
-									 + "' and cannot be assigned");
-		else if (!Cast.implicit(type, this.__type))
+		var isArray = this.__type instanceof Type.Array;
+		if (isArray
+			&& this.__type.elementsType() == basicTypes.char
+			&& type instanceof Type.String){
+			if (this.__type.length() === undefined)
+				throw new Errors.Error("string cannot be assigned to open " + this.__type.description());
+			if (type.length() > this.__type.length())
+				throw new Errors.Error(
+					this.__type.length() + "-character ARRAY is too small for " 
+					+ type.length() + "-character string");
+			this.__code = new Code.SimpleGenerator(
+				this.rtl().assignArrayFromString(this.__leftOp, this.__code.result()));
+			return;
+		}
+		
+		if (!Cast.implicit(type, this.__type))
 			throw new Errors.Error("type mismatch: '" + this.__leftOp
 								 + "' is '" + this.__type.description()
 								 + "' and cannot be assigned to '" + type.description() + "' expression");
-		else if (designator)
+
+		if (isArray && type instanceof Type.Array)
+			if (this.__type.length() === undefined)
+				throw new Errors.Error("'" + this.__leftOp
+									 + "' is open '" + this.__type.description()
+									 + "' and cannot be assigned");
+			else if (type.length() === undefined)
+				throw new Errors.Error("'" + this.__leftOp
+									 + "' cannot be assigned to open '"
+									 + this.__type.description() + "'");
+			else if (this.__type.length() != type.length())
+				throw new Errors.Error("array size mismatch: '" + this.__leftOp
+									 + "' has size " + this.__type.length()
+									 + " and cannot be assigned to the array with size " + type.length());
+			else
+				this.__code = new Code.SimpleGenerator(
+					this.rtl().copy(this.__code.result(), this.__leftOp));
+
+		if (designator)
 			writeDerefDesignatorCode(designator, this.__code);
 	},
 	endParse: function(){

+ 33 - 6
src/procedure.js

@@ -33,6 +33,7 @@ var ProcCallGenerator = Class.extend({
 		this.writeCode(this.prolog());
 	},
 	id: function(){return this.__id;},
+	context: function(){return this.__context;},
 	codeGenerator: function(){return this.__code;},
 	handleArgument: function(type, designator, code){
 		var pos = this.__argumentsCount++;
@@ -64,16 +65,18 @@ var ProcCallGenerator = Class.extend({
 		this.writeCode(prefix + code);
 	},
 	end: function(){
-		if (this.__type){
-			var procArgs = this.__type.arguments();
-			if (this.__argumentsCount != procArgs.length)
-				throw new Errors.Error(procArgs.length + " argument(s) expected, got "
-									 + this.__argumentsCount);
-		}
+		if (this.__type)
+			this.checkArgumentsCount(this.__argumentsCount);
 		this.writeCode(this.epilog());
 		return this.codeGenerator().result();
 	},
 	prolog: function(){return this.__id + "(";},
+	checkArgumentsCount: function(count){
+		var procArgs = this.__type.arguments();
+		if (count != procArgs.length)
+			throw new Errors.Error(procArgs.length + " argument(s) expected, got "
+								 + this.__argumentsCount);
+	},
 	checkArgument: function(pos, type, designator){
 		var arg = this.__type.arguments()[pos];
 		var convert;
@@ -198,6 +201,30 @@ exports.predefined = [
 		var symbol = new Type.Symbol(name, type);
 		return symbol;
 	}(),
+	function(){
+		var AssertProcCallGenerator = ProcCallGenerator.extend({
+			init: function AssertProcCallGenerator(context, id, type){
+				ProcCallGenerator.prototype.init.call(this, context, id, type);
+			},
+			prolog: function(){return this.context().rtl().assertId() + "(";},
+			checkArgumentsCount: function(count){
+				if (count != 1 && count != 2 )
+					throw new Errors.Error("1 or 2 arguments expected, got " + this.__argumentsCount);
+			}
+		});
+
+		var args = [new Arg(Type.basic.bool), new Arg(Type.basic.int)];
+		var proc = new ProcType(
+			"predefined procedure ASSERT",
+			args,
+			undefined,
+			function(context, id, type){
+				return new AssertProcCallGenerator(context, id, type);
+			});
+		var type = new Type.Procedure(proc);
+		var symbol = new Type.Symbol("ASSERT", type);
+		return symbol;
+	}(),
 	function(){
 		var args = [new Arg(Type.basic.set, true),
 					new Arg(Type.basic.int, false)];

+ 22 - 2
src/rtl.js

@@ -81,6 +81,20 @@ var impl = {
 		for(i = 0; i < s.length; ++i)
 			result[i] = s.charCodeAt(i);
 		return result;
+	},
+	copy: function(from, to){
+        for(var prop in from){
+            var v = from[prop];
+            if (typeof v == "object")
+                this.copy(v, to[prop]);
+            else if (typeof v != "function")
+                to[prop] = v;
+        }
+	},
+	assert: function(condition, code){
+		if (!condition)
+			throw new Error("assertion failed"
+						  + ((code !== undefined) ? " with code " + code : ""));
 	}
 };
 
@@ -91,6 +105,7 @@ exports.RTL = Class.extend({
 		this.__supportJS = false;
 		for(var fName in impl){
 			this[fName] = this.__makeOnDemand(fName);
+			this[fName + "Id"] = this.__makeIdOnDemand(fName);
 		}
 	},
 	supportJS: function(){this.__supportJS = true;},
@@ -118,11 +133,16 @@ exports.RTL = Class.extend({
 			result += "var JS = function(){return this;}();\n";
 		return result;
 	},
-	__makeOnDemand: function(name){
+	__makeIdOnDemand: function(name){
 		return function(){
 			if (!this.__entries[name])
 				this.__entries[name] = impl[name];
-			var result = "RTL$." + name + "(";
+			return "RTL$." + name;
+		};
+	},
+	__makeOnDemand: function(name){
+		return function(){
+			var result = this[name +"Id"]() + "(";
 			if (arguments.length){
 				result += arguments[0];
 				for(var a = 1; a < arguments.length; ++a)

+ 11 - 1
test/expected/array.js

@@ -27,11 +27,20 @@ var RTL$ = {
 		result.prototype = methods;
 		result.extend = extend;
 		return result;
+	},
+	copy: function (from, to){
+        for(var prop in from){
+            var v = from[prop];
+            if (typeof v == "object")
+                this.copy(v, to[prop]);
+            else if (typeof v != "function")
+                to[prop] = v;
+        }
 	}
 };
 var m = function (){
 var arraySize = 10;
-var a1 = RTL$.makeArray(10, 0);
+var a1 = RTL$.makeArray(10, 0);var a11 = RTL$.makeArray(10, 0);
 var a2 = RTL$.makeArray(5, function(){return RTL$.makeArray(10, 0);});
 var a3 = RTL$.makeArray(5, false);
 var a4 = RTL$.makeArray(3, 4, false);
@@ -49,4 +58,5 @@ a1[0] = 1;
 a3[1] = true;
 a4[1][2] = true;
 a4[1][2] = true;
+RTL$.copy(a11, a1);
 }();

+ 11 - 0
test/expected/assert.js

@@ -0,0 +1,11 @@
+var RTL$ = {
+	assert: function (condition, code){
+		if (!condition)
+			throw new Error("assertion failed"
+						  + ((code !== undefined) ? " with code " + code : ""));
+	}
+};
+var m = function (){
+RTL$.assert(true);
+RTL$.assert(true, 123);
+}();

+ 4 - 2
test/input/array.ob

@@ -9,7 +9,7 @@ TYPE
     T4 = ARRAY 3, 4 OF BOOLEAN;
 
 VAR
-    a1: T1;
+    a1, a11: T1;
     a2: T2;
     a3: ARRAY 5 OF BOOLEAN;
 	a4: T4;
@@ -25,5 +25,7 @@ BEGIN
     a1[0] := 1;
     a3[1] := TRUE;
 	a4[1][2] := TRUE;
-	a4[1, 2] := TRUE
+	a4[1, 2] := TRUE;
+
+    a1 := a11
 END m.

+ 6 - 0
test/input/assert.ob

@@ -0,0 +1,6 @@
+MODULE m;
+
+BEGIN
+    ASSERT(TRUE);
+    ASSERT(TRUE, 123)
+END m.

+ 14 - 0
test/input/run/copy.ob

@@ -0,0 +1,14 @@
+MODULE m;
+
+TYPE
+	T1 = RECORD field: INTEGER END;
+VAR
+	a1: ARRAY 3 OF T1;
+	a2: ARRAY 3 OF T1;
+BEGIN
+	a1[0].field := 123;
+	a2 := a1;
+	ASSERT(a2[0].field = 123);
+	a1[0].field := 345;
+	ASSERT(a2[0].field = 123)
+END m.

+ 17 - 2
test/test_compile.js

@@ -14,12 +14,17 @@ function compareResults(result, name, dirs){
         throw new Test.TestError("Failed");
 }
 
-function expectOk(src, dirs){
+function compile(src){
     var text = fs.readFileSync(src, "utf8");
     var errors = "";
     var result = oc.compile(text, function(e){errors += e;});
     if (errors)
         throw new Test.TestError(errors);
+    return result;
+}
+
+function expectOk(src, dirs){
+    var result = compile(src);
     var resultName = path.basename(src).replace(".ob", ".js");
     compareResults(result, resultName, dirs);
 }
@@ -39,6 +44,10 @@ function expectError(src, dirs){
     compareResults(errors, resultName, dirs);
 }
 
+function run(src, dirs){
+    eval(compile(src));
+}
+
 function makeTest(test, src, dirs){
     return function(){test(src, dirs);};
 }
@@ -57,14 +66,20 @@ function makeTests(test, dirs){
 
 var okDirs = {input: "input", output: "output", expected: "expected"};
 var errDirs = {};
+var runDirs = {};
 for(var p in okDirs)
     errDirs[p] = okDirs[p] + "/errors";
+for(var p in okDirs)
+    runDirs[p] = okDirs[p] + "/run";
 
 if (!fs.existsSync(okDirs.output))
     fs.mkdirSync(okDirs.output);
 if (!fs.existsSync(errDirs.output))
     fs.mkdirSync(errDirs.output);
+if (!fs.existsSync(runDirs.output))
+    fs.mkdirSync(runDirs.output);
 
 Test.run({"expect OK": makeTests(expectOk, okDirs),
-          "expect compile error": makeTests(expectError, errDirs)}
+          "expect compile error": makeTests(expectError, errDirs),
+          "run": makeTests(run, runDirs)}
         );

+ 22 - 6
test/test_unit.js

@@ -364,7 +364,7 @@ expression: function(){
 	test.expectError("VAR a: ARRAY 10 OF BOOLEAN; BEGIN a[0,0] := TRUE END"
 				   , "ARRAY expected, got 'BOOLEAN'");
 	test.expectError("VAR a: ARRAY 10, 20 OF BOOLEAN; BEGIN a[0] := TRUE END"
-				   , "'a[0]' is 'ARRAY OF BOOLEAN' and cannot be assigned");
+				   , "type mismatch: 'a[0]' is 'ARRAY OF BOOLEAN' and cannot be assigned to 'BOOLEAN' expression");
 	test.expectError("VAR a: ARRAY 10 OF INTEGER; BEGIN a[10] := 0 END"
 				   , "index out of bounds: maximum possible index is 9, got 10");
 	test.expectError("CONST c1 = 5; VAR a: ARRAY 10 OF INTEGER; BEGIN a[10 + c1] := 0 END"
@@ -769,23 +769,32 @@ procedure: function(){
 	test.parse("ch1 := 22X");
 	test.expectError("a1 := \"abcd\"", "3-character ARRAY is too small for 4-character string");
 	test.expectError("intArray := \"abcd\""
-				   , "'intArray' is 'ARRAY OF INTEGER' and cannot be assigned");
+				   , "type mismatch: 'intArray' is 'ARRAY OF INTEGER' and cannot be assigned to 'multi-character string' expression");
 },
-"array assignment fails": function(){
+"array assignment": function(){
 	var test = setupWithContext(
 		  Grammar.statement
 		, "VAR charArray: ARRAY 3 OF CHAR;"
 			+ "intArray: ARRAY 10 OF INTEGER;"
+			+ "intArray2: ARRAY 10 OF INTEGER;"
+			+ "intArray3: ARRAY 5 OF INTEGER;"
 		);
-	test.expectError("intArray := intArray"
-				   , "'intArray' is 'ARRAY OF INTEGER' and cannot be assigned");
+	test.parse("intArray := intArray2");
+	test.expectError("intArray := charArray"
+				   , "type mismatch: 'intArray' is 'ARRAY OF INTEGER' and cannot be assigned to 'ARRAY OF CHAR' expression");
+	test.expectError("intArray2 := intArray3"
+				   , "array size mismatch: 'intArray2' has size 10 and cannot be assigned to the array with size 5");
+	test.expectError("intArray3 := charArray"
+				   , "type mismatch: 'intArray3' is 'ARRAY OF INTEGER' and cannot be assigned to 'ARRAY OF CHAR' expression");
 },
 "open array assignment fails": function(){
 	var test = setup(Grammar.procedureDeclaration);
 	test.expectError("PROCEDURE p(s1, s2: ARRAY OF CHAR); BEGIN s1 := s2 END p"
 				   , "cannot assign to read-only variable");
 	test.expectError("PROCEDURE p(VAR s1, s2: ARRAY OF CHAR); BEGIN s1 := s2 END p"
-				   , "'s1' is 'ARRAY OF CHAR' and can be assigned to a string only (got 'ARRAY OF CHAR' expression intead)");
+				   , "'s1' is open 'ARRAY OF CHAR' and cannot be assigned");
+	test.expectError("PROCEDURE p(s1: ARRAY OF CHAR); VAR s2: ARRAY 10 OF CHAR; BEGIN s2 := s1 END p"
+				   , "'s2' cannot be assigned to open 'ARRAY OF CHAR'");
 },
 "string assignment to open array fails": function(){
 	var test = setup(Grammar.procedureDeclaration);
@@ -813,6 +822,13 @@ module: function(){
 	test.expectError("MODULE m; END undeclared.",
 					 "original module name 'm' expected, got 'undeclared'");
 },
+assert: function(){
+	var test = setup(Grammar.statement);
+	test.parse("ASSERT(TRUE)");
+	test.parse("ASSERT(TRUE, 123)");
+	test.expectError("ASSERT()", "1 or 2 arguments expected, got 0");
+	test.expectError("ASSERT(123, TRUE)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'BOOLEAN'");
+},
 IMPORT: function(){
 	var test = setup(Grammar.module);
 	test.parse("MODULE m; IMPORT JS; END m.");