Jelajahi Sumber

more strings operations and tests

Vladislav Folts 12 tahun lalu
induk
melakukan
679e6de5dd

+ 4 - 2
src/context.js

@@ -232,7 +232,7 @@ exports.Designator = ChainedContext.extend({
 
 		checkTypeCast(this.__currentType, type, "invalid type cast");
 
-		var code = this.rtl().genCast(this.__code.result(), type);
+		var code = this.rtl().typeGuard(this.__code.result(), type.name());
 		this.__code = new Code.SimpleGenerator(code);
 
 		if (this.__currentType instanceof Type.Pointer)
@@ -1242,6 +1242,8 @@ exports.Assignment = ChainedContext.extend({
 		if (type instanceof Type.String
 			&& this.__type instanceof Type.Array
 			&& this.__type.elementsType() == basicTypes.char){
+			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 " 
@@ -1353,8 +1355,8 @@ exports.ProcedureCall = ChainedContext.extend({
 		var type = d.type();
 		assertProcType(type);
 		this.__type = type;
-		this.__procCall = type.callGenerator(new Code.SimpleGenerator(), d.code());
 		this.__code = new Code.SimpleGenerator();
+		this.__procCall = type.callGenerator(this, d.code());
 	},
 	codeGenerator: function(){
 		return this.__code ? this.__code : this.parent().codeGenerator();

+ 2 - 2
src/module.js

@@ -6,8 +6,8 @@ var AnyType = Type.Basic.extend({
 		Type.Basic.prototype.init.call(this, "ANY");
 	},
 	findSymbol: function(){return this;},
-	callGenerator: function(codegenerator, id){
-		return new Procedure.CallGenerator(codegenerator, id);
+	callGenerator: function(context, id){
+		return new Procedure.CallGenerator(context, id);
 	}
 });
 

+ 47 - 25
src/procedure.js

@@ -16,18 +16,28 @@ var Arg = Class.extend({
 });
 exports.Arg = Arg;
 
+var CheckArgumentResult = Arg.extend({
+	init: function(type, isVar, convert){
+		Arg.prototype.init.call(this, type, isVar);
+		this.convert = convert;
+	}
+});
+
 var ProcCallGenerator = Class.extend({
-	init: function ProcCallGenerator(codeGenerator, id, type){
-		this.__codeGenerator = codeGenerator;
+	init: function ProcCallGenerator(context, id, type){
+		this.__context = context;
 		this.__id = id;
 		this.__type = type;
 		this.__argumentsCount = 0;
+		this.__code = new Code.SimpleGenerator();
 		this.writeCode(this.prolog());
 	},
 	id: function(){return this.__id;},
+	codeGenerator: function(){return this.__code;},
 	handleArgument: function(type, designator, code){
 		var pos = this.__argumentsCount++;
 		var isVarArg = false;
+		var convert;
 		if (this.__type){
 			var expectedArguments = this.__type.arguments();
 			if (pos >= expectedArguments.length )
@@ -36,6 +46,7 @@ var ProcCallGenerator = Class.extend({
 			
 			var arg = this.checkArgument(pos, type, designator);
 			isVarArg = arg.isVar;
+			convert = arg.convert;
 		}
 		if (designator){
 			var info = designator.info();
@@ -45,6 +56,10 @@ var ProcCallGenerator = Class.extend({
 				else if (!info.isVar() && isVarArg)
 					code = designator.refCode();
 			}
+		
+		if (convert)
+			code = convert(code);
+		
 		var prefix = pos ? ", " : "";
 		this.writeCode(prefix + code);
 	},
@@ -56,15 +71,21 @@ var ProcCallGenerator = Class.extend({
 									 + this.__argumentsCount);
 		}
 		this.writeCode(this.epilog());
-		return this.__codeGenerator.result();
+		return this.codeGenerator().result();
 	},
 	prolog: function(){return this.__id + "(";},
 	checkArgument: function(pos, type, designator){
 		var arg = this.__type.arguments()[pos];
+		var convert;
 		var expectType = arg.type; // can be undefined for predefined functions (like NEW), dont check it in this case
 		if (expectType && !Cast.implicit(type, expectType))
-			throw new Errors.Error("expect '" + expectType.name() + "' type for argument "
-								 + pos + ", got '" + type.name() + "'");
+			if (type instanceof Type.String && expectType instanceof Type.Array){
+				var rtl = this.__context.rtl();
+				convert = function(code){return rtl.strToArray(code);};
+			}
+			else
+				throw new Errors.Error("type mismatch for argument " + (pos + 1) + ": '" + type.description()
+								 	 + "' cannot be converted to '" + expectType.description() + "'");
 		if (arg.isVar){
 			if (!designator)
 				throw new Errors.Error("expression cannot be used as VAR parameter");
@@ -74,10 +95,10 @@ var ProcCallGenerator = Class.extend({
 			if (info.isReadOnly())
 				throw new Errors.Error("read-only variable cannot be used as VAR parameter");
 		}
-		return arg;
+		return new CheckArgumentResult(arg.type, arg.isVar, convert);
 	},
 	epilog: function(){return ")";},
-	writeCode: function(s){this.__codeGenerator.write(s);}
+	writeCode: function(s){this.codeGenerator().write(s);}
 });
 
 var ProcType = Type.Basic.extend({
@@ -87,8 +108,8 @@ var ProcType = Type.Basic.extend({
 		this.__result = result;
 		this.__callGeneratorFactory = callGeneratorFactory
 			? callGeneratorFactory
-			: function(codeGenerator, id, type){
-				return new ProcCallGenerator(codeGenerator, id, type);
+			: function(context, id, type){
+				return new ProcCallGenerator(context, id, type);
 			};
 	},
 	isProcedure: function(){return true;},
@@ -105,8 +126,8 @@ var ProcType = Type.Basic.extend({
 		return 'PROCEDURE' + this.__dumpProcArgs()
 			+ (this.__result ? ": " + this.__result.name() : "");
 		},
-	callGenerator: function(codeGenerator, id){
-		return this.__callGeneratorFactory(codeGenerator, id, this);
+	callGenerator: function(context, id){
+		return this.__callGeneratorFactory(context, id, this);
 	},
 	__dumpProcArgs: function(){
 		if (!this.__arguments.length)
@@ -124,20 +145,21 @@ var ProcType = Type.Basic.extend({
 });
 
 var TwoArgToOperatorProcCallGenerator = ProcCallGenerator.extend({
-	init: function TwoArgToOperatorProcCallGenerator(codeGenerator, id, type, operator){
-		ProcCallGenerator.prototype.init.bind(this)(Code.nullGenerator, id, type);
-		this.__code = codeGenerator;
+	init: function TwoArgToOperatorProcCallGenerator(context, id, type, operator){
+		ProcCallGenerator.prototype.init.call(this, context, id, type);
+		this.__code = context.codeGenerator();
 		this.__operator = operator;
 		this.__firstArgumentCode = undefined;
 		this.__secondArgumentCode = undefined;
 	},
+	codeGenerator: function(){return Code.nullGenerator;},
 	prolog: function(id){return "";},
 	handleArgument: function(type, designator, code){
 		if (!this.__firstArgumentCode)
 			this.__firstArgumentCode = code;
 		else
 			this.__secondArgumentCode = code;
-		ProcCallGenerator.prototype.handleArgument.bind(this)(type, designator, code);
+		ProcCallGenerator.prototype.handleArgument.call(this, type, designator, code);
 	},
 	epilog: function(type){return "";},
 	end: function(){
@@ -148,18 +170,18 @@ var TwoArgToOperatorProcCallGenerator = ProcCallGenerator.extend({
 exports.predefined = [
 	function(){
 		var NewProcCallGenerator = ProcCallGenerator.extend({
-			init: function NewProcCallGenerator(codeGenerator, id, type){
-				ProcCallGenerator.prototype.init.bind(this)(codeGenerator, id, type);
+			init: function NewProcCallGenerator(context, id, type){
+				ProcCallGenerator.prototype.init.call(this, context, id, type);
 				this.__baseType = undefined;
 			},
 			prolog: function(id){return "";},
 			checkArgument: function(pos, type, designator){
-				ProcCallGenerator.prototype.checkArgument.bind(this)(pos, type, designator);
+				ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
 				if (!(type instanceof Type.Pointer))
 					throw new Errors.Error("POINTER variable expected, got '"
 										 + type.name() + "'");
 				this.__baseType = type.baseType();
-				return new Arg(type, false);
+				return new CheckArgumentResult(type, false);
 			},
 			epilog: function(){return " = new " + this.__baseType.name() + "()";}
 		});
@@ -170,8 +192,8 @@ exports.predefined = [
 			"predefined procedure NEW",
 			args,
 			undefined,
-			function(codeGenerator, id, type){
-				return new NewProcCallGenerator(codeGenerator, id, type);
+			function(context, id, type){
+				return new NewProcCallGenerator(context, id, type);
 			}));
 		var symbol = new Type.Symbol(name, type);
 		return symbol;
@@ -184,9 +206,9 @@ exports.predefined = [
 			"predefined procedure INCL",
 			args,
 			undefined,
-			function(codeGenerator, id, type){
+			function(context, id, type){
 				return new TwoArgToOperatorProcCallGenerator(
-					codeGenerator, id, type, operator);
+					context, id, type, operator);
 				});
 		var type = new Type.Procedure(proc);
 		var symbol = new Type.Symbol("INCL", type);
@@ -200,9 +222,9 @@ exports.predefined = [
 			"predefined procedure EXCL",
 			args,
 			undefined,
-			function(codeGenerator, id, type){
+			function(context, id, type){
 				return new TwoArgToOperatorProcCallGenerator(
-					codeGenerator, id, type, operator);
+					context, id, type, operator);
 				});
 		var type = new Type.Procedure(proc);
 		var symbol = new Type.Symbol("EXCL", type);

+ 92 - 112
src/rtl.js

@@ -1,89 +1,102 @@
 function Class(){}
 Class.extend = function extend(methods){
-	methods.__proto__ = this.prototype; // make instanceof work
+		methods.__proto__ = this.prototype; // make instanceof work
 
-	// to see constructor name in diagnostic
-	var result = methods.init;
-	methods.constructor = result.prototype.constructor;
+		// to see constructor name in diagnostic
+		var result = methods.init;
+		methods.constructor = result.prototype.constructor;
 
-	result.prototype = methods;
-	result.extend = extend;
-	return result;
-};
-
-function RTLTypeGuard(from, to){
-	if (!(from instanceof to))
-		throw new Error("typeguard assertion failed");
-	return from;
-}
+		result.prototype = methods;
+		result.extend = extend;
+		return result;
+	};
 
-function RTLMakeArray(/*dimensions, initializer*/){
-	var forward = Array.prototype.slice.call(arguments);
-	var result = new Array(forward.shift());
-	var i;
-	if (forward.length == 1){
-		var init = forward[0];
-		if (typeof init == "function")
-			for(i = 0; i < result.length; ++i)
-				result[i] = init();
+var impl = {
+	typeGuard: function(from, to){
+		if (!(from instanceof to))
+			throw new Error("typeguard assertion failed");
+		return from;
+	},
+	makeArray: function(/*dimensions, initializer*/){
+		var forward = Array.prototype.slice.call(arguments);
+		var result = new Array(forward.shift());
+		var i;
+		if (forward.length == 1){
+			var init = forward[0];
+			if (typeof init == "function")
+				for(i = 0; i < result.length; ++i)
+					result[i] = init();
+			else
+				for(i = 0; i < result.length; ++i)
+					result[i] = init;
+		}
 		else
 			for(i = 0; i < result.length; ++i)
-				result[i] = init;
-	}
-	else
-		for(i = 0; i < result.length; ++i)
-			result[i] = RTLMakeArray.apply(this, forward);
-	return result;
-}
-
-function RTLMakeSet(/*...*/){
-	var result = 0;
-	
-	function checkBit(b){
-		if (b < 0 || b > 31)
-			throw new Error("integes between 0 and 31 expected, got " + b);
-	}
+				result[i] = RTLMakeArray.apply(this, forward);
+		return result;
+	},
+	makeSet: function(/*...*/){
+		var result = 0;
+		
+		function checkBit(b){
+			if (b < 0 || b > 31)
+				throw new Error("integes between 0 and 31 expected, got " + b);
+		}
 
-	function setBit(b){
-		checkBit(b);
-		result |= 1 << b;
-	}
-	
-	for(var i = 0; i < arguments.length; ++i){
-		var b = arguments[i];
-		if (b instanceof Array){
-			var from = b[0];
-			var to = b[1];
-			if (from < to)
-				throw new Error("invalid SET diapason: " + from + ".." + to);
-			for(var bi = from; bi <= to; ++bi)
-				setBit(bi);
+		function setBit(b){
+			checkBit(b);
+			result |= 1 << b;
 		}
-		else
-			setBit(b);
+		
+		for(var i = 0; i < arguments.length; ++i){
+			var b = arguments[i];
+			if (b instanceof Array){
+				var from = b[0];
+				var to = b[1];
+				if (from < to)
+					throw new Error("invalid SET diapason: " + from + ".." + to);
+				for(var bi = from; bi <= to; ++bi)
+					setBit(bi);
+			}
+			else
+				setBit(b);
+		}
+		return result;
+	},
+	makeRef: function(obj, prop){
+	    return {set: function(v){ obj[prop] = v; },
+	            get: function(){ return obj[prop]; }};
+	},
+	setInclL: function(l, r){return l & r == l;},
+	setInclR: function(l, r){return l & r == r;},
+	assignArrayFromString: function(a, s){
+		var i;
+		for(i = 0; i < s.length; ++i)
+			a[i] = s.charCodeAt(i);
+		for(i = s.length; i < a.length; ++i)
+			a[i] = 0;
+	},
+	strToArray: function(s){
+		var result = new Array(s.length);
+		for(i = 0; i < s.length; ++i)
+			result[i] = s.charCodeAt(i);
+		return result;
 	}
-	return result;
-}
-
-function RTLMakeRef(obj, prop){
-    return {set: function(v){ obj[prop] = v; },
-            get: function(){ return obj[prop]; }};
-}
-
-function RTLSetInclL(l, r){
-	return l & r == l;
-}
-
-function RTLSetInclR(l, r){
-	return l & r == r;
-}
+};
 
-function RTLAssignArrayFromString(a, s){
-	var i;
-	for(i = 0; i < s.length; ++i)
-		a[i] = s.charCodeAt(i);
-	for(i = s.length; i < a.length; ++i)
-		a[i] = 0;
+function makeOnDemandFunction(name){
+	return function(){
+		if (!this.__entries[name])
+			this.__entries[name] = impl[name];
+		var result = "RTL$." + name + "(";
+		for(var a = 0; a < arguments.length; ++a){
+			if (a)
+				result += ", ";
+			result += arguments[a];
+		}
+		result += ")";
+		return result;
+	};
 }
 
 exports.Class = Class;
@@ -91,6 +104,9 @@ exports.RTL = Class.extend({
 	init: function RTL(){
 		this.__entries = {};
 		this.__supportJS = false;
+		for(var fName in impl){
+			this[fName] = makeOnDemandFunction(fName);
+		}
 	},
 	supportJS: function(){this.__supportJS = true;},
 	baseClass: function(){
@@ -98,42 +114,6 @@ exports.RTL = Class.extend({
 			this.__entries.extend = Class.extend;
 		return "RTL$";
 	},
-	genCast: function(obj, type){
-		if (!this.__entries["typeGuard"])
-			this.__entries.typeGuard = RTLTypeGuard;
-
-		return "RTL$.typeGuard(" + obj + ", " + type.name() + ")";
-	},
-	makeRef: function(obj, prop){
-		if (!this.__entries["makeRef"])
-			this.__entries.makeRef = RTLMakeRef;
-		return "RTL$.makeRef(" + obj + ", " + prop + ")";
-	},
-	makeArray: function(args){
-		if (!this.__entries.makeArray)
-			this.__entries.makeArray = RTLMakeArray;
-		return "RTL$.makeArray(" + args + ")";
-	},
-	makeSet: function(args){
-		if (!this.__entries["makeSet"])
-			this.__entries.makeSet = RTLMakeSet;
-		return "RTL$.makeSet(" + args + ")";
-	},
-	setInclL: function(args){
-		if (!this.__entries.setInclL)
-			this.__entries.setInclL = RTLSetInclL;
-		return "RTL$.setInclL(" + args + ")";
-	},
-	setInclR: function(args){
-		if (!this.__entries.setInclR)
-			this.__entries.setInclR = RTLSetInclR;
-		return "RTL$.setInclR(" + args + ")";
-	},
-	assignArrayFromString: function(a, s){
-		if (!this.__entries.assignArrayFromString)
-			this.__entries.assignArrayFromString = RTLAssignArrayFromString;
-		return "RTL$.assignArrayFromString(" + a + ", " + s + ")";
-	},
 	generate: function(){
 		var result = "var RTL$ = {\n";
 		var firstEntry = true;
@@ -142,7 +122,7 @@ exports.RTL = Class.extend({
 				result += ",\n";
 			else
 				firstEntry = false;
-			result += "\t" + name + ": " + this.__entries[name].toString().replace(/\n/g, "\n\t");
+			result += "\t" + name + ": " + this.__entries[name].toString();
 		}
 		if (!firstEntry)
 			result += "\n};\n";

+ 1 - 1
src/type.js

@@ -10,7 +10,7 @@ var Type = Id.extend({
 		Id.prototype.init.bind(this)();
 	},
 	idType: function(){return "type";},
-	isProcedure: function(){return false;}
+	isProcedure: function(){return false;},
 });
 
 exports.Type = Type;

+ 3 - 3
test/expected/array.js

@@ -1,5 +1,5 @@
 var RTL$ = {
-	makeArray: function RTLMakeArray(/*dimensions, initializer*/){
+	makeArray: function (/*dimensions, initializer*/){
 		var forward = Array.prototype.slice.call(arguments);
 		var result = new Array(forward.shift());
 		var i;
@@ -19,11 +19,11 @@ var RTL$ = {
 	},
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;

+ 3 - 3
test/expected/cast.js

@@ -1,16 +1,16 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;
 	},
-	typeGuard: function RTLTypeGuard(from, to){
+	typeGuard: function (from, to){
 		if (!(from instanceof to))
 			throw new Error("typeguard assertion failed");
 		return from;

+ 2 - 2
test/expected/is.js

@@ -1,11 +1,11 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;

+ 2 - 2
test/expected/new.js

@@ -1,11 +1,11 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;

+ 2 - 2
test/expected/nil.js

@@ -1,11 +1,11 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;

+ 2 - 2
test/expected/proc.js

@@ -1,11 +1,11 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;

+ 4 - 8
test/expected/set.js

@@ -1,12 +1,12 @@
 var RTL$ = {
-	makeSet: function RTLMakeSet(/*...*/){
+	makeSet: function (/*...*/){
 		var result = 0;
 		
 		function checkBit(b){
 			if (b < 0 || b > 31)
 				throw new Error("integes between 0 and 31 expected, got " + b);
 		}
-	
+
 		function setBit(b){
 			checkBit(b);
 			result |= 1 << b;
@@ -27,12 +27,8 @@ var RTL$ = {
 		}
 		return result;
 	},
-	setInclL: function RTLSetInclL(l, r){
-		return l & r == l;
-	},
-	setInclR: function RTLSetInclR(l, r){
-		return l & r == r;
-	}
+	setInclL: function (l, r){return l & r == l;},
+	setInclR: function (l, r){return l & r == r;}
 };
 var m = function (){
 var ci = 3;

+ 13 - 2
test/expected/string.js

@@ -1,5 +1,5 @@
 var RTL$ = {
-	makeArray: function RTLMakeArray(/*dimensions, initializer*/){
+	makeArray: function (/*dimensions, initializer*/){
 		var forward = Array.prototype.slice.call(arguments);
 		var result = new Array(forward.shift());
 		var i;
@@ -17,12 +17,18 @@ var RTL$ = {
 				result[i] = RTLMakeArray.apply(this, forward);
 		return result;
 	},
-	assignArrayFromString: function RTLAssignArrayFromString(a, s){
+	assignArrayFromString: function (a, s){
 		var i;
 		for(i = 0; i < s.length; ++i)
 			a[i] = s.charCodeAt(i);
 		for(i = s.length; i < a.length; ++i)
 			a[i] = 0;
+	},
+	strToArray: function (s){
+		var result = new Array(s.length);
+		for(i = 0; i < s.length; ++i)
+			result[i] = s.charCodeAt(i);
+		return result;
 	}
 };
 var m = function (){
@@ -30,6 +36,11 @@ var s1 = "\"";
 var s2 = "ABC";
 var s3 = "with space";
 var a2 = RTL$.makeArray(3, 0);
+
+function p1(s/*ARRAY OF CHAR*/){
+}
 RTL$.assignArrayFromString(a2, "\"");
 RTL$.assignArrayFromString(a2, "ABC");
+p1(RTL$.strToArray("\""));
+p1(RTL$.strToArray("ABC"));
 }();

+ 4 - 4
test/expected/var_parameter.js

@@ -1,16 +1,16 @@
 var RTL$ = {
 	extend: function extend(methods){
 		methods.__proto__ = this.prototype; // make instanceof work
-	
+
 		// to see constructor name in diagnostic
 		var result = methods.init;
 		methods.constructor = result.prototype.constructor;
-	
+
 		result.prototype = methods;
 		result.extend = extend;
 		return result;
 	},
-	makeArray: function RTLMakeArray(/*dimensions, initializer*/){
+	makeArray: function (/*dimensions, initializer*/){
 		var forward = Array.prototype.slice.call(arguments);
 		var result = new Array(forward.shift());
 		var i;
@@ -28,7 +28,7 @@ var RTL$ = {
 				result[i] = RTLMakeArray.apply(this, forward);
 		return result;
 	},
-	makeRef: function RTLMakeRef(obj, prop){
+	makeRef: function (obj, prop){
 	    return {set: function(v){ obj[prop] = v; },
 	            get: function(){ return obj[prop]; }};
 	}

+ 8 - 1
test/input/string.ob

@@ -6,7 +6,14 @@ CONST
 	s3 = "with space";
 VAR
 	a2: ARRAY 3 OF CHAR;
+
+PROCEDURE p1(s: ARRAY OF CHAR);
+END p1;
+
 BEGIN
 	a2 := s1;
-	a2 := s2
+	a2 := s2;
+
+	p1(s1);
+	p1(s2)
 END m.

+ 4 - 1
test/test_compile.js

@@ -16,7 +16,10 @@ function compareResults(result, name, dirs){
 
 function expectOk(src, dirs){
     var text = fs.readFileSync(src, "utf8");
-    var result = oc.compile(text);
+    var errors = "";
+    var result = oc.compile(text, function(e){errors += e;});
+    if (errors)
+        throw new Test.TestError(errors);
     var resultName = path.basename(src).replace(".ob", ".js");
     compareResults(result, resultName, dirs);
 }

+ 19 - 3
test/test_unit.js

@@ -629,7 +629,7 @@ procedure: function(){
 	test.parse("PROCEDURE p(a: INTEGER); BEGIN p(a) END p");
 	test.parse("PROCEDURE p(a: INTEGER; b: BOOLEAN); BEGIN p(a, b) END p");
 	test.expectError("PROCEDURE p(a: INTEGER; b: BOOLEAN); BEGIN p(b, a) END p"
-				  , "expect 'INTEGER' type for argument 0, got 'BOOLEAN'");
+				   , "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'");
 	test.expectError("PROCEDURE p; BEGIN p1() END p", "undeclared identifier: 'p1'");
 
 	test.parse("PROCEDURE p(): ProcType; RETURN p END p");
@@ -714,8 +714,8 @@ procedure: function(){
 
 	test.parse("p2(1, TRUE)");
 	test.expectError("notProcedure", "PROCEDURE expected, got 'INTEGER'");
-	test.expectError("p2(TRUE, 1)", "expect 'INTEGER' type for argument 0, got 'BOOLEAN'");
-	test.expectError("p2(1, 1)", "expect 'BOOLEAN' type for argument 1, got 'INTEGER'");
+	test.expectError("p2(TRUE, 1)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'");
+	test.expectError("p2(1, 1)", "type mismatch for argument 2: 'INTEGER' cannot be converted to 'BOOLEAN'");
 	test.expectError("p3()()", "not parsed");
 },
 "procedure assignment": function(){
@@ -765,6 +765,22 @@ procedure: function(){
 	test.parse("a1 := 22X");
 	test.expectError("a1 := \"abcd\"", "3-character ARRAY is too small for 4-character string");
 },
+"string assignment to open array fails": function(){
+	var test = setup(Grammar.procedureDeclaration);
+	test.expectError("PROCEDURE p(s: ARRAY OF CHAR); BEGIN s := \"abc\" END p", "cannot assign to read-only variable");
+	test.expectError("PROCEDURE p(VAR s: ARRAY OF CHAR); BEGIN s := \"abc\" END p", "string cannot be assigned to open ARRAY OF CHAR");
+},
+"string argument": function(){
+	var test = setupWithContext(
+		  Grammar.statement
+		, "PROCEDURE p1(s: ARRAY OF CHAR); END p1;"
+		+ "PROCEDURE p2(VAR s: ARRAY OF CHAR); END p2;"
+		+ "PROCEDURE p3(i: INTEGER); END p3;"
+		);
+	test.parse("p1(\"abc\")");
+	test.expectError("p2(\"abc\")", "expression cannot be used as VAR parameter");
+	test.expectError("p3(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'INTEGER'");
+},
 "scope": function(){
 	var test = setup(Grammar.declarationSequence);
 	test.parse("PROCEDURE p1(a1: INTEGER); END p1; PROCEDURE p2(a1: BOOLEAN); END p2;");