Ver Fonte

more built-in functions + bug fixes

Vladislav Folts há 12 anos atrás
pai
commit
6e60392f59

+ 27 - 14
src/cast.js

@@ -1,18 +1,30 @@
+var Class = require("rtl.js").Class;
 var Type = require("type.js");
 var ArrayType = Type.Array;
 var PointerType = Type.Pointer;
 
+var Operation = Class.extend({
+	init: function Operation(convert){this.__convert = convert;},
+	code: function(context, code){return this.__convert ? this.__convert(context, code) : code;}
+});
+
+var doNoting = new Operation();
+
 function implicitCast(from, to){
-	if (from == to)
-		return true;
+	if (from === to)
+		return doNoting;
 
 	//if (from instanceof VarParameter)
 	//	return implicitCast(from.type(), (to instanceof VarParameter) ? to.type() : to);
 	
-	if ((to === Type.basic.char) && (from instanceof Type.String)){
-		var v = from.asChar();
-		if (v !== undefined)
-			return true;
+	if (from instanceof Type.String){
+		if (to === Type.basic.char){
+			var v = from.asChar();
+			if (v !== undefined)
+				return new Operation(function(){return v;});
+		}
+		else if (to instanceof Type.Array && to.elementsType() == Type.basic.char)
+			return new Operation(function(context, code){return context.rtl().strToArray(code);});
 	}
 	else if (from instanceof ArrayType && to instanceof ArrayType)
 		return implicitCast(from.elementsType(), to.elementsType());
@@ -23,11 +35,11 @@ function implicitCast(from, to){
 		while (fromR && fromR != toR)
 			fromR = fromR.baseType();
 		if (fromR)
-			return true;
+			return doNoting;
 	}
 	else if (from == Type.nil
 		&& (to instanceof PointerType || to.isProcedure()))
-		return true;
+		return doNoting;
 	else if (from.isProcedure() && to.isProcedure()){
 		var fromArgs = from.arguments();
 		var toArgs = to.arguments();
@@ -36,21 +48,22 @@ function implicitCast(from, to){
 				var fromArg = fromArgs[i];
 				var toArg = toArgs[i];
 				if (toArg.isVar != fromArg.isVar)
-					return false;
+					return undefined;
 				if ((toArg.type != to || fromArg.type != from)
 					&& !implicitCast(fromArg.type, toArg.type))
-					return false;
+					return undefined;
 			}
 
 			var fromResult = from.result();
 			var toResult = to.result();
 			if (toResult == to && fromResult == from)
-				return true;
+				return doNoting;
 			if (implicitCast(fromResult, toResult))
-				return true;
+				return doNoting;
 		}
 	}
-	return false;
+	return undefined;
 }
 
-exports.implicit = implicitCast;
+exports.implicit = implicitCast;
+exports.Operation = Operation;

+ 29 - 20
src/context.js

@@ -1221,7 +1221,9 @@ exports.Assignment = ChainedContext.extend({
 	init: function AssignmentContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
 		this.__designator = undefined;
-		this.__leftOp = undefined;
+		this.__leftCode = undefined;
+        this.__rightCode = undefined;
+        this.__resultCode = undefined;
 		this.__type = undefined;
 		this.__code = new Code.SimpleGenerator();
 	},
@@ -1234,11 +1236,9 @@ exports.Assignment = ChainedContext.extend({
 		var d_info = d.info();
 		if (!(d_info instanceof Type.Variable) || d_info.isReadOnly())
 			throw new Errors.Error("cannot assign to " + d_info.idType());
-		this.__leftOp = d.code();
+		this.__leftCode = d.code();
+        this.__code = new Code.SimpleGenerator();
 		this.__type = d.type();
-		if (!(this.__type instanceof Type.Array)
-			&& !(this.__type instanceof Type.Record))
-			this.__code.write(this.__leftOp + (d_info.isVar() ? ".set(" : " = "));
 	},
 	handleExpression: function(type, value, designator){
 		var isArray = this.__type instanceof Type.Array;
@@ -1249,43 +1249,52 @@ exports.Assignment = ChainedContext.extend({
 				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 " 
+					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()));
+			this.__resultCode = this.rtl().assignArrayFromString(this.__leftCode, this.__code.result());
 			return;
 		}
-		
-		if (!Cast.implicit(type, this.__type))
-			throw new Errors.Error("type mismatch: '" + this.__leftOp
+
+		var castOperation = Cast.implicit(type, this.__type);
+		if (!castOperation)
+			throw new Errors.Error("type mismatch: '" + this.__leftCode
 								 + "' is '" + this.__type.description()
 								 + "' and cannot be assigned to '" + type.description() + "' expression");
 
 		if (isArray && type instanceof Type.Array)
 			if (this.__type.length() === undefined)
-				throw new Errors.Error("'" + this.__leftOp
+				throw new Errors.Error("'" + this.__leftCode
 									 + "' is open '" + this.__type.description()
 									 + "' and cannot be assigned");
 			else if (type.length() === undefined)
-				throw new Errors.Error("'" + this.__leftOp
+				throw new Errors.Error("'" + this.__leftCode
 									 + "' cannot be assigned to open '"
 									 + this.__type.description() + "'");
 			else if (this.__type.length() != type.length())
-				throw new Errors.Error("array size mismatch: '" + this.__leftOp
+				throw new Errors.Error("array size mismatch: '" + this.__leftCode
 									 + "' has size " + this.__type.length()
 									 + " and cannot be assigned to the array with size " + type.length());
 		
-		if (isArray || type instanceof Type.Record)
-			this.__code = new Code.SimpleGenerator(
-					this.rtl().copy(this.__code.result(), this.__leftOp));
+		if (isArray || type instanceof Type.Record){
+			this.__resultCode = this.rtl().copy(this.__code.result(), this.__leftCode);
+            return;
+        }
 
 		if (designator)
 			writeDerefDesignatorCode(designator, this.__code);
+
+        var castCode = castOperation.code();
+        this.__rightCode = castCode ? castCode : this.__code.result();
 	},
 	endParse: function(){
-		if (this.__designator.info().isVar())
-			this.__code.write(")");
-		this.parent().codeGenerator().write(this.__code.result());
+		var code = this.__resultCode;
+		if (!code)
+			code = this.__leftCode
+				 + (this.__designator.info().isVar()
+					? ".set(" + this.__rightCode + ")"
+					: " = " + this.__rightCode);
+
+		this.parent().codeGenerator().write(code);
 	}
 });
 

+ 75 - 16
src/procedure.js

@@ -62,7 +62,7 @@ var ProcCallGenerator = Class.extend({
 			}
 		
 		if (convert)
-			code = convert(code);
+			code = convert.code(this.__context, code);
 		
 		var prefix = pos ? ", " : "";
 		this.writeCode(prefix + code);
@@ -82,16 +82,14 @@ var ProcCallGenerator = Class.extend({
 	},
 	checkArgument: function(pos, type, designator){
 		var arg = this.__type.arguments()[pos];
-		var convert;
+		var castOperation;
 		var expectType = arg.type; // can be undefined for predefined functions (like NEW), dont check it in this case
-		if (expectType && !Cast.implicit(type, expectType))
-			if (type instanceof Type.String && expectType instanceof Type.Array){
-				var rtl = this.__context.rtl();
-				convert = function(code){return rtl.strToArray(code);};
-			}
-			else
+		if (expectType){
+            castOperation = Cast.implicit(type, expectType);
+            if (!castOperation)
 				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");
@@ -101,7 +99,7 @@ var ProcCallGenerator = Class.extend({
 			if (info.isReadOnly())
 				throw new Errors.Error("read-only variable cannot be used as VAR parameter");
 		}
-		return new CheckArgumentResult(arg.type, arg.isVar, convert);
+		return new CheckArgumentResult(arg.type, arg.isVar, castOperation);
 	},
 	epilog: function(){return ")";},
 	writeCode: function(s){this.codeGenerator().write(s);}
@@ -211,17 +209,17 @@ exports.predefined = [
 			},
 			prolog: function(id){return "";},
 			checkArgument: function(pos, type, designator){
-				ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
-				if (!(type instanceof Type.Array))
-					throw new Errors.Error("ARRAY expected, got '"
-										 + type.name() + "'");
-				return new CheckArgumentResult(type, false);
+				if (type instanceof Type.Array || type instanceof Type.String)
+                    return new CheckArgumentResult(type, false);
+                
+                // should throw error
+                ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
 			},
 			epilog: function(){return ".length";}
 		});
 
 		var name = "LEN";
-		var args = [new Arg(undefined, false)];
+		var args = [new Arg(new Type.Array("ARRAY OF any type"), false)];
 		var type = new Type.Procedure(new ProcType(
 			"predefined procedure LEN",
 			args,
@@ -232,6 +230,26 @@ exports.predefined = [
 		var symbol = new Type.Symbol(name, type);
 		return symbol;
 	}(),
+    function(){
+        var CallGenerator = ProcCallGenerator.extend({
+            init: function OddProcCallGenerator(context, id, type){
+                ProcCallGenerator.prototype.init.call(this, context, id, type);
+            },
+            prolog: function(){return "(";},
+            epilog: function(){return " & 1)";}
+        });
+        var name = "ODD";
+        var args = [new Arg(Type.basic.int, false)];
+        var type = new Type.Procedure(new ProcType(
+            "predefined procedure ODD",
+            args,
+            Type.basic.bool,
+            function(context, id, type){
+                return new CallGenerator(context, id, type);
+            }));
+        var symbol = new Type.Symbol(name, type);
+        return symbol;
+    }(),
 	function(){
 		var AssertProcCallGenerator = ProcCallGenerator.extend({
 			init: function AssertProcCallGenerator(context, id, type){
@@ -287,7 +305,48 @@ exports.predefined = [
 		var type = new Type.Procedure(proc);
 		var symbol = new Type.Symbol("EXCL", type);
 		return symbol;
-	}()
+	}(),
+    function(){
+        var CallGenerator = ProcCallGenerator.extend({
+            init: function OrdProcCallGenerator(context, id, type){
+                ProcCallGenerator.prototype.init.call(this, context, id, type);
+            },
+            prolog: function(){return "";},
+            epilog: function(){return "";},
+            checkArgument: function(pos, type, designator){
+                if (type == Type.basic.char || type == Type.basic.set )
+                    return new CheckArgumentResult(type, false);
+                else if (type == Type.basic.bool)
+                    return new CheckArgumentResult(
+                        type,
+                        false,
+                        new Cast.Operation(function(context, code){return "((" + code + ") ? 1 : 0)";}));
+                else if (type instanceof Type.String){
+                    var ch = type.asChar();
+                    if (ch !== undefined)
+                        return new CheckArgumentResult(
+                            type,
+                            false,
+                            new Cast.Operation(function(){return ch;}));
+                }
+
+            // should throw error
+            ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
+            }
+        });
+        var name = "ORD";
+        var argType = new Type.Basic("CHAR or BOOLEAN or SET");
+        var args = [new Arg(argType, false)];
+        var type = new Type.Procedure(new ProcType(
+            "predefined procedure " + name,
+            args,
+            Type.basic.int,
+            function(context, id, type){
+                return new CallGenerator(context, id, type);
+            }));
+        var symbol = new Type.Symbol(name, type);
+        return symbol;
+    }()
 	];
 
 exports.CallGenerator = ProcCallGenerator;

+ 3 - 0
test/expected/len.js

@@ -24,6 +24,7 @@ var RTL$ = {
     }
 };
 var m = function (){
+var s1 = "\"";
 var a1 = RTL$.makeArray(10, 0);
 var a2 = RTL$.makeArray(15, false);
 var a3 = RTL$.makeArray(20, 0);
@@ -38,4 +39,6 @@ function p2(a/*VAR ARRAY OF BOOLEAN*/){
 p1(a1);
 p2(a2);
 RTL$.assert(a3.length == 20);
+RTL$.assert("\"".length == 1);
+RTL$.assert("abc".length == 3);
 }();

+ 14 - 0
test/expected/odd.js

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

+ 17 - 0
test/expected/ord.js

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

+ 6 - 0
test/expected/string.js

@@ -35,12 +35,18 @@ var m = function (){
 var s1 = "\"";
 var s2 = "ABC";
 var s3 = "with space";
+var ch1 = 0;
 var a2 = RTL$.makeArray(3, 0);
 
 function p1(s/*ARRAY OF CHAR*/){
 }
+
+function p2(c/*CHAR*/){
+}
+ch1 = 34;
 RTL$.assignArrayFromString(a2, "\"");
 RTL$.assignArrayFromString(a2, "ABC");
 p1(RTL$.strToArray("\""));
 p1(RTL$.strToArray("ABC"));
+p2(34);
 }();

+ 6 - 1
test/input/len.ob

@@ -1,5 +1,8 @@
 MODULE m;
                 
+CONST
+    s1 = 22X;
+
 VAR
     a1: ARRAY 10 OF INTEGER;
     a2: ARRAY 15 OF BOOLEAN;
@@ -16,5 +19,7 @@ END p2;
 BEGIN
 	p1(a1);
 	p2(a2);
-	ASSERT(LEN(a3) = 20)
+	ASSERT(LEN(a3) = 20);
+    ASSERT(LEN(s1) = 1);
+    ASSERT(LEN("abc") = 3)
 END m.

+ 10 - 0
test/input/odd.ob

@@ -0,0 +1,10 @@
+MODULE m;
+
+VAR i: INTEGER;
+                
+BEGIN
+	ASSERT(ODD(1));
+	i := 4;
+	ASSERT(ODD(1 + i));
+	ASSERT(~ODD(2))
+END m.

+ 15 - 0
test/input/ord.ob

@@ -0,0 +1,15 @@
+MODULE m;
+
+VAR 
+	ch: CHAR;
+	set: SET;
+                
+BEGIN
+	ch := "a";
+	ASSERT(ORD(ch) = ORD("a"));
+
+	set := {1};
+	ASSERT(ORD({1}) = ORD(set));
+	ASSERT(ORD(TRUE) = 1);
+	ASSERT(ORD(FALSE) = 0)
+END m.

+ 7 - 1
test/input/string.ob

@@ -5,15 +5,21 @@ CONST
 	s2 = "ABC";
 	s3 = "with space";
 VAR
+    ch1: CHAR;
 	a2: ARRAY 3 OF CHAR;
 
 PROCEDURE p1(s: ARRAY OF CHAR);
 END p1;
 
+PROCEDURE p2(c: CHAR);
+END p2;
+
 BEGIN
+    ch1 := s1;
 	a2 := s1;
 	a2 := s2;
 
 	p1(s1);
-	p1(s2)
+	p1(s2);
+    p2(s1)
 END m.

+ 23 - 1
test/test_unit.js

@@ -327,8 +327,28 @@ expression: function(){
 
 	test.parse("PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a) END p");
 	test.parse("PROCEDURE p(VAR a: ARRAY OF BOOLEAN): INTEGER; RETURN LEN(a) END p");
+	test.parse("PROCEDURE p(): INTEGER; RETURN LEN(\"abc\") END p");
 	test.expectError("PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a[0]) END p",
-					 "ARRAY expected, got 'INTEGER'");
+					 "type mismatch for argument 1: 'INTEGER' cannot be converted to 'ARRAY OF any type'");
+},
+"ODD": function(){
+	var test = setup(Grammar.statement);
+
+	test.parse("ODD(1)");
+	test.parse("ASSERT(ODD(123))");
+	test.expectError("ODD(1.2)", "type mismatch for argument 1: 'REAL' cannot be converted to 'INTEGER'");
+	test.expectError("ODD(TRUE)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'");
+},
+"ORD": function(){
+	var test = setupWithContext(Grammar.statement, "VAR ch: CHAR;");
+
+	test.parse("ORD(ch)");
+	test.parse("ORD(TRUE)");
+	test.parse("ORD({1})");
+	test.parse("ORD(\"a\")");
+	test.parse("ASSERT(ORD(22X) = 022H)");
+	test.expectError("ORD(1.2)", "type mismatch for argument 1: 'REAL' cannot be converted to 'CHAR or BOOLEAN or SET'");
+	test.expectError("ORD(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'CHAR or BOOLEAN or SET'");
 },
 "assignment statement": function(){
 	var test = setupWithContext(
@@ -828,10 +848,12 @@ procedure: function(){
 		, "PROCEDURE p1(s: ARRAY OF CHAR); END p1;"
 		+ "PROCEDURE p2(VAR s: ARRAY OF CHAR); END p2;"
 		+ "PROCEDURE p3(i: INTEGER); END p3;"
+		+ "PROCEDURE p4(a: ARRAY OF INTEGER); END p4;"
 		);
 	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'");
+	test.expectError("p4(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'ARRAY OF INTEGER'");
 },
 "scope": function(){
 	var test = setup(Grammar.declarationSequence);