Просмотр исходного кода

more built-in procedures, eliminate extra parentheses from generated code, bug fixes

Vladislav Folts 12 лет назад
Родитель
Сommit
8e5654c8fc
13 измененных файлов с 403 добавлено и 349 удалено
  1. 6 10
      src/cast.js
  2. 44 0
      src/code.js
  3. 261 270
      src/context.js
  4. 1 1
      src/grammar.js
  5. 8 0
      src/operator.js
  6. 62 51
      src/procedure.js
  7. 1 1
      test/expected/case.js
  8. 2 2
      test/expected/const.js
  9. 1 1
      test/expected/len.js
  10. 2 2
      test/expected/ord.js
  11. 8 6
      test/expected/set.js
  12. 4 4
      test/expected/string.js
  13. 3 1
      test/input/set.ob

+ 6 - 10
src/cast.js

@@ -1,14 +1,9 @@
-var Class = require("rtl.js").Class;
+var Code = require("code.js");
 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 doNoting(context, e){return e;}
 
 function implicitCast(from, to){
 	if (from === to)
@@ -21,10 +16,12 @@ function implicitCast(from, to){
 		if (to === Type.basic.char){
 			var v = from.asChar();
 			if (v !== undefined)
-				return new Operation(function(){return v;});
+				return function(){return new Code.Expression(v, to);};
 		}
 		else if (to instanceof Type.Array && to.elementsType() == Type.basic.char)
-			return new Operation(function(context, code){return context.rtl().strToArray(code);});
+			return function(context, e){
+				return new Code.Expression(context.rtl().strToArray(e.code()), to);
+			};
 	}
 	else if (from instanceof ArrayType && to instanceof ArrayType)
 		return implicitCast(from.elementsType(), to.elementsType());
@@ -66,4 +63,3 @@ function implicitCast(from, to){
 }
 
 exports.implicit = implicitCast;
-exports.Operation = Operation;

+ 44 - 0
src/code.js

@@ -1,4 +1,5 @@
 var Class = require("rtl.js").Class;
+var Type = require("type.js");
 
 var NullCodeGenerator = Class.extend({
 	init: function NullCodeGenerator(){},
@@ -34,4 +35,47 @@ exports.SimpleGenerator = Class.extend({
 	result: function(){return this.__result;}
 });
 
+var Expression = Class.extend({
+	init: function Expression(code, type, designator, constValue, maxPrecedence){
+        this.__code = code;
+        this.__type = type;
+        this.__designator = designator;
+        this.__constValue = constValue;
+        this.__maxPrecedence = maxPrecedence;
+    },
+    code: function(){return this.__code;},
+    type: function(){return this.__type;},
+    designator: function(){return this.__designator;},
+    constValue: function(){return this.__constValue;},
+    maxPrecedence: function(){return this.__maxPrecedence;},
+    deref: function(){
+        if (!this.__designator)
+            return this;
+        if (this.__type instanceof Type.Array || this.__type instanceof Type.Record)
+            return this;
+        var info = this.__designator.info();
+        if (!(info instanceof Type.Variable && info.isVar()))
+            return this;
+        return new Expression(this.__code + ".get()", this.__type);
+    },
+    ref: function(){
+        if (!this.__designator)
+            return this;
+        
+        var info = this.__designator.info();
+        if (info instanceof Type.Variable && info.isVar())
+            return this;
+
+        return new Expression(this.__designator.refCode(), this.__type);
+    }
+});
+
+exports.adjustPrecedence = function(e, precedence){
+    var code = e.code();
+    if (e.maxPrecedence() > precedence)
+        code = "(" + code + ")";
+    return code;
+};
+
 exports.nullGenerator = new NullCodeGenerator();
+exports.Expression = Expression;

+ 261 - 270
src/context.js

@@ -3,6 +3,7 @@ var Code = require("code.js");
 var Errors = require("errors.js");
 var Lexer = require("lexer.js");
 var Module = require("module.js");
+var precedence = require("operator.js").precedence;
 var Parser = require("parser.js");
 var Procedure = require("procedure.js");
 var ImportRTL = require("rtl.js");
@@ -22,6 +23,11 @@ function getSymbol(context, id){
 	return s;
 }
 
+function checkImplicitCast(from, to){
+	if (!Cast.implicit(from, to))
+		throw new Errors.Error("type mismatch: expected '" + to.name() + "', got '" + from.name() + "'");
+}
+
 function checkTypeCast(from, to, msg){
 	if (from instanceof Type.Pointer)
 		from = from.baseType();
@@ -45,9 +51,9 @@ var ChainedContext = Class.extend({
 	popScope: function(){this.__parent.popScope();},
 	setType: function(type){this.__parent.setType(type);},
 	setDesignator: function(d){this.__parent.setDesignator(d);},
-	handleExpression: function(type, value, designator){this.__parent.handleExpression(type, value, designator);},
+	handleExpression: function(e){this.__parent.handleExpression(e);},
 	handleLiteral: function(s){this.__parent.handleLiteral(s);},
-	handleConst: function(type, value){this.__parent.handleConst(type, value);},
+	handleConst: function(type, value, code){this.__parent.handleConst(type, value, code);},
 	genTypeName: function(){return this.__parent.genTypeName();},
 	genVarName: function(id){return this.__parent.genVarName(id);},
 	rtl: function(){return this.__parent.rtl();}
@@ -65,8 +71,7 @@ exports.Integer = ChainedContext.extend({
 	toInt: function(s){return parseInt(this.__result);},
 	endParse: function(){
 		var n = this.toInt();
-		this.parent().codeGenerator().write(n.toString());
-		this.parent().handleConst(basicTypes.int, n);
+		this.parent().handleConst(basicTypes.int, n, n.toString());
 	}
 });
 
@@ -91,8 +96,7 @@ exports.Real = ChainedContext.extend({
 	},
 	endParse: function(){
 		var n = Number(this.__result);
-		this.parent().codeGenerator().write(n.toString());
-		this.parent().handleConst(basicTypes.real, n);
+		this.parent().handleConst(basicTypes.real, n, n.toString());
 	}
 });
 
@@ -117,8 +121,7 @@ exports.String = ChainedContext.extend({
 	toStr: function(s){return s;},
 	endParse: function(){
 		var s = this.toStr(this.__result);
-		this.parent().codeGenerator().write(escapeString(s));
-		this.parent().handleConst(new Type.String(s), s);
+		this.parent().handleConst(new Type.String(s), s, escapeString(s));
 		}
 });
 
@@ -188,19 +191,22 @@ exports.Designator = ChainedContext.extend({
 		this.__code.write(id);
 	},
 	codeGenerator: function(){return this.__code;},
-	handleExpression: function(expType, value, designator){
+	handleExpression: function(e){
+        var expType = e.type();
 		if (expType != basicTypes.int)
 			throw new Errors.Error("'INTEGER' expression expected, got '" + expType.name() + "'");
 
 		var type = this.__currentType;
 		if (!(type instanceof Type.Array))
 			throw new Errors.Error("ARRAY expected, got '" + type.name() + "'");
+        var value = e.constValue();
 		if (value !== undefined && value >= type.length())
 			throw new Errors.Error("index out of bounds: maximum possible index is "
 								 + (type.length() - 1)
 								 + ", got " + value );
 		this.__currentType = type.elementsType();
 		this.__info = new Type.Variable(this.__currentType, false, this.__info.isReadOnly());
+        var designator = e.designator();
 		if (designator)
 			writeDerefDesignatorCode(designator, this.__code);
 	},
@@ -419,8 +425,9 @@ exports.Return = ChainedContext.extend({
 		this.__code = new Code.SimpleGenerator();
 	},
 	codeGenerator: function(){return this.__code;},
-	handleExpression: function(type, value, designator){
-		this.__type = type;
+	handleExpression: function(e){
+		this.__type = e.type();
+        var designator = e.designator();
 		if (designator)
 			writeDerefDesignatorCode(designator, this.__code);
 	},
@@ -516,9 +523,11 @@ exports.ArrayDimensions = ChainedContext.extend({
 		this.__dimensions = [];
 	},
 	codeGenerator: function(){return Code.nullGenerator;},
-	handleExpression: function(type, value){
+	handleExpression: function(e){
+        var type = e.type();
 		if (type !== basicTypes.int)
 			throw new Errors.Error("'INTEGER' constant expression expected, got '" + type.name() + "'");
+        var value = e.constValue();
 		if (value === undefined)
 			throw new Errors.Error("constant expression expected as ARRAY size");
 		if (value <= 0)
@@ -530,6 +539,33 @@ exports.ArrayDimensions = ChainedContext.extend({
 	}
 });
 
+function makeUnaryOperator(op, code){
+	return function(e){
+		var type = e.type();
+        var value = e.constValue();
+        if (value !== undefined)
+            value = op(value, type) ;
+        var expCode = ((typeof code == "function") ? code(type) : code) + e.deref().code();
+		return new Code.Expression(expCode, type, undefined, value);
+	};
+}
+
+function makeBinaryOperator(op, code){
+	return function(left, right){
+		var leftValue = left.constValue();
+		var rightValue = right.constValue();
+		var value = (leftValue !== undefined && rightValue !== undefined)
+			? op(leftValue, rightValue) : undefined;
+
+        var leftCode = left.deref().code();
+        var rightCode = right.deref().code();
+        var expCode = (typeof code == "function")
+                    ? code(leftCode, rightCode)
+                    : leftCode + code + rightCode;
+		return new Code.Expression(expCode,	left.type(), undefined,	value);
+	};
+}
+
 exports.AddOperator = ChainedContext.extend({
 	init: function AddOperatorContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
@@ -537,36 +573,52 @@ exports.AddOperator = ChainedContext.extend({
 	handleLiteral: function(s){
 		var parent = this.parent();
 		if (s == "+"){
-			if (parent.type() == basicTypes.set){
-				parent.handleBinaryOperator(function(x, y){return x | y;});
-				parent.codeGenerator().write(" | ");
-			}
-			else {
-				parent.handleBinaryOperator(function(x, y){return x + y;});
-				parent.codeGenerator().write(" + ");
-			}
+			if (parent.type() == basicTypes.set)
+				parent.handleBinaryOperator(
+					makeBinaryOperator(function(x, y){return x | y;}, " | "));
+			else
+				parent.handleBinaryOperator(
+					makeBinaryOperator(function(x, y){return x + y;}, " + "));
 		}
 		else if (s == "-"){
-			if (parent.type() == basicTypes.set){
-				parent.handleBinaryOperator(function(x, y){return x & ~y;});
-				parent.codeGenerator().write(" & ~");
-			}
-			else {
-				parent.handleBinaryOperator(function(x, y){return x - y;});
-				parent.codeGenerator().write(" - ");
-			}
+			if (parent.type() == basicTypes.set)
+				parent.handleBinaryOperator(
+					makeBinaryOperator(function(x, y){return x & ~y;}, " & ~"));
+			else
+				parent.handleBinaryOperator(
+					makeBinaryOperator(function(x, y){return x - y;}, " - "));
 		}
 		else if (s == "OR"){
 			var type = parent.type();
 			if (type != basicTypes.bool)
 				throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
 									 + type.name() + "'");
-			parent.handleBinaryOperator(function(x, y){return x || y;});
-			this.codeGenerator().write(" || ");
+			parent.handleBinaryOperator(
+				makeBinaryOperator(function(x, y){return x || y;}, " || "));
 		}
 	}
 });
-
+/*
+var Operator = Class.extend({
+	init: function Operator(eval, code){
+		this.__eval = eval;
+		this.__code = code;
+	},
+	eval: function(x, y){return this.__eval(x, y);},
+	code: function(x, y){return this.__code(x, y);},
+	expression: function(x, y){
+		var xValue = x ? x.constValue() : undefined;
+		var yValue = y.constValue();
+		var value = ((!x || xValue) && yValue) ? this.__eval(xValue, yValue) : undefined;
+		return new Code.Expression(
+			this.__code(x ? x.code() : undefined, y.code()),
+			y.type(),
+			undefined,
+			value
+			);
+	}
+});
+*/
 exports.MulOperator = ChainedContext.extend({
 	init: function MulOperatorContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
@@ -575,45 +627,33 @@ exports.MulOperator = ChainedContext.extend({
 		var parent = this.parent();
 		if (s == "*")
 			if (parent.type() == basicTypes.set)
-				parent.handleOperator({
-					  eval: function(x, y){return x & y;}
-					, code: function(x, y){return x + " & " + y;}
-					});
+				parent.handleOperator(makeBinaryOperator(
+					  function(x, y){return x & y;}, " & "));
 			else
-				parent.handleOperator({
-					  eval: function(x, y){return x * y;}
-					, code: function(x, y){return x + " * " + y;}
-					});
+				parent.handleOperator(makeBinaryOperator(
+					  function(x, y){return x * y;}, " * "));
 		else if (s == "/")
 			if (parent.type() == basicTypes.set)
-				parent.handleOperator({
-					  eval: function(x, y){return x ^ y;}
-					, code: function(x, y){return x + " ^ " + y;}
-					});
+				parent.handleOperator(makeBinaryOperator(
+					  function(x, y){return x ^ y;}, " ^ "));
 			else
-				parent.handleOperator({
-					  eval: function(x, y){return x / y;}
-					, code: function(x, y){return x + " / " + y;}
-					});
+				parent.handleOperator(makeBinaryOperator(
+					  function(x, y){return x / y;}, " / "));
 		else if (s == "DIV")
-			parent.handleOperator({
-				  eval: function(x, y){return (x / y) >> 0;}
-				, code: function(x, y){return "(" + x + " / " + y + ") >> 0";}
-				});
+			parent.handleOperator(makeBinaryOperator(
+				  function(x, y){return (x / y) >> 0;}
+				, function(x, y){return "(" + x + " / " + y + ") >> 0";}
+				));
 		else if (s == "MOD")
-			parent.handleOperator({
-				  eval: function(x, y){return x % y;}
-				, code: function(x, y){return x + " % " + y;}
-				});
+			parent.handleOperator(makeBinaryOperator(
+				  function(x, y){return x % y;}, " % "));
 		else if (s == "&"){
 			var type = parent.type();
 			if (type != basicTypes.bool)
 				throw new Errors.Error("BOOLEAN expected as operand of '&', got '"
 									 + type.name() + "'");
-			parent.handleOperator({
-				  eval: function(x, y){return x && y;}
-				, code: function(x, y){return x + " && " + y;}
-				});
+			parent.handleOperator(makeBinaryOperator(
+				  function(x, y){return x && y;}, " && "));
 		}
 	}
 });
@@ -633,6 +673,7 @@ exports.Term = ChainedContext.extend({
 		this.__isConst = true;
 		this.__value = undefined;
 		this.__designator = undefined;
+		this.__expression = undefined;
 	},
 	codeGenerator: function(){return this.__code;},
 	type: function(){return this.parent().type();},
@@ -640,42 +681,50 @@ exports.Term = ChainedContext.extend({
 		var type = d.type();
 		this.parent().setType(type);
 
+		var value;
 		var info = d.info();
 		if (!(info instanceof Type.Const))
 			this.__isConst = false;
-		else
-			this.handleConst(type, info.value());
+		else {
+			value = info.value();
+			this.handleConst(type, info.value(), d.code());
+		}
 		
+		this.__handleExpression(
+			new Code.Expression(d.code(), d.type(), d, value));
+
 		this.__code.write(d.code());
 		this.__designator = d;
-		if (this.__operator)
-			this.__derefDesignator();
+		//if (this.__operator)
+		//	this.__derefDesignator();
 	},
 	handleOperator: function(o){
 		this.__derefDesignator();
-		this.__left = this.__operator
-			? this.__operator.code(this.__left, this.__code.result())
-			: this.__code.result();
+		//this.__left = this.__operator
+		//	? this.__operator.code(this.__left, this.__code.result())
+		//	: this.__code.result();
 		this.__operator = o;
 		this.__code = new Code.SimpleGenerator();
 	},
-	handleConst: function(type, value){
+	handleConst: function(type, value, code){
 		this.parent().setType(type);
-		if (value === undefined)
-			this.__isConst = false;
-		else if (this.__isConst)
-			this.__value = this.__operator ? this.__operator.eval(this.__value, value)
-										   : value;
-	},
-	procCalled: function(type){this.parent().procCalled(type);},
-	endParse: function(){
-		var code = this.__operator ? this.__operator.code(this.__left, this.__code.result())
-								   : this.__code.result();
-		this.parent().handleTerm(
-			  code
-			, this.__isConst ? this.__value : undefined
-			, this.__operator ? undefined : this.__designator);
+		//if (value === undefined)
+		//	this.__isConst = false;
+		//else if (this.__isConst)
+		//	this.__value = this.__operator ? this.__operator.eval(this.__value, value)
+		//								   : value;
+		this.__handleExpression(new Code.Expression(
+			code, type, undefined, value));
+	},
+	handleFactor: function(e){
+		var type = e.type();
+		if (!type)
+			throw new Errors.Error("procedure returning no result cannot be used in an expression");
+		this.setType(type);
+		this.__handleExpression(e);
+		this.__code.write(e.code());
 	},
+	endParse: function(){this.parent().handleTerm(this.__expression);},
 	__derefDesignator: function(){
 		var designator = this.__designator;
 		if (!designator)
@@ -683,6 +732,12 @@ exports.Term = ChainedContext.extend({
 
 		writeDerefDesignatorCode(designator, this.__code);
 		this.__designator = undefined;
+	},
+	__handleExpression: function(e){
+		if (this.__operator)
+			e = this.__expression ? this.__operator(this.__expression, e)
+                                  : this.__operator(e);
+		this.__expression = e;
 	}
 });
 
@@ -693,27 +748,19 @@ exports.Factor = ChainedContext.extend({
 	type: function(){return this.parent().type();},
 	handleLiteral: function(s){
 		var parent = this.parent();
-		if (s == "NIL"){
-			parent.handleConst(Type.nil, undefined);
-			this.codeGenerator().write("null");
-		}
-		else if (s == "TRUE"){
-			parent.handleConst(basicTypes.bool, true);
-			this.codeGenerator().write("true");
-		}
-		else if (s == "FALSE"){
-			parent.handleConst(basicTypes.bool, false);
-			this.codeGenerator().write("false");
-		}
+		if (s == "NIL")
+			parent.handleConst(Type.nil, undefined, "null");
+		else if (s == "TRUE")
+			parent.handleConst(basicTypes.bool, true, "true");
+		else if (s == "FALSE")
+			parent.handleConst(basicTypes.bool, false, "false");
 		else if (s == "~"){
 			parent.setType(basicTypes.bool);
-			parent.handleOperator({
-				  eval: function(x, y){return !y;}
-				, code: function(x, y){return "!" + y;}
-				});
+			parent.handleOperator(makeUnaryOperator(
+				  function(x){return !x;}, "!"));
 		}
 	},
-	procCalled: function(type){this.parent().procCalled(type);}
+	handleFactor: function(e){this.parent().handleFactor(e);}
 });
 
 exports.Set = ChainedContext.extend({
@@ -739,16 +786,15 @@ exports.Set = ChainedContext.extend({
 		}
 	},
 	endParse: function(){
-		var gen = this.codeGenerator();
-		if (!this.__expr.length){
-			gen.write(this.__value.toString());
-			this.parent().handleConst(basicTypes.set, this.__value);
-		}
+		var parent = this.parent();
+		if (!this.__expr.length)
+			parent.handleConst(basicTypes.set, this.__value, this.__value.toString());
 		else{
-			this.parent().setType(basicTypes.set);
-			gen.write(this.rtl().makeSet(this.__expr));
+			var code = this.rtl().makeSet(this.__expr);
 			if (this.__value)
-				gen.write(" | " + this.__value);
+				code += " | " + this.__value;
+			var e = new Code.Expression(code, basicTypes.set);
+			parent.handleFactor(e);
 		}
 	}
 });
@@ -763,7 +809,8 @@ exports.SetElement = ChainedContext.extend({
 		this.__expr = new Code.SimpleGenerator();
 	},
 	codeGenerator: function(){return this.__expr;},
-	handleExpression: function(type, value){
+	handleExpression: function(e){
+        var value = e.constValue();
 		if (!this.__from)
 			{
 			this.__from = this.__expr.result();
@@ -789,169 +836,107 @@ function constValueCode(value){
 exports.SimpleExpression = ChainedContext.extend({
 	init: function SimpleExpressionContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
-		//this.__type = undefined;
+		this.__unaryOperator = undefined;
 		this.__binaryOperator = undefined;
-		this.__unaryMinus = false;
-		this.__unaryPlus = false;
-		this.__isConst = true;
-		this.__constValue = undefined;
-		this.__designator = undefined;
-		this.__code = new Code.SimpleGenerator();
+		this.__type = undefined;
+		this.__exp = undefined;
 	},
-	codeGenerator: function(){return this.__code;},
-	handleTerm: function(code, value, designator){
-		if (value !== undefined)
-			this.__handleConst(value);
+	handleTerm: function(e){
+		if (this.__unaryOperator){
+			this.__exp = this.__unaryOperator(e);
+			this.__unaryOperator = undefined;
+		}
 		else
-			this.__isConst = false;
-		this.codeGenerator().write(code);
-		this.__designator = designator;
-		this.__derefDesignator();
+			this.__exp = this.__exp ? this.__binaryOperator(this.__exp, e) : e;
 	},
 	handleLiteral: function(s){
 		if (s == "-")
-			this.__unaryMinus = true;
+			this.__unaryOperator = makeUnaryOperator(
+				function(x, type){return type == basicTypes.set ? ~x : -x;},
+				function(type){return type == basicTypes.set ? "~" : "-";}
+				);
 		else if (s == "+")
-			this.__unaryPlus = true;
-	},
-	type: function(){return this.parent().type();},
-	constValue: function(){return this.__isConst ? this.__constValue : undefined;},
-	handleBinaryOperator: function(o){
-		this.__binaryOperator = o;
-		this.__derefDesignator();
+			this.__unaryOperator = makeUnaryOperator(
+				function(x){return x;}, "");
 	},
-	handleUnaryOperator: function(o){
-		this.__unary_operator = o;
-	},
-	procCalled: function(type){this.parent().procCalled(type);},
-	endParse: function(){
-		var parent = this.parent();
-		var code = parent.codeGenerator();
-		if (this.__unaryMinus)
-			if (this.type() == basicTypes.set){
-				if (this.__isConst)
-					this.__constValue = ~this.__constValue;
-				else
-					code.write('~');
-			}
-			else {
-				if (this.__isConst)
-					this.__constValue = -this.__constValue;
-				else
-					code.write('-');
-			}
-
-		code.write(this.__isConst ? constValueCode(this.__constValue) : this.__code.result());
-		parent.handleSimpleExpression(this.constValue(), this.__designator);
-	},
-	__handleConst: function(value){
-		if (!this.__isConst)
-			return;
-
-		if (this.__unary_operator){
-			value = this.__unary_operator(value);
-			this.__unary_operator = undefined;
-		}
-
-		if (!this.__binaryOperator)
-			this.__constValue = value;
+	type: function(){return this.__type;},
+	setType: function(type){
+		if (type === undefined || this.__type === undefined)
+			this.__type = type;
 		else
-			this.__constValue = this.__binaryOperator(this.__constValue, value);
+            checkImplicitCast(type, this.__type);
 	},
-	__derefDesignator: function(){
-		if (!this.__designator)
-			return;
-		if (!this.__binaryOperator && !this.__unary_operator
-			&& !this.__unaryMinus && !this.__unaryPlus)
-			return;
-
-		writeDerefDesignatorCode(this.__designator, this.__code);
-		this.__designator = undefined;
+	handleBinaryOperator: function(o){this.__binaryOperator = o;},
+	endParse: function(){
+		this.parent().handleSimpleExpression(this.__exp);
 	}
 });
 
 exports.Expression = ChainedContext.extend({
 	init: function ExpressionContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
-		this.__leftParsed = false;
-		this.__type = undefined;
 		this.__relation = undefined;
-		this.__value = undefined;
-		this.__designator = undefined;
-		this.__code = new Code.SimpleGenerator();
+		this.__expression = undefined;
 	},
-	setType: function(type){
-		if (this.__relation == "IS"){
-			if (!(type instanceof Type.Record))
-				throw new Errors.Error("RECORD type expected after 'IS'");
-			
-			checkTypeCast(this.__type, type, "invalid type test");
-		}
-		else if (type === undefined || this.__type === undefined)
-			this.__type = type;
-		else if (!Cast.implicit(type, this.__type))
-			throw new Errors.Error("type mismatch: expected '" + this.__type.name()
-								 + "', got '" + type.name() + "'");
-	},
-	type: function(){return this.__type;},
-	codeGenerator: function(){return this.__code;},
-	handleSimpleExpression: function(value, designator){
-		if (!this.__leftParsed){
-			this.__leftParsed = true;
-			this.__value = value;
-			this.__designator = designator;
-		}
-		else {
-			if (this.__relation == "IS"){
-				if (!designator || !(designator.info() instanceof Type.Type))
-					throw new Errors.Error("type name expected");
-			}
-			this.__type = basicTypes.bool;
-			this.__value = undefined;
-			this.__designator = undefined;
-		}
-	},
-	procCalled: function(type){
-		if (!type)
-			throw new Errors.Error("procedure returning no result cannot be used in an expression");
-		this.__type = type;
-		this.__designator = undefined;
+	handleSimpleExpression: function(e){
+		if (!this.__expression){
+			this.__expression = e;
+            return;
+        }
+
+        var leftType = this.__expression.type();
+        var rightType = e.type();
+        var leftCode = this.__expression.code();
+        var leftExpression = this.__expression;
+        var rightCode = e.code();
+        var code;
+
+        if (this.__relation == "IN"){
+            if (leftType != basicTypes.int)
+                throw new Errors.Error("'INTEGER' expected as an element of SET, got '" + leftType.name() + "'");
+            checkImplicitCast(rightType, basicTypes.set);
+
+            code = "1 << " + leftCode + " & " + rightCode;
+        }
+        else if (this.__relation == "IS"){
+            if (!(leftType instanceof Type.Pointer))
+                throw new Errors.Error("POINTER to type expected before 'IS'");
+            else if (!(rightType instanceof Type.Record))
+                throw new Errors.Error("RECORD type expected after 'IS'");
+            else
+                checkTypeCast(leftType, rightType, "invalid type test");
+
+            var designator = e.designator();
+            if (!designator || !(designator.info() instanceof Type.Type))
+                throw new Errors.Error("type name expected");
+            code = leftCode + " instanceof " + rightCode;
+        }
+        else
+            checkImplicitCast(rightType, leftType);
+
+        if (this.__relation == "=")
+            code = Code.adjustPrecedence(leftExpression, precedence.equal) + " == " + rightCode;
+        else if (this.__relation == "#")
+            code = leftCode + " != " + rightCode;
+        else if (this.__relation == "<=")
+            code = this.rtl().setInclL(leftCode, rightCode);
+        else if (this.__relation == ">=")
+            code = this.rtl().setInclR(leftCode, rightCode);
+
+        this.__expression = new Code.Expression(code, basicTypes.bool);
 	},
 	handleLiteral: function(relation){
-		if (relation == "IS")
-			if (!(this.__type instanceof Type.Pointer))
-				throw new Errors.Error("POINTER to type expected before 'IS'");
-			else
-				this.codeGenerator().write(" instanceof ");
-		else if (relation == "IN"){
-			if (this.__type != basicTypes.int)
-				throw new Errors.Error("'INTEGER' expected as an element of SET, got '" + this.__type.name() + "'");
-			this.__type = basicTypes.set;
-			this.__code = new Code.SimpleGenerator("1 << " + this.__code.result() + " & ");
-		}
-		else if (relation == "=")
-			this.__code = new Code.SimpleGenerator(this.__code.result() + " == ");
-		else if (relation == "#")
-			this.__code = new Code.SimpleGenerator(this.__code.result() + " != ");
-		else if (relation == "<=" || relation == ">=")
-			this.__code.write(", ");
-
 		this.__relation = relation;
 	},
 	endParse: function(){
-		var parent = this.parent();
-		var code = parent.codeGenerator();
-		if (this.__relation == "<=")
-			code.write(this.rtl().setInclL(this.__code.result()));
-		else if (this.__relation == ">=")
-			code.write(this.rtl().setInclR(this.__code.result()));
-		else
-			code.write(this.__code.result());
-		parent.handleExpression(this.__type, this.__value, this.__designator);
+        var parent = this.parent();
+		parent.codeGenerator().write(this.__expression.code());
+		parent.handleExpression(this.__expression);
 	}
 });
 
-function handleIfExpression(type){
+function handleIfExpression(e){
+    var type = e.type();
 	if (type !== basicTypes.bool)
 		throw new Errors.Error("'BOOLEAN' expression expected, got '" + type.name() + "'");
 }
@@ -1008,7 +993,8 @@ exports.Case = ChainedContext.extend({
 		this.genVarName("$c");
 		this.codeGenerator().write("$c = ");
 	},
-	handleExpression: function(type){
+	handleExpression: function(e){
+        var type = e.type();
 		var gen = this.codeGenerator();
 		if (type instanceof Type.String){
 			var v = type.asChar();
@@ -1171,7 +1157,9 @@ exports.For = ChainedContext.extend({
 		this.codeGenerator().write("for (" + id + " = ");
 		this.__var = id;
 	},
-	handleExpression: function(type, value){
+	handleExpression: function(e){
+        var type = e.type();
+        var value = e.constValue();
 		if (type !== basicTypes.int)
 			throw new Errors.Error(
 				!this.__initExprParsed
@@ -1240,7 +1228,8 @@ exports.Assignment = ChainedContext.extend({
         this.__code = new Code.SimpleGenerator();
 		this.__type = d.type();
 	},
-	handleExpression: function(type, value, designator){
+	handleExpression: function(e){
+        var type = e.type();
 		var isArray = this.__type instanceof Type.Array;
 		if (isArray
 			&& this.__type.elementsType() == basicTypes.char
@@ -1280,10 +1269,7 @@ exports.Assignment = ChainedContext.extend({
             return;
         }
 
-		if (designator)
-			writeDerefDesignatorCode(designator, this.__code);
-
-        var castCode = castOperation.code();
+        var castCode = castOperation(this, e.deref()).code();
         this.__rightCode = castCode ? castCode : this.__code.result();
 	},
 	endParse: function(){
@@ -1309,10 +1295,11 @@ exports.ConstDecl = ChainedContext.extend({
 		this.__id = id;
 		this.codeGenerator().write("var " + id + " = ");
 	},
-	handleExpression: function(type, value){
+	handleExpression: function(e){
+        var value = e.constValue();
 		if (value === undefined)
 			throw new Errors.Error("constant expression expected");
-		this.__type = type;
+		this.__type = e.type();
 		this.__value = value;
 	},
 	endParse: function(){
@@ -1378,37 +1365,41 @@ exports.ActualParameters = ChainedContext.extend({
 	},
 });
 
-exports.ProcedureCall = ChainedContext.extend({
+var ProcedureCall = ChainedContext.extend({
 	init: function ProcedureCallContext(context){
 		ChainedContext.prototype.init.bind(this)(context);
 		this.__type = undefined;
 		this.__procCall = undefined;
-		this.__code = undefined;
+		this.__code = new Code.SimpleGenerator();
 	},
 	setDesignator: function(d){
 		var type = d.type();
 		assertProcType(type);
 		this.__type = type;
-		this.__code = new Code.SimpleGenerator();
 		this.__procCall = type.callGenerator(this, d.code());
+		this.__callExpression = undefined;
 	},
-	codeGenerator: function(){
-		return this.__code ? this.__code : this.parent().codeGenerator();
-	},
+	codeGenerator: function(){return this.__code;},
 	type: function(){return this.__type;},
-	setType: function(){},
 	hasActualParameters: function(){},
-	handleExpression: function(type, value, designator){
-		var code = this.__code.result();
-		this.__code = new Code.SimpleGenerator();
-		this.__procCall.handleArgument(type, designator, code);
+	handleExpression: function(e){this.__procCall.handleArgument(e);},
+	callExpression: function(){return this.__callExpression;},
+	endParse: function(){this.__callExpression = this.__procCall.end(); /*console.log(this.__callExpression.maxPrecedence())*/;}
+});
+
+exports.StatementProcedureCall = ProcedureCall.extend({
+	init: function StatementProcedureCallContext(context){
+		ProcedureCall.prototype.init.bind(this)(context);
 	},
-	endParse: function(){this.parent().codeGenerator().write(this.__procCall.end());}
+	endParse: function(){
+		ProcedureCall.prototype.endParse.call(this);
+		this.parent().codeGenerator().write(this.callExpression().code());
+	}
 });
 
-exports.ExpressionProcedureCall = exports.ProcedureCall.extend({
+exports.ExpressionProcedureCall = ProcedureCall.extend({
 	init: function ExpressionProcedureCallContext(context){
-		exports.ProcedureCall.prototype.init.bind(this)(context);
+		ProcedureCall.prototype.init.bind(this)(context);
 		this.__designator = undefined;
 		this.__hasActualParameters = false;
 	},
@@ -1416,13 +1407,13 @@ exports.ExpressionProcedureCall = exports.ProcedureCall.extend({
 		this.__designator = d;
 	},
 	hasActualParameters: function(){
-		exports.ProcedureCall.prototype.setDesignator.bind(this)(this.__designator);
+		ProcedureCall.prototype.setDesignator.bind(this)(this.__designator);
 		this.__hasActualParameters = true;
 	},
 	endParse: function(){
 		if (this.__hasActualParameters){
-			exports.ProcedureCall.prototype.endParse.bind(this)();
-			this.parent().procCalled(this.__type.result());
+			ProcedureCall.prototype.endParse.call(this);
+			this.parent().handleFactor(this.callExpression());
 		}
 		else
 			this.parent().setDesignator(this.__designator);

+ 1 - 1
src/grammar.js

@@ -76,7 +76,7 @@ var set = and("{", context(optional(and(element, repeat(and(",", element)))), Co
 var expList = and(expression, repeat(and(",", expression)));
 var actualParameters = and("(", context(optional(expList), Context.ActualParameters), ")");
 var procedureCall = context(and(designator, optional(actualParameters))
-						  , Context.ProcedureCall);
+						  , Context.StatementProcedureCall);
 
 var assignment = context(and(designator, ":=", required(expression, "expression expected"))
 					   , Context.Assignment);

+ 8 - 0
src/operator.js

@@ -0,0 +1,8 @@
+var precedence = {
+    shift: 7,
+    equal: 9,
+    conditional: 15,
+    assignment: 17
+};
+
+exports.precedence = precedence;

+ 62 - 51
src/procedure.js

@@ -3,6 +3,7 @@ var Cast = require("cast.js");
 var Class = require("rtl.js").Class;
 var Code = require("code.js");
 var Errors = require("errors.js");
+var precedence = require("operator.js").precedence;
 var Type = require("type.js");
 
 var Arg = Class.extend({
@@ -35,7 +36,7 @@ var ProcCallGenerator = Class.extend({
 	id: function(){return this.__id;},
 	context: function(){return this.__context;},
 	codeGenerator: function(){return this.__code;},
-	handleArgument: function(type, designator, code){
+	handleArgument: function(e){
 		var pos = this.__argumentsCount++;
 		var isVarArg = false;
 		var convert;
@@ -45,33 +46,25 @@ var ProcCallGenerator = Class.extend({
 				// ignore, handle error after parsing all arguments
 				return;
 			
-			var arg = this.checkArgument(pos, type, designator);
+			var arg = this.checkArgument(pos, e);
 			isVarArg = arg.isVar;
 			convert = arg.convert;
 		}
-		if (designator){
-			var info = designator.info();
-			if (info instanceof Type.Variable)
-				if (info.isVar() && !isVarArg 
-					&& !(type instanceof Type.Array)
-					&& !(type instanceof Type.Record)
-					)
-					code += ".get()";
-				else if (!info.isVar() && isVarArg)
-					code = designator.refCode();
-			}
-		
-		if (convert)
-			code = convert.code(this.__context, code);
-		
+
+		e = isVarArg ? e.ref() : e.deref();
+        var code = (convert ? convert(this.__context, e) : e).code();
 		var prefix = pos ? ", " : "";
 		this.writeCode(prefix + code);
 	},
 	end: function(){
 		if (this.__type)
 			this.checkArgumentsCount(this.__argumentsCount);
+        return this.callExpression();
+    },
+    callExpression: function(){
 		this.writeCode(this.epilog());
-		return this.codeGenerator().result();
+		return new Code.Expression(this.codeGenerator().result(),
+                                   this.__type ? this.__type.result() : undefined);
 	},
 	prolog: function(){return this.__id + "(";},
 	checkArgumentsCount: function(count){
@@ -80,17 +73,19 @@ var ProcCallGenerator = Class.extend({
 			throw new Errors.Error(procArgs.length + " argument(s) expected, got "
 								 + this.__argumentsCount);
 	},
-	checkArgument: function(pos, type, designator){
+	checkArgument: function(pos, e){
 		var arg = this.__type.arguments()[pos];
 		var castOperation;
 		var expectType = arg.type; // can be undefined for predefined functions (like NEW), dont check it in this case
 		if (expectType){
+            var type = e.type();
             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() + "'");
+                                     + "' cannot be converted to '" + expectType.description() + "'");
         }
 		if (arg.isVar){
+            var designator = e.designator();
 			if (!designator)
 				throw new Errors.Error("expression cannot be used as VAR parameter");
 			var info = designator.info();
@@ -151,23 +146,21 @@ var ProcType = Type.Basic.extend({
 var TwoArgToOperatorProcCallGenerator = ProcCallGenerator.extend({
 	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;
+		this.__firstArgument = undefined;
+		this.__secondArgument = undefined;
 	},
-	codeGenerator: function(){return Code.nullGenerator;},
 	prolog: function(id){return "";},
-	handleArgument: function(type, designator, code){
-		if (!this.__firstArgumentCode)
-			this.__firstArgumentCode = code;
+	handleArgument: function(e){
+		if (!this.__firstArgument)
+			this.__firstArgument = e;
 		else
-			this.__secondArgumentCode = code;
-		ProcCallGenerator.prototype.handleArgument.call(this, type, designator, code);
+			this.__secondArgument = e;
+		ProcCallGenerator.prototype.handleArgument.call(this, e);
 	},
 	epilog: function(type){return "";},
 	end: function(){
-		return this.__operator(this.__firstArgumentCode, this.__secondArgumentCode);
+		return this.__operator(this.__firstArgument, this.__secondArgument);
 	}
 });
 
@@ -179,8 +172,10 @@ exports.predefined = [
 				this.__baseType = undefined;
 			},
 			prolog: function(id){return "";},
-			checkArgument: function(pos, type, designator){
-				ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
+			checkArgument: function(pos, e){
+				ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+
+                var type = e.type();
 				if (!(type instanceof Type.Pointer))
 					throw new Errors.Error("POINTER variable expected, got '"
 										 + type.name() + "'");
@@ -208,12 +203,13 @@ exports.predefined = [
 				ProcCallGenerator.prototype.init.call(this, context, id, type);
 			},
 			prolog: function(id){return "";},
-			checkArgument: function(pos, type, designator){
+			checkArgument: function(pos, e){
+                var type = e.type();
 				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);
+                ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
 			},
 			epilog: function(){return ".length";}
 		});
@@ -277,7 +273,13 @@ exports.predefined = [
 	function(){
 		var args = [new Arg(Type.basic.set, true),
 					new Arg(Type.basic.int, false)];
-		function operator(x, y){return x + " |= 1 << " + y;}
+		function operator(x, y){
+            var code = Code.adjustPrecedence(x, precedence.assignment) +
+                       " |= 1 << " +
+                       Code.adjustPrecedence(y, precedence.shift);
+            return new Code.Expression(
+                code, Type.basic.set, undefined, undefined, precedence.assignment);
+        }
 		var proc = new ProcType(
 			"predefined procedure INCL",
 			args,
@@ -293,7 +295,14 @@ exports.predefined = [
 	function(){
 		var args = [new Arg(Type.basic.set, true),
 					new Arg(Type.basic.int, false)];
-		function operator(x, y){return x + " &= ~(1 << " + y + ")";}
+		function operator(x, y){
+            var code = Code.adjustPrecedence(x, precedence.assignment) +
+                       " &= ~(1 << " +
+                       Code.adjustPrecedence(y, precedence.shift) +
+                       ")";
+            return new Code.Expression(
+                code, Type.basic.set, undefined, undefined, precedence.assignment);
+        }
 		var proc = new ProcType(
 			"predefined procedure EXCL",
 			args,
@@ -310,29 +319,31 @@ exports.predefined = [
         var CallGenerator = ProcCallGenerator.extend({
             init: function OrdProcCallGenerator(context, id, type){
                 ProcCallGenerator.prototype.init.call(this, context, id, type);
+                this.__callExpression = undefined;
             },
             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)";}));
+            checkArgument: function(pos, e){
+                var type = e.type();
+                if (type == Type.basic.char || type == Type.basic.set)
+                    this.__callExpression = new Code.Expression(e.code(), Type.basic.int);
+                else if (type == Type.basic.bool){
+                    var code = Code.adjustPrecedence(e, precedence.conditional) + " ? 1 : 0";
+                    this.__callExpression = new Code.Expression(code, Type.basic.int, undefined, undefined, precedence.conditional);
+                }
                 else if (type instanceof Type.String){
                     var ch = type.asChar();
                     if (ch !== undefined)
-                        return new CheckArgumentResult(
-                            type,
-                            false,
-                            new Cast.Operation(function(){return ch;}));
+                        this.__callExpression = new Code.Expression("" + ch, Type.basic.int);
                 }
+                
+                if (this.__callExpression)
+                    return new CheckArgumentResult(type, false);
 
-            // should throw error
-            ProcCallGenerator.prototype.checkArgument.call(this, pos, type, designator);
-            }
+                // should throw error
+                ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+            },
+            callExpression: function(){return this.__callExpression;}
         });
         var name = "ORD";
         var argType = new Type.Basic("CHAR or BOOLEAN or SET");

+ 1 - 1
test/expected/case.js

@@ -32,7 +32,7 @@ if ($c === 1 || $c === 2 || $c === 3){
 	i = 4;
 }
 else if ($c === 12){
-	i = 12;
+	i = constI;
 }
 else if (($c >= 4 && $c <= 5)){
 	i = 5;

+ 2 - 2
test/expected/const.js

@@ -1,7 +1,7 @@
 var m = function (){
 var i1 = 1;
 var b1 = true;
-var i2 = 3;
+var i2 = i1 + 2;
 var i3 = 31;
 var i4 = 10;
 var i5 = 3;
@@ -13,5 +13,5 @@ var r4 = 12345600000;
 var lr1 = 23456700000000;
 var lr2 = 23.4567;
 var lr3 = 0.0000234567;
-var b2 = true;
+var b2 = b1;
 }();

+ 1 - 1
test/expected/len.js

@@ -39,6 +39,6 @@ function p2(a/*VAR ARRAY OF BOOLEAN*/){
 p1(a1);
 p2(a2);
 RTL$.assert(a3.length == 20);
-RTL$.assert("\"".length == 1);
+RTL$.assert(s1.length == 1);
 RTL$.assert("abc".length == 3);
 }();

+ 2 - 2
test/expected/ord.js

@@ -12,6 +12,6 @@ ch = 97;
 RTL$.assert(ch == 97);
 set = 2;
 RTL$.assert(2 == set);
-RTL$.assert(((true) ? 1 : 0) == 1);
-RTL$.assert(((false) ? 1 : 0) == 0);
+RTL$.assert((true ? 1 : 0) == 1);
+RTL$.assert((false ? 1 : 0) == 0);
 }();

+ 8 - 6
test/expected/set.js

@@ -32,11 +32,11 @@ var RTL$ = {
 };
 var m = function (){
 var ci = 3;
-var cs1 = 6;
-var cs2 = 12;
-var cs3 = 2;
-var cs4 = 28;
-var cs5 = -3;
+var cs1 = 2 | 4;
+var cs2 = 14 & ~18;
+var cs3 = 14 & 18;
+var cs4 = 14 ^ 18;
+var cs5 = ~2;
 var s1 = 0;var s2 = 0;
 var i1 = 0;
 var b = false;
@@ -58,7 +58,9 @@ s1 = s1 & s2;
 s1 = s1 ^ s2;
 s1 = ~s2;
 s2 |= 1 << 3;
-s1 |= 1 << 9;
+s1 |= 1 << ci * 2 + 3;
 s1 |= 1 << ci * 2 - i1 + 3;
+s1 |= 1 << (b ? 1 : 0);
 s2 &= ~(1 << 3);
+s2 &= ~(1 << (b ? 1 : 0));
 }();

+ 4 - 4
test/expected/string.js

@@ -44,9 +44,9 @@ 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"));
+RTL$.assignArrayFromString(a2, s1);
+RTL$.assignArrayFromString(a2, s2);
+p1(RTL$.strToArray(s1));
+p1(RTL$.strToArray(s2));
 p2(34);
 }();

+ 3 - 1
test/input/set.ob

@@ -38,6 +38,8 @@ BEGIN
 	INCL(s2, 3);
 	INCL(s1, ci * 2 + 3);
 	INCL(s1, ci * 2 - i1 + 3);
+	INCL(s1, ORD(b));
 
-	EXCL(s2, 3)
+	EXCL(s2, 3);
+	EXCL(s2, ORD(b))
 END m.