Parcourir la source

procedure.js -> Procedure.ob + bug fixes

Vladislav Folts il y a 11 ans
Parent
commit
a47ab7c5ee

+ 17 - 12
src/context.js

@@ -6,7 +6,7 @@ var Errors = require("js/Errors.js");
 var Module = require("module.js");
 var op = require("js/Operator.js");
 var Parser = require("parser.js");
-var Procedure = require("procedure.js");
+var Procedure = require("js/Procedure.js");
 var Class = require("rtl.js").Class;
 var Scope = require("scope.js");
 var Symbol = require("js/Symbols.js");
@@ -474,7 +474,7 @@ exports.FormalParameters = ChainedContext.extend({
         this.__result = undefined;
 
         var parent = this.parent();
-        this.__type = new Procedure.Type(parent.typeName());
+        this.__type = new Procedure.make(parent.typeName());
         parent.setType(this.__type);
     },
     handleMessage: function(msg){
@@ -728,7 +728,7 @@ exports.ArrayDimensions = ChainedContext.extend({
         if (type !== basicTypes.integer)
             throw new Errors.Error("'INTEGER' constant expression expected, got '" + type.description() + "'");
         var value = e.constValue();
-        if (value === undefined)
+        if (!value)
             throw new Errors.Error("constant expression expected as ARRAY size");
         if (value.value <= 0)
             throw new Errors.Error("array size must be greater than 0, got " + value.value);
@@ -984,9 +984,9 @@ exports.Factor = ChainedContext.extend({
         if (s == "NIL")
             parent.handleConst(nilType, undefined, "null");
         else if (s == "TRUE")
-            parent.handleConst(basicTypes.bool, Code.makeIntConst(true), "true");
+            parent.handleConst(basicTypes.bool, Code.makeIntConst(1), "true");
         else if (s == "FALSE")
-            parent.handleConst(basicTypes.bool, Code.makeIntConst(false), "false");
+            parent.handleConst(basicTypes.bool, Code.makeIntConst(0), "false");
         else if (s == "~")
             parent.handleLogicalNot();
     },
@@ -1573,9 +1573,16 @@ exports.FieldListDeclaration = HandleSymbolAsType.extend({
     }
 });
 
-function assertProcType(type){
-    if (!(type instanceof Type.Procedure) && !(type instanceof Module.AnyType))
-        throw new Errors.Error("PROCEDURE expected, got '" + type.description() + "'");
+function assertProcType(d){
+    var type = d.type();
+    var unexpected;
+    if ( !type )
+        unexpected = d.info().idType();
+    else if (!(type instanceof Type.Procedure) && !(type instanceof Module.AnyType))
+        unexpected = type.description();
+    if (unexpected)
+        throw new Errors.Error("PROCEDURE expected, got '" + unexpected + "'");
+    return type;
 }
 
 exports.ActualParameters = ChainedContext.extend({
@@ -1593,10 +1600,8 @@ var ProcedureCall = ChainedContext.extend({
         this.__code = Code.makeSimpleGenerator();
     },
     setDesignator: function(d){
-        var type = d.type();
-        assertProcType(type);
-        this.__type = type;
-        this.__procCall = type.callGenerator(this, d.code());
+        this.__type = assertProcType(d);
+        this.__procCall = this.__type.callGenerator(this, d.code());
         this.__callExpression = undefined;
     },
     codeGenerator: function(){return this.__code;},

+ 48 - 33
src/eberon/eberon_context.js

@@ -1,30 +1,21 @@
 "use strict";
 
 var Cast = require("js/Cast.js");
+var Code = require("js/Code.js");
 var Context = require("context.js");
 var Errors = require("js/Errors.js");
 var Symbol = require("js/Symbols.js");
-var Procedure = require("procedure.js");
+var Procedure = require("js/Procedure.js");
 var Type = require("js/Types.js");
 
 function methodCallGenerator(context, id, type){
-    return new Procedure.CallGenerator(context, id, type);
+    return new Procedure.makeProcCallGenerator(context, id, type);
 }
 
-var SuperCallGenerator = Procedure.CallGenerator.extend({
-    init: function(context, id, type){
-        Procedure.CallGenerator.prototype.init.call(this, context, id, type);
-    },
-    prolog: function(){
-        return Procedure.CallGenerator.prototype.prolog.call(this) + "this";
-    },
-    writeArgumentCode: function(e, pos, isVar, convert){
-        Procedure.CallGenerator.prototype.writeArgumentCode.call(this, e, pos + 1, isVar, convert);
-    }
-});
-
 function superMethodCallGenerator(context, id, type){
-    return new SuperCallGenerator(context, id, type);
+    var args = Procedure.makeArgumentsCode(context);
+    args.write(Code.makeExpression("this"));
+    return Procedure.makeProcCallGeneratorWithCustomArgs(context, id, type, args);
 }
 
 var MethodType = Type.Procedure.extend({
@@ -115,7 +106,7 @@ var RecordType = Type.Record.extend({
         this.__definedMethods = [];
         this.__abstractMethods = [];
         this.__instantiated = false;
-        this.__lazyDefinitions = [];
+        this.__lazyDefinitions = {};
         this.__nonExportedMethods = [];
     },
     initializer: function(context){
@@ -172,14 +163,21 @@ var RecordType = Type.Record.extend({
         
         this.__definedMethods.push(id);
     },
-    requireMethodDefinition: function(id){
+    requireMethodDefinition: function(id, reason){
         if (!this.__hasMethodDeclaration(id))
             throw new Errors.Error(
                 "there is no method '" + id + "' in base type(s)");
         if (this.__finalized)
-            this.__ensureMethodDefinitions([id]);
-        else if (this.__lazyDefinitions.indexOf(id) == -1)
-            this.__lazyDefinitions.push(id);
+            this.__ensureMethodDefinitions({reason: [id]});
+        else {
+            var ids = this.__lazyDefinitions[reason];
+            if (!ids){
+                ids = [id];
+                this.__lazyDefinitions[reason] = ids;
+            }
+            else if (ids.indexOf(id) == -1)
+                ids.push(id);
+            }
     },
     abstractMethods: function(){return this.__abstractMethods;},
     __collectAbstractMethods: function(){
@@ -206,26 +204,43 @@ var RecordType = Type.Record.extend({
 
         Type.Record.prototype.finalize.call(this);
     },
-    __ensureMethodDefinitions: function(ids){
+    __ensureMethodDefinitions: function(reasons){
         var result = [];
-        for(var i = 0; i < ids.length; ++i){
-            var m = ids[i];
-            if (!this.__hasMethodDefinition(m))
-                result.push(m);
+        for(var reason in reasons){
+            var ids = reasons[reason];
+            var report = [];
+            for(var i = 0; i < ids.length; ++i){
+                var m = ids[i];
+                if (!this.__hasMethodDefinition(m))
+                    report.push(m);
+            }
+            if (report.length)
+                result.push(reason + ": " + report.join(", "));
         }
         if (result.length)
-            throw new Errors.Error(
-                  "cannot use abstract method(s) in SUPER calls: "
-                + result.join(", "));
+            throw new Errors.Error(result.join("; "));
     },
     __ensureNonAbstract: function(){
+        function errMsg(self){
+            return "cannot instantiate '" 
+                 + Type.typeName(self) 
+                 + "' because it has abstract method(s)";
+        }
+
         var am = this.abstractMethods();
         if (am.length)
-            throw new Errors.Error(
-                  "cannot instantiate '" + Type.typeName(this) 
-                + "' because it has abstract method(s): "
-                + am.join(", ")
+            throw new Errors.Error(errMsg(this) + ": " + am.join(", ")
                 );
+
+        var baseType = Type.recordBase(this);
+        while (baseType){
+            if (!baseType.__finalized)
+                for(var id in baseType.__declaredMethods){
+                    if (!this.__hasMethodDefinition(id))
+                        baseType.requireMethodDefinition(id, errMsg(this));
+                }
+            baseType = Type.recordBase(baseType);
+        }
     },
     __hasMethodDeclaration: function(id){
         var type = this;
@@ -349,7 +364,7 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
                 + "' has no base type - SUPER cannot be used");
 
         var id = this.__methodId.id();
-        baseType.requireMethodDefinition(id);
+        baseType.requireMethodDefinition(id, "cannot use abstract method(s) in SUPER calls");
         return {
             symbol: Symbol.makeSymbol(
                 "method",  

+ 8 - 7
src/js/Cast.js

@@ -5,8 +5,9 @@ var JsArray = require("js/JsArray.js");
 var JsString = require("js/JsString.js");
 var Object = require("js/Object.js");
 var Types = require("js/Types.js");
-var CastOp = RTL$.extend({
+var CastOp = Object.Type.extend({
 	init: function CastOp(){
+		Object.Type.prototype.init.call(this);
 	}
 });
 var CastOpDoNothing = CastOp.extend({
@@ -56,7 +57,7 @@ function areTypesMatch(t1/*PType*/, t2/*PType*/){
 	return areTypesExactlyMatch(t1, t2) || Types.isInt(t1) && Types.isInt(t2) || (t1 == Types.nil() && matchesToNIL(t2) || t2 == Types.nil() && matchesToNIL(t1));
 }
 
-function areArgsMatch(oa1/*PType*/, oa2/*PType*/, p1/*PProcedure*/, p2/*PProcedure*/){
+function areArgsMatch(oa1/*PType*/, oa2/*PType*/, p1/*PDefinedProcedure*/, p2/*PDefinedProcedure*/){
 	var a1 = null;
 	var a2 = null;
 	a1 = RTL$.typeGuard(oa1, Types.ProcedureArgument);
@@ -64,7 +65,7 @@ function areArgsMatch(oa1/*PType*/, oa2/*PType*/, p1/*PProcedure*/, p2/*PProcedu
 	return a1.isVar == a2.isVar && (a1.type == p1 && a2.type == p2 || areTypesExactlyMatch(a1.type, a2.type));
 }
 
-function areProceduresMatch(p1/*PProcedure*/, p2/*PProcedure*/){
+function areProceduresMatch(p1/*PDefinedProcedure*/, p2/*PDefinedProcedure*/){
 	var result = false;
 	var args1 = null;var args2 = null;
 	var argsLen = 0;
@@ -99,8 +100,8 @@ function areTypesExactlyMatchImpl(t1/*PType*/, t2/*PType*/){
 	else if (t1 instanceof Types.Pointer && t2 instanceof Types.Pointer){
 		result = areTypesMatch(Types.pointerBase(RTL$.typeGuard(t1, Types.Pointer)), Types.pointerBase(RTL$.typeGuard(t2, Types.Pointer)));
 	}
-	else if (t1 instanceof Types.Procedure && t2 instanceof Types.Procedure){
-		result = areProceduresMatch(RTL$.typeGuard(t1, Types.Procedure), RTL$.typeGuard(t2, Types.Procedure));
+	else if (t1 instanceof Types.DefinedProcedure && t2 instanceof Types.DefinedProcedure){
+		result = areProceduresMatch(RTL$.typeGuard(t1, Types.DefinedProcedure), RTL$.typeGuard(t2, Types.DefinedProcedure));
 	}
 	return result;
 }
@@ -155,8 +156,8 @@ function implicit(from/*PType*/, to/*PType*/, ops/*Operations*/){
 	else if (from == Types.nil() && matchesToNIL(to)){
 		result = doNothing;
 	}
-	else if (from instanceof Types.Procedure && to instanceof Types.Procedure){
-		if (areProceduresMatch(RTL$.typeGuard(from, Types.Procedure), RTL$.typeGuard(to, Types.Procedure))){
+	else if (from instanceof Types.DefinedProcedure && to instanceof Types.DefinedProcedure){
+		if (areProceduresMatch(RTL$.typeGuard(from, Types.DefinedProcedure), RTL$.typeGuard(to, Types.DefinedProcedure))){
 			result = doNothing;
 		}
 	}

+ 3 - 1
src/js/Code.js

@@ -67,8 +67,9 @@ var StringConst = Const.extend({
 		this.value = null;
 	}
 });
-var Expression = RTL$.extend({
+var Expression = Object.Type.extend({
 	init: function Expression(){
+		Object.Type.prototype.init.call(this);
 		this.mCode = null;
 		this.mType = null;
 		this.mDesignator = null;
@@ -204,6 +205,7 @@ function makeStringConst(s/*Type*/){
 
 function makeExpressionWithPrecedence(code/*Type*/, type/*PType*/, designator/*PDesignator*/, constValue/*PConst*/, maxPrecedence/*INTEGER*/){
 	var result = null;
+	RTL$.assert(code != null);
 	result = new Expression();
 	result.mCode = code;
 	result.mType = type;

+ 1 - 0
src/js/Context.js

@@ -12,6 +12,7 @@ var Rtl = RTL$.extend({
 		this.assignArrayFromString = null;
 		this.setInclL = null;
 		this.setInclR = null;
+		this.assertId = null;
 	}
 });
 var Type = RTL$.extend({

+ 7 - 0
src/js/JsArray.js

@@ -43,6 +43,12 @@ function stringsAt(a/*Strings*/, i/*INTEGER*/){
 	return result;
 }
 
+function contains(a/*Type*/, x/*PType*/){
+	var result = false;
+	result = (a.indexOf(x) != -1);
+	return result;
+}
+
 function make(){
 	var result = null;
 	result = [];
@@ -62,5 +68,6 @@ exports.add = add;
 exports.stringsAdd = stringsAdd;
 exports.at = at;
 exports.stringsAt = stringsAt;
+exports.contains = contains;
 exports.make = make;
 exports.makeStrings = makeStrings;

+ 773 - 0
src/js/Procedure.js

@@ -0,0 +1,773 @@
+var RTL$ = require("rtl.js");
+var Cast = require("js/Cast.js");
+var Code = require("js/Code.js");
+var Context = require("js/Context.js");
+var Errors = require("js/Errors.js");
+var JsArray = require("js/JsArray.js");
+var JsString = require("js/JsString.js");
+var Object = require("js/Object.js");
+var Operator = require("js/Operator.js");
+var Precedence = require("js/CodePrecedence.js");
+var Symbols = require("js/Symbols.js");
+var Types = require("js/Types.js");
+var Call = RTL$.extend({
+	init: function Call(){
+	}
+});
+var StdCall = Call.extend({
+	init: function StdCall(){
+		Call.prototype.init.call(this);
+		this.args = null;
+	}
+});
+var CallGenerator = RTL$.extend({
+	init: function CallGenerator(){
+		this.args = null;
+		this.cx = null;
+		this.call = null;
+	}
+});
+var Impl = Types.Procedure.extend({
+	init: function Impl(){
+		Types.Procedure.prototype.init.call(this);
+	}
+});
+var Type = Types.DefinedProcedure.extend({
+	init: function Type(){
+		Types.DefinedProcedure.prototype.init.call(this);
+		this.mArgs = null;
+		this.mResult = null;
+	}
+});
+var Std = Impl.extend({
+	init: function Std(){
+		Impl.prototype.init.call(this);
+		this.call = null;
+	}
+});
+var ArgumentsCode = RTL$.extend({
+	init: function ArgumentsCode(){
+	}
+});
+var GenArgCode = ArgumentsCode.extend({
+	init: function GenArgCode(){
+		ArgumentsCode.prototype.init.call(this);
+		this.code = null;
+		this.cx = null;
+	}
+});
+var predefined = null;
+
+function checkArgument(actual/*PExpression*/, expected/*PProcedureArgument*/, pos/*INTEGER*/, code/*PArgumentsCode*/){
+	var actualType = null;var expectType = null;
+	var designator = null;
+	var info = null;
+	var result = null;
+	expectType = expected.type;
+	if (expectType != null){
+		actualType = actual.type();
+		result = Cast.implicit(actualType, expectType, Operator.castOperations());
+		if (result == null){
+			Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.make("type mismatch for argument "), JsString.fromInt(pos + 1 | 0)), JsString.make(": '")), actualType.description()), JsString.make("' cannot be converted to '")), expectType.description()), JsString.make("'")));
+		}
+		if (expected.isVar && expectType != actualType && Types.isInt(actualType)){
+			Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.make("type mismatch for argument "), JsString.fromInt(pos + 1 | 0)), JsString.make(": cannot pass '")), actualType.description()), JsString.make("' as VAR parameter of type '")), expectType.description()), JsString.make("'")));
+		}
+	}
+	if (expected.isVar){
+		designator = actual.designator();
+		if (designator == null){
+			Errors.raise(JsString.make("expression cannot be used as VAR parameter"));
+		}
+		info = designator.info();
+		if (info instanceof Types.Const){
+			Errors.raise(JsString.make("constant cannot be used as VAR parameter"));
+		}
+		if (info instanceof Types.Variable && Types.isVariableReadOnly(RTL$.typeGuard(info, Types.Variable))){
+			Errors.raise(JsString.concat(info.idType(), JsString.make(" cannot be used as VAR parameter")));
+		}
+	}
+	if (code != null){
+		code.write(actual, expected, result);
+	}
+}
+
+function checkArgumentsType(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/){
+	var actualLen = 0;
+	var i = 0;
+	var actualExp = null;
+	var expectedArg = null;
+	actualLen = JsArray.len(actual);
+	while (true){
+		if (i < actualLen){
+			actualExp = JsArray.at(actual, i);
+			expectedArg = JsArray.at(expected, i);
+			checkArgument(RTL$.typeGuard(actualExp, Code.Expression), RTL$.typeGuard(expectedArg, Types.ProcedureArgument), i, code);
+			++i;
+		} else break;
+	}
+}
+
+function checkArgumentsCount(actual/*INTEGER*/, expected/*INTEGER*/){
+	if (actual != expected){
+		Errors.raise(JsString.concat(JsString.concat(JsString.fromInt(expected), JsString.make(" argument(s) expected, got ")), JsString.fromInt(actual)));
+	}
+}
+
+function processArguments(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/){
+	checkArgumentsCount(JsArray.len(actual), JsArray.len(expected));
+	checkArgumentsType(actual, expected, code);
+}
+
+function checkArguments(actual/*Type*/, expected/*Type*/){
+	processArguments(actual, expected, null);
+}
+
+function makeStd(name/*Type*/, call/*PCall*/){
+	var result = null;
+	result = new Std();
+	Types.initProcedure(result, name);
+	result.call = call;
+	return result;
+}
+CallGenerator.prototype.handleArgument = function(e/*PExpression*/){
+	JsArray.add(this.args, e);
+}
+CallGenerator.prototype.end = function(){
+	return this.call.make(this.args, this.cx);
+}
+
+function makeCallGenerator(call/*PCall*/, cx/*PType*/){
+	var result = null;
+	RTL$.assert(cx != null);
+	result = new CallGenerator();
+	result.args = JsArray.make();
+	result.cx = cx;
+	result.call = call;
+	return result;
+}
+GenArgCode.prototype.write = function(actual/*PExpression*/, expected/*PProcedureArgument*/, cast/*PCastOp*/){
+	var e = null;
+	if (expected != null && expected.isVar){
+		actual = Code.refExpression(actual);
+	}
+	else {
+		actual = Code.derefExpression(actual);
+	}
+	if (JsString.len(this.code) != 0){
+		this.code = JsString.concat(this.code, JsString.make(", "));
+	}
+	if (cast != null){
+		e = cast.make(this.cx, actual);
+	}
+	else {
+		e = actual;
+	}
+	this.code = JsString.concat(this.code, e.code());
+}
+GenArgCode.prototype.result = function(){
+	return this.code;
+}
+
+function makeProcCallGeneratorWithCustomArgs(cx/*PType*/, id/*Type*/, type/*DefinedProcedure*/, argumentsCode/*PArgumentsCode*/){
+	var CallImpl = Call.extend({
+		init: function CallImpl(){
+			Call.prototype.init.call(this);
+			this.id = null;
+			this.args = null;
+			this.result = null;
+			this.argumentsCode = null;
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var expectedArgs = null;
+		var a = null;
+		var i = 0;
+		expectedArgs = this.args;
+		if (expectedArgs != null){
+			processArguments(args, expectedArgs, this.argumentsCode);
+		}
+		else {
+			for (i = 0; i <= JsArray.len(args) - 1 | 0; ++i){
+				a = JsArray.at(args, i);
+				this.argumentsCode.write(RTL$.typeGuard(a, Code.Expression), null, null);
+			}
+		}
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.concat(this.id, JsString.make("(")), this.argumentsCode.result()), JsString.make(")")), this.result);
+	}
+	call = new CallImpl();
+	call.id = id;
+	call.args = type.args();
+	call.result = type.result();
+	call.argumentsCode = argumentsCode;
+	return makeCallGenerator(call, cx);
+}
+
+function makeArgumentsCode(cx/*PType*/){
+	var result = null;
+	result = new GenArgCode();
+	result.code = JsString.makeEmpty();
+	result.cx = cx;
+	return result;
+}
+
+function makeProcCallGenerator(cx/*PType*/, id/*Type*/, type/*DefinedProcedure*/){
+	return makeProcCallGeneratorWithCustomArgs(cx, id, type, makeArgumentsCode(cx));
+}
+Std.prototype.description = function(){
+	return JsString.concat(JsString.make("standard procedure "), Types.typeName(this));
+}
+Std.prototype.callGenerator = function(cx/*PType*/){
+	return makeCallGenerator(this.call, cx);
+}
+
+function makeSymbol(p/*PProcedure*/){
+	return Symbols.makeSymbol(p.name, Types.makeProcedure(p));
+}
+
+function nthArgument(args/*Type*/, i/*INTEGER*/){
+	var arg = null;
+	arg = JsArray.at(args, i);
+	return RTL$.typeGuard(arg, Code.Expression);
+}
+
+function initStdCall(call/*PStdCall*/){
+	call.args = JsArray.make();
+}
+
+function hasArgument(call/*PStdCall*/, type/*PType*/){
+	var a = null;
+	a = new Types.ProcedureArgument();
+	a.type = type;
+	JsArray.add(call.args, a);
+}
+
+function hasVarArgument(call/*PStdCall*/, type/*PType*/){
+	var a = null;
+	a = new Types.ProcedureArgument();
+	a.isVar = true;
+	a.type = type;
+	JsArray.add(call.args, a);
+}
+
+function hasArgumentWithCustomType(call/*PStdCall*/){
+	var a = null;
+	a = new Types.ProcedureArgument();
+	JsArray.add(call.args, a);
+}
+
+function hasVarArgumnetWithCustomType(call/*PStdCall*/){
+	var a = null;
+	a = new Types.ProcedureArgument();
+	a.isVar = true;
+	JsArray.add(call.args, a);
+}
+
+function checkSingleArgument(actual/*Type*/, expected/*Type*/){
+	RTL$.assert(JsArray.len(expected) == 1);
+	checkArguments(actual, expected);
+	RTL$.assert(JsArray.len(actual) == 1);
+	return nthArgument(actual, 0);
+}
+
+function makeNew(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var argType = null;
+		var baseType = null;
+		arg = checkSingleArgument(args, this.args);
+		argType = arg.type();
+		if (!(argType instanceof Types.Pointer)){
+			Errors.raise(JsString.concat(JsString.concat(JsString.make("POINTER variable expected, got '"), argType.description()), JsString.make("'")));
+		}
+		baseType = Types.pointerBase(RTL$.typeGuard(argType, Types.Pointer));
+		if (baseType instanceof Types.NonExportedRecord){
+			Errors.raise(JsString.make("non-exported RECORD type cannot be used in NEW"));
+		}
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(arg.code(), JsString.make(" = ")), baseType.initializer(cx)), null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasVarArgumnetWithCustomType(call);
+	return makeSymbol(makeStd(JsString.make("NEW"), call));
+}
+
+function makeLen(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var argType = null;
+		arg = checkSingleArgument(args, this.args);
+		argType = arg.type();
+		if (!(argType instanceof Types.Array) && !(argType instanceof Types.String)){
+			Errors.raise(JsString.concat(JsString.concat(JsString.make("ARRAY or string is expected as an argument of LEN, got '"), argType.description()), JsString.make("'")));
+		}
+		return Code.makeSimpleExpression(JsString.concat(arg.code(), JsString.make(".length")), Types.basic().integer);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgumentWithCustomType(call);
+	return makeSymbol(makeStd(JsString.make("LEN"), call));
+}
+
+function makeOdd(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var code = null;
+		var constValue = null;
+		arg = checkSingleArgument(args, this.args);
+		code = Code.adjustPrecedence(arg, Precedence.bitAnd);
+		constValue = arg.constValue();
+		if (constValue != null){
+			constValue = Code.makeIntConst(RTL$.typeGuard(constValue, Code.IntConst).value & 1 ? 1 : 0);
+		}
+		return Code.makeExpressionWithPrecedence(JsString.concat(code, JsString.make(" & 1")), Types.basic().bool, null, constValue, Precedence.bitAnd);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(JsString.make("ODD"), call));
+}
+
+function makeAssert(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var rtl = null;
+		arg = checkSingleArgument(args, this.args);
+		rtl = cx.rtl();
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.concat(rtl.assertId(), JsString.make("(")), arg.code()), JsString.make(")")), null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgument(call, Types.basic().bool);
+	return makeSymbol(makeStd(JsString.make("ASSERT"), call));
+}
+
+function setBitImpl(name/*ARRAY OF CHAR*/, bitOp/*BinaryOpStr*/){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+			this.name = null;
+			this.bitOp = null;
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var x = null;var y = null;
+		var yValue = 0;
+		var value = null;
+		var valueCodeExp = null;
+		var valueCode = null;
+		var comment = null;
+		checkArguments(args, this.args);
+		RTL$.assert(JsArray.len(args) == 2);
+		x = nthArgument(args, 0);
+		y = nthArgument(args, 1);
+		value = y.constValue();
+		if (value == null){
+			valueCodeExp = Operator.lsl(Code.makeExpression(JsString.make("1"), Types.basic().integer, null, Code.makeIntConst(1)), y, cx);
+			valueCode = valueCodeExp.code();
+		}
+		else {
+			yValue = RTL$.typeGuard(value, Code.IntConst).value;
+			if (yValue < 0 || yValue > 31){
+				Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.make("value (0..31) expected as a second argument of "), this.name), JsString.make(", got ")), JsString.fromInt(yValue)));
+			}
+			comment = JsString.make("bit: ");
+			if (y.isTerm()){
+				comment = JsString.concat(comment, JsString.fromInt(yValue));
+			}
+			else {
+				comment = JsString.concat(comment, Code.adjustPrecedence(y, Precedence.shift));
+			}
+			yValue = 1 << yValue;
+			valueCode = JsString.concat(JsString.concat(JsString.concat(JsString.fromInt(yValue), JsString.make("/*")), comment), JsString.make("*/"));
+		}
+		return Code.makeSimpleExpression(this.bitOp(Code.adjustPrecedence(x, Precedence.assignment), valueCode), null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	call.name = JsString.make(name);
+	call.bitOp = bitOp;
+	hasVarArgument(call, Types.basic().set);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(call.name, call));
+}
+
+function checkVariableArgumentsCount(min/*INTEGER*/, max/*INTEGER*/, actual/*Type*/){
+	var len = 0;
+	len = JsArray.len(actual);
+	if (len < min){
+		Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.make("at least "), JsString.fromInt(min)), JsString.make(" argument expected, got ")), JsString.fromInt(len)));
+	}
+	else if (len > max){
+		Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.make("at most "), JsString.fromInt(max)), JsString.make(" arguments expected, got ")), JsString.fromInt(len)));
+	}
+}
+
+function incImpl(name/*ARRAY OF CHAR*/, unary/*ARRAY OF CHAR*/, incOp/*BinaryOpStr*/){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+			this.name = null;
+			this.unary = null;
+			this.incOp = null;
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var x = null;var y = null;
+		var code = null;
+		var value = null;
+		var valueCode = null;
+		checkVariableArgumentsCount(1, 2, args);
+		checkArgumentsType(args, this.args, null);
+		x = nthArgument(args, 0);
+		if (JsArray.len(args) == 1){
+			code = JsString.concat(this.unary, x.code());
+		}
+		else {
+			y = nthArgument(args, 1);
+			value = y.constValue();
+			if (value == null){
+				valueCode = y.code();
+			}
+			else {
+				valueCode = JsString.fromInt(RTL$.typeGuard(value, Code.IntConst).value);
+				if (!y.isTerm()){
+					valueCode = JsString.concat(JsString.concat(JsString.concat(valueCode, JsString.make("/*")), y.code()), JsString.make("*/"));
+				}
+			}
+			code = this.incOp(x.code(), valueCode);
+		}
+		return Code.makeSimpleExpression(code, null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	call.name = JsString.make(name);
+	call.unary = JsString.make(unary);
+	call.incOp = incOp;
+	hasVarArgument(call, Types.basic().integer);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(call.name, call));
+}
+
+function inclOp(x/*Type*/, y/*Type*/){
+	return JsString.concat(JsString.concat(x, JsString.make(" |= ")), y);
+}
+
+function exclOp(x/*Type*/, y/*Type*/){
+	return JsString.concat(JsString.concat(JsString.concat(x, JsString.make(" &= ~(")), y), JsString.make(")"));
+}
+
+function incOp(x/*Type*/, y/*Type*/){
+	return JsString.concat(JsString.concat(x, JsString.make(" += ")), y);
+}
+
+function decOp(x/*Type*/, y/*Type*/){
+	return JsString.concat(JsString.concat(x, JsString.make(" -= ")), y);
+}
+
+function makeAbs(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var argType = null;
+		arg = checkSingleArgument(args, this.args);
+		argType = arg.type();
+		if (!JsArray.contains(Types.numeric(), argType)){
+			Errors.raise(JsString.concat(JsString.concat(JsString.make("type mismatch: expected numeric type, got '"), argType.description()), JsString.make("'")));
+		}
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.make("Math.abs("), arg.code()), JsString.make(")")), argType);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgumentWithCustomType(call);
+	return makeSymbol(makeStd(JsString.make("ABS"), call));
+}
+
+function makeFloor(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		arg = checkSingleArgument(args, this.args);
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.make("Math.floor("), arg.code()), JsString.make(")")), Types.basic().integer);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgument(call, Types.basic().real);
+	return makeSymbol(makeStd(JsString.make("FLOOR"), call));
+}
+
+function makeFlt(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var value = null;
+		arg = checkSingleArgument(args, this.args);
+		value = arg.constValue();
+		if (value != null){
+			value = Code.makeRealConst(RTL$.typeGuard(value, Code.IntConst).value);
+		}
+		return Code.makeExpressionWithPrecedence(arg.code(), Types.basic().real, null, value, arg.maxPrecedence());
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(JsString.make("FLT"), call));
+}
+
+function bitShiftImpl(name/*ARRAY OF CHAR*/, op/*BinaryOp*/){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+			this.name = null;
+			this.op = null;
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var x = null;var y = null;
+		checkArguments(args, this.args);
+		RTL$.assert(JsArray.len(args) == 2);
+		x = nthArgument(args, 0);
+		y = nthArgument(args, 1);
+		return this.op(x, y, cx);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	call.name = JsString.make(name);
+	call.op = op;
+	hasArgument(call, Types.basic().integer);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(call.name, call));
+}
+
+function makeOrd(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		var argType = null;
+		var value = null;
+		var code = null;
+		var ch = 0;
+		var result = null;
+		arg = checkSingleArgument(args, this.args);
+		argType = arg.type();
+		if (argType == Types.basic().ch || argType == Types.basic().set){
+			value = arg.constValue();
+			if (value != null && argType == Types.basic().set){
+				value = Code.makeIntConst(RTL$.typeGuard(value, Code.SetConst).value);
+			}
+			result = Code.makeExpression(arg.code(), Types.basic().integer, null, value);
+		}
+		else if (argType == Types.basic().bool){
+			code = JsString.concat(Code.adjustPrecedence(arg, Precedence.conditional), JsString.make(" ? 1 : 0"));
+			result = Code.makeExpressionWithPrecedence(code, Types.basic().integer, null, arg.constValue(), Precedence.conditional);
+		}
+		else if (argType instanceof Types.String && Types.stringAsChar(RTL$.typeGuard(argType, Types.String), {set: function($v){ch = $v;}, get: function(){return ch;}})){
+			result = Code.makeExpression(JsString.fromInt(ch), Types.basic().integer, null, Code.makeIntConst(ch));
+		}
+		else {
+			Errors.raise(JsString.concat(JsString.concat(JsString.make("ORD function expects CHAR or BOOLEAN or SET as an argument, got '"), argType.description()), JsString.make("'")));
+		}
+		return result;
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgumentWithCustomType(call);
+	return makeSymbol(makeStd(JsString.make("ORD"), call));
+}
+
+function makeChr(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var arg = null;
+		arg = checkSingleArgument(args, this.args);
+		return Code.makeSimpleExpression(arg.code(), Types.basic().ch);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(JsString.make("CHR"), call));
+}
+
+function makePack(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var x = null;var y = null;
+		checkArguments(args, this.args);
+		x = nthArgument(args, 0);
+		y = nthArgument(args, 1);
+		return Code.makeSimpleExpression(Operator.mulInplace(x, Operator.pow2(y), cx), null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasVarArgument(call, Types.basic().real);
+	hasArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(JsString.make("PACK"), call));
+}
+
+function makeUnpk(){
+	var CallImpl = StdCall.extend({
+		init: function CallImpl(){
+			StdCall.prototype.init.call(this);
+		}
+	});
+	var call = null;
+	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
+		var x = null;var y = null;
+		checkArguments(args, this.args);
+		x = nthArgument(args, 0);
+		y = nthArgument(args, 1);
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(Operator.assign(y, Operator.log2(x), cx), JsString.make("; ")), Operator.divInplace(x, Operator.pow2(y), cx)), null);
+	}
+	call = new CallImpl();
+	initStdCall(call);
+	hasVarArgument(call, Types.basic().real);
+	hasVarArgument(call, Types.basic().integer);
+	return makeSymbol(makeStd(JsString.make("UNPK"), call));
+}
+
+function dumpProcArgs(proc/*Type*/){
+	var result = null;
+	var len = 0;
+	var i = 0;
+	var arg = null;
+	len = JsArray.len(proc.mArgs);
+	if (len == 0){
+		if (proc.mResult != null){
+			result = JsString.make("()");
+		}
+		else {
+			result = JsString.makeEmpty();
+		}
+	}
+	else {
+		result = JsString.make("(");
+		for (i = 0; i <= len - 1 | 0; ++i){
+			if (i != 0){
+				result = JsString.concat(result, JsString.make(", "));
+			}
+			arg = JsArray.at(proc.mArgs, i);
+			result = JsString.concat(result, RTL$.typeGuard(arg, Types.ProcedureArgument).type.description());
+		}
+		result = JsString.concat(result, JsString.make(")"));
+	}
+	return result;
+}
+Type.prototype.description = function(){
+	var result = null;
+	result = Types.typeName(this);
+	if (result == null){
+		result = JsString.concat(JsString.make("PROCEDURE"), dumpProcArgs(this));
+		if (this.mResult != null){
+			result = JsString.concat(JsString.concat(result, JsString.make(": ")), this.mResult.description());
+		}
+	}
+	return result;
+}
+Type.prototype.callGenerator = function(cx/*PType*/, id/*Type*/){
+	return makeProcCallGenerator(cx, id, this);
+}
+Type.prototype.define = function(args/*Type*/, result/*PType*/){
+	this.mArgs = args;
+	this.mResult = result;
+}
+Type.prototype.args = function(){
+	return this.mArgs;
+}
+Type.prototype.result = function(){
+	return this.mResult;
+}
+
+function make(name/*Type*/){
+	var result = null;
+	result = new Type();
+	result.name = name;
+	return result;
+}
+predefined = JsArray.make();
+JsArray.add(predefined, makeNew());
+JsArray.add(predefined, makeLen());
+JsArray.add(predefined, makeOdd());
+JsArray.add(predefined, makeAssert());
+JsArray.add(predefined, setBitImpl("INCL", inclOp));
+JsArray.add(predefined, setBitImpl("EXCL", exclOp));
+JsArray.add(predefined, incImpl("INC", "++", incOp));
+JsArray.add(predefined, incImpl("DEC", "--", decOp));
+JsArray.add(predefined, makeAbs());
+JsArray.add(predefined, makeFloor());
+JsArray.add(predefined, makeFlt());
+JsArray.add(predefined, bitShiftImpl("LSL", Operator.lsl));
+JsArray.add(predefined, bitShiftImpl("ASR", Operator.asr));
+JsArray.add(predefined, bitShiftImpl("ROR", Operator.ror));
+JsArray.add(predefined, makeOrd());
+JsArray.add(predefined, makeChr());
+JsArray.add(predefined, makePack());
+JsArray.add(predefined, makeUnpk());
+exports.Call = Call;
+exports.CallGenerator = CallGenerator;
+exports.Type = Type;
+exports.Std = Std;
+exports.predefined = function(){return predefined;};
+exports.checkArgumentsCount = checkArgumentsCount;
+exports.makeCallGenerator = makeCallGenerator;
+exports.makeProcCallGeneratorWithCustomArgs = makeProcCallGeneratorWithCustomArgs;
+exports.makeArgumentsCode = makeArgumentsCode;
+exports.makeProcCallGenerator = makeProcCallGenerator;
+exports.make = make;

+ 8 - 1
src/js/Types.js

@@ -100,6 +100,11 @@ var Procedure = NamedType.extend({
 		NamedType.prototype.init.call(this);
 	}
 });
+var DefinedProcedure = Procedure.extend({
+	init: function DefinedProcedure(){
+		Procedure.prototype.init.call(this);
+	}
+});
 var ProcedureArgument = Object.Type.extend({
 	init: function ProcedureArgument(){
 		Object.Type.prototype.init.call(this);
@@ -467,7 +472,7 @@ function makeProcedureArgument(type/*PType*/, isVar/*BOOLEAN*/){
 	return result;
 }
 Module.prototype.idType = function(){
-	return JsString.make("module");
+	return JsString.make("MODULE");
 }
 
 function makeTypeId(type/*PType*/){
@@ -582,9 +587,11 @@ exports.Variable = Variable;
 exports.VariableRef = VariableRef;
 exports.ProcedureId = ProcedureId;
 exports.String = String;
+exports.NamedType = NamedType;
 exports.Array = Array;
 exports.Pointer = Pointer;
 exports.Procedure = Procedure;
+exports.DefinedProcedure = DefinedProcedure;
 exports.ProcedureArgument = ProcedureArgument;
 exports.Record = Record;
 exports.NonExportedRecord = NonExportedRecord;

+ 16 - 22
src/module.js

@@ -2,7 +2,7 @@
 
 var Code = require("js/Code.js");
 var Errors = require("js/Errors.js");
-var Procedure = require("procedure.js");
+var Procedure = require("js/Procedure.js");
 var Symbol = require("js/Symbols.js");
 var Type = require("js/Types.js");
 
@@ -10,7 +10,8 @@ var AnyTypeProc = Type.Procedure.extend({
     init: function AnyTypeProc(){
         Type.Procedure.prototype.init.call("PROCEDURE: JS.var");
     },
-    result: function(){return any;}
+    result: function(){return any;},
+    args: function(){return undefined;}
 });
 
 var anyProc = new AnyTypeProc();
@@ -23,7 +24,7 @@ var AnyType = Type.Type.extend({
     initializer: function(){return undefined;},
     findSymbol: function(){return this;},
     callGenerator: function(context, id){
-        return new Procedure.CallGenerator(context, id, anyProc);
+        return new Procedure.makeProcCallGenerator(context, id, anyProc);
     }
 });
 
@@ -35,40 +36,33 @@ var varTypeId = "var$";
 var doProcSymbol = (function(){
     var description = "JS predefined procedure 'do'";
 
-    var DoProcCallGenerator = Procedure.CallGenerator.extend({
-        init: function DoProcCallGenerator(context, id, type){
-            Procedure.CallGenerator.prototype.init.call(this, context, id, type);
-            this.__code = undefined;
+    var expectedArgs = [Type.makeProcedureArgument(undefined, false)];
+
+    var Call = Procedure.Call.extend({
+        init: function DoProcedureCall(){
+            Procedure.Call.prototype.init.call(this);
         },
-        prolog: function(id){return "";},
-        checkArgument: function(pos, e){
-            var type = e.type();
+        make: function(args, cx){
+            Procedure.checkArgumentsCount(args.length, expectedArgs.length);
+            var type = args[0].type();
             if (!(type instanceof Type.String))
                 throw new Errors.Error(
                     "string is expected as an argument of " + description
                     + ", got " + type.description());
             
-            this.__code = Type.stringValue(type);
-            return Procedure.CallGenerator.prototype.checkArgument.call(this, pos, e);
-        },
-        epilog: function(){return "";},
-        writeArgumentCode: function(){},
-        callExpression: function(){
-            return Code.makeExpression(this.__code);
+            return Code.makeExpression(Type.stringValue(type));
         }
     });
 
-    var args = [Type.makeProcedureArgument(undefined, false)];
+    var call = new Call();
     var ProcType = Type.Procedure.extend({
         init: function(){
             Type.Procedure.prototype.init.call(this);
             Type.initProcedure(this, doProcId);
         },
         description: function(){return description;},
-        args: function(){return args;},
-        result: function(){return undefined;},
-        callGenerator: function(context, id){
-            return new DoProcCallGenerator(context, id, this);
+        callGenerator: function(context){
+            return Procedure.makeCallGenerator(call, context);
         }
         });
 

+ 8 - 8
src/ob/Cast.ob

@@ -2,7 +2,7 @@ MODULE Cast;
 IMPORT Code, Context, JsArray, JsString, Object, Types;
 
 TYPE
-    CastOp* = RECORD
+    CastOp* = RECORD(Object.Type)
         PROCEDURE make*(c: Context.Type; e: Code.PExpression): Code.PExpression
     END;
 
@@ -57,7 +57,7 @@ PROCEDURE areTypesMatch*(t1: Types.PType; t2: Types.PType): BOOLEAN;
             OR ((t2 = Types.nil) & (matchesToNIL(t1^))))
 END areTypesMatch;
 
-PROCEDURE areArgsMatch(oa1, oa2: Object.PType; p1, p2: Types.PProcedure): BOOLEAN;
+PROCEDURE areArgsMatch(oa1, oa2: Object.PType; p1, p2: Types.PDefinedProcedure): BOOLEAN;
 VAR
     a1: Types.PProcedureArgument;
     a2: Types.PProcedureArgument;
@@ -69,7 +69,7 @@ BEGIN
             OR areTypesExactlyMatch(a1.type, a2.type))
 END areArgsMatch;
 
-PROCEDURE areProceduresMatch*(p1: Types.PProcedure; p2: Types.PProcedure): BOOLEAN;
+PROCEDURE areProceduresMatch*(p1, p2: Types.PDefinedProcedure): BOOLEAN;
 VAR
     result: BOOLEAN;
     args1, args2: JsArray.Type;
@@ -107,9 +107,9 @@ BEGIN
     ELSIF (t1 IS Types.PPointer) & (t2 IS Types.PPointer) THEN
         result := areTypesMatch(Types.pointerBase(t1(Types.PPointer)^), 
                                 Types.pointerBase(t2(Types.PPointer)^));
-    ELSIF (t1 IS Types.PProcedure) & (t2 IS Types.PProcedure) THEN
-        result := areProceduresMatch(t1(Types.PProcedure), 
-                                     t2(Types.PProcedure));
+    ELSIF (t1 IS Types.PDefinedProcedure) & (t2 IS Types.PDefinedProcedure) THEN
+        result := areProceduresMatch(t1(Types.PDefinedProcedure), 
+                                     t2(Types.PDefinedProcedure));
     END;
     RETURN result
 END areTypesExactlyMatchImpl;
@@ -163,8 +163,8 @@ BEGIN
         END;
     ELSIF (from = Types.nil) & matchesToNIL(to^) THEN
         result := doNothing;
-    ELSIF (from IS Types.PProcedure) & (to IS Types.PProcedure) THEN
-        IF areProceduresMatch(from(Types.PProcedure), to(Types.PProcedure)) THEN
+    ELSIF (from IS Types.PDefinedProcedure) & (to IS Types.PDefinedProcedure) THEN
+        IF areProceduresMatch(from(Types.PDefinedProcedure), to(Types.PDefinedProcedure)) THEN
             result := doNothing;
         END
     END;

+ 3 - 2
src/ob/Code.ob

@@ -65,14 +65,14 @@ TYPE
         value: JsString.Type
     END;
 
-    Expression* = RECORD
+    Expression* = RECORD(Object.Type)
         PROCEDURE code*(): JsString.Type;
         PROCEDURE lval*(): JsString.Type;
         PROCEDURE type*(): Types.PType;
         PROCEDURE designator*(): PDesignator;
         PROCEDURE constValue*(): PConst;
         PROCEDURE maxPrecedence*(): INTEGER;
-        PROCEDURE isTerm(): BOOLEAN;
+        PROCEDURE isTerm*(): BOOLEAN;
 
         mCode: JsString.Type;
         mType: Types.PType;
@@ -251,6 +251,7 @@ PROCEDURE makeExpressionWithPrecedence*(
 VAR
     result: PExpression;
 BEGIN
+    ASSERT(code # NIL);
     NEW(result);
     result.mCode := code;
     result.mType := type;

+ 3 - 1
src/ob/Context.ob

@@ -13,7 +13,8 @@ TYPE
         strCmp*: PROCEDURE(s1, s2: JsString.Type): JsString.Type;
         assignArrayFromString*: PROCEDURE(s1, s2: JsString.Type): JsString.Type;
         setInclL*: PROCEDURE(l, r: JsString.Type): JsString.Type;
-        setInclR*: PROCEDURE(l, r: JsString.Type): JsString.Type
+        setInclR*: PROCEDURE(l, r: JsString.Type): JsString.Type;
+        assertId*: PROCEDURE(): JsString.Type
     END;
     PRtl* = POINTER TO Rtl;
 
@@ -26,5 +27,6 @@ TYPE
         qualifyScope*:  PROCEDURE(scope: PScope): JsString.Type;
         rtl*:           PROCEDURE(): PRtl
     END;
+    PType* = POINTER TO Type;
 
 END Context.

+ 8 - 0
src/ob/JsArray.ob

@@ -46,6 +46,14 @@ BEGIN
     RETURN result
 END stringsAt;
 
+PROCEDURE contains*(a: Type; x: Object.PType): BOOLEAN;
+VAR
+    result: BOOLEAN;
+BEGIN
+    JS.do("result = (a.indexOf(x) != -1)");
+    RETURN result
+END contains;
+
 PROCEDURE make*(): Type;
 VAR
     result: Type;

+ 1006 - 0
src/ob/Procedure.ob

@@ -0,0 +1,1006 @@
+MODULE Procedure;
+IMPORT
+    Cast,
+    Code, 
+    Context, 
+    Errors, 
+    JsArray, 
+    JsString, 
+    Object, 
+    Operator, 
+    Precedence := CodePrecedence, 
+    Symbols, 
+    Types;
+TYPE
+    Call* = RECORD
+        PROCEDURE make(args: JsArray.Type; cx: Context.Type): Code.PExpression
+    END;
+    PCall = POINTER TO Call;
+
+    StdCall = RECORD(Call)
+        args: JsArray.Type
+    END;
+    PStdCall = POINTER TO StdCall;
+
+    CallGenerator* = RECORD
+        PROCEDURE handleArgument(e: Code.PExpression);
+        PROCEDURE end(): Code.PExpression;
+
+        args: JsArray.Type;
+        cx: Context.PType;
+        call: PCall
+    END;
+    PCallGenerator = POINTER TO CallGenerator;
+
+    Impl = RECORD(Types.Procedure)
+        PROCEDURE callGenerator(cx: Context.PType): PCallGenerator
+    END;
+
+    Type* = RECORD(Types.DefinedProcedure)
+        PROCEDURE callGenerator(cx: Context.PType; id: JsString.Type): PCallGenerator;
+        PROCEDURE define(args: JsArray.Type; result: Types.PType);
+
+        mArgs: JsArray.Type;
+        mResult: Types.PType
+    END;
+    PType = POINTER TO Type;
+
+    Std* = RECORD(Impl)
+        call: PCall
+    END;
+
+    ArgumentsCode = RECORD
+        PROCEDURE write(actual: Code.PExpression; 
+                        expected: Types.PProcedureArgument; 
+                        cast: Cast.PCastOp
+                        );
+        PROCEDURE result(): JsString.Type
+    END;
+    PArgumentsCode = POINTER TO ArgumentsCode;
+
+    GenArgCode = RECORD(ArgumentsCode)
+        code: JsString.Type;
+        cx: Context.PType
+    END;
+
+    BinaryOp = PROCEDURE(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
+    BinaryOpStr = PROCEDURE (x, y: JsString.Type): JsString.Type;
+VAR
+    predefined*: JsArray.Type;
+
+PROCEDURE checkArgument(
+    actual: Code.PExpression; 
+    expected: Types.PProcedureArgument; 
+    pos: INTEGER;
+    code: PArgumentsCode
+    );
+VAR
+    actualType, expectType: Types.PType;
+    designator: Code.PDesignator;
+    info: Types.PId;
+    result: Cast.PCastOp;
+BEGIN
+    expectType := expected.type; (* can be NIL for predefined functions (like NEW), dont check it in this case *)
+    IF expectType # NIL THEN
+        actualType := actual.type();
+        result := Cast.implicit(actualType, expectType, Operator.castOperations);
+        IF result = NIL THEN
+            Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
+                JsString.make("type mismatch for argument "),
+                JsString.fromInt(pos + 1)),
+                JsString.make(": '")),
+                actualType.description()),
+                JsString.make("' cannot be converted to '")),
+                expectType.description()),
+                JsString.make("'")));
+        END;
+        IF expected.isVar & (expectType # actualType) & Types.isInt(actualType) THEN
+            Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
+                JsString.make("type mismatch for argument "),
+                JsString.fromInt(pos + 1)),
+                JsString.make(": cannot pass '")),
+                actualType.description()),
+                JsString.make("' as VAR parameter of type '")),
+                expectType.description()),
+                JsString.make("'")));
+        END;
+    END;
+    IF expected.isVar THEN
+        designator := actual.designator();
+        IF designator = NIL THEN
+            Errors.raise(JsString.make("expression cannot be used as VAR parameter"));
+        END;
+        info := designator.info();
+        IF info IS Types.PConst THEN
+            Errors.raise(JsString.make("constant cannot be used as VAR parameter"));
+        END;
+        IF (info IS Types.PVariable) 
+         & Types.isVariableReadOnly(info(Types.PVariable)^) THEN
+            Errors.raise(JsString.concat(info.idType(), JsString.make(" cannot be used as VAR parameter")));
+        END;
+    END;
+    IF code # NIL THEN
+        code.write(actual, expected, result);
+    END;
+END checkArgument;
+
+PROCEDURE checkArgumentsType(actual: JsArray.Type; expected: JsArray.Type; code: PArgumentsCode);
+VAR
+    actualLen: INTEGER;
+    i: INTEGER;
+    actualExp: Object.PType;
+    expectedArg: Object.PType;
+BEGIN
+    actualLen := JsArray.len(actual);
+    WHILE i < actualLen DO
+        actualExp := JsArray.at(actual, i);
+        expectedArg := JsArray.at(expected, i);
+        checkArgument(actualExp(Code.PExpression), expectedArg(Types.PProcedureArgument), i, code);
+        INC(i);
+    END;
+END checkArgumentsType;
+
+PROCEDURE checkArgumentsCount*(actual, expected: INTEGER);
+BEGIN
+    IF actual # expected THEN
+        Errors.raise(JsString.concat(JsString.concat(
+            JsString.fromInt(expected),
+            JsString.make(" argument(s) expected, got ")),
+            JsString.fromInt(actual)));
+    END;
+END checkArgumentsCount;
+
+PROCEDURE processArguments(actual: JsArray.Type; expected: JsArray.Type; code: PArgumentsCode);
+BEGIN
+    checkArgumentsCount(JsArray.len(actual), JsArray.len(expected));
+    checkArgumentsType(actual, expected, code);
+END processArguments;
+
+PROCEDURE checkArguments(actual: JsArray.Type; expected: JsArray.Type);
+BEGIN
+    processArguments(actual, expected, NIL);
+END checkArguments;
+
+PROCEDURE makeStd(name: JsString.Type; call: PCall): Types.PProcedure;
+VAR
+    result: POINTER TO Std;
+BEGIN
+    NEW(result);
+    Types.initProcedure(result^, name);
+    result.call := call;
+    RETURN result
+END makeStd;
+
+PROCEDURE CallGenerator.handleArgument(e: Code.PExpression);
+BEGIN
+    JsArray.add(SELF.args, e);
+END CallGenerator.handleArgument;
+
+PROCEDURE CallGenerator.end(): Code.PExpression;
+    RETURN SELF.call.make(SELF.args, SELF.cx^)
+END CallGenerator.end;
+
+PROCEDURE makeCallGenerator*(call: PCall; cx: Context.PType): PCallGenerator;
+VAR
+    result: PCallGenerator;
+BEGIN
+    ASSERT(cx # NIL);
+    NEW(result);
+    result.args := JsArray.make();
+    result.cx := cx;
+    result.call := call;
+    RETURN result
+END makeCallGenerator;
+
+PROCEDURE GenArgCode.write(actual: Code.PExpression; expected: Types.PProcedureArgument; cast: Cast.PCastOp);
+VAR
+    e: Code.PExpression;
+BEGIN
+    IF (expected # NIL) & expected.isVar THEN
+        actual := Code.refExpression(actual);
+    ELSE
+        actual := Code.derefExpression(actual);
+    END;
+    IF JsString.len(SELF.code) # 0 THEN
+        SELF.code := JsString.concat(SELF.code, JsString.make(", "));
+    END;
+    IF cast # NIL THEN
+        e := cast.make(SELF.cx^, actual);
+    ELSE
+        e := actual;
+    END;
+    SELF.code := JsString.concat(SELF.code, e.code());
+END GenArgCode.write;
+
+PROCEDURE GenArgCode.result(): JsString.Type;
+    RETURN SELF.code
+END GenArgCode.result;
+
+PROCEDURE makeProcCallGeneratorWithCustomArgs*(
+    cx: Context.PType; 
+    id: JsString.Type; 
+    type: Types.DefinedProcedure;
+    argumentsCode: PArgumentsCode
+    ) : PCallGenerator;
+TYPE
+    CallImpl = RECORD(Call)
+        id: JsString.Type;
+        args: JsArray.Type;
+        result: Types.PType;
+        argumentsCode: PArgumentsCode
+    END;
+VAR
+    call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        expectedArgs: JsArray.Type;
+        a: Object.PType;
+        i: INTEGER;
+
+    BEGIN
+        expectedArgs := SELF.args;
+        IF expectedArgs # NIL THEN
+            processArguments(args, expectedArgs, SELF.argumentsCode);
+        ELSE
+            FOR i := 0 TO JsArray.len(args) - 1 DO
+                a := JsArray.at(args, i);
+                SELF.argumentsCode.write(a(Code.PExpression), NIL, NIL);
+            END;
+        END;
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(JsString.concat(
+                SELF.id,
+                JsString.make("(")),
+                SELF.argumentsCode.result()),
+                JsString.make(")")),
+            SELF.result
+            )
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    call.id := id;
+    call.args := type.args();
+    call.result := type.result();
+    call.argumentsCode := argumentsCode;
+    RETURN makeCallGenerator(call, cx)
+END makeProcCallGeneratorWithCustomArgs;
+
+PROCEDURE makeArgumentsCode*(cx: Context.PType): PArgumentsCode;
+VAR
+    result: POINTER TO GenArgCode;
+BEGIN
+    NEW(result);
+    result.code := JsString.makeEmpty();
+    result.cx := cx;
+    RETURN result
+END makeArgumentsCode;
+
+PROCEDURE makeProcCallGenerator*(
+    cx: Context.PType; 
+    id: JsString.Type; 
+    type: Types.DefinedProcedure
+    ) : PCallGenerator;
+    RETURN makeProcCallGeneratorWithCustomArgs(cx, id, type, makeArgumentsCode(cx))
+END makeProcCallGenerator;
+
+PROCEDURE Std.description(): JsString.Type;
+    RETURN JsString.concat(JsString.make("standard procedure "), Types.typeName(SELF))
+END Std.description;
+
+PROCEDURE Std.callGenerator(cx: Context.PType): PCallGenerator;
+    RETURN makeCallGenerator(SELF.call, cx)
+END Std.callGenerator;
+
+PROCEDURE makeSymbol(p: Types.PProcedure): Symbols.PSymbol;
+    RETURN Symbols.makeSymbol(p.name, Types.makeProcedure(p))
+END makeSymbol;
+
+PROCEDURE nthArgument(args: JsArray.Type; i: INTEGER): Code.PExpression;
+VAR
+    arg: Object.PType;
+BEGIN
+    arg := JsArray.at(args, i);
+    RETURN arg(Code.PExpression)
+END nthArgument;
+
+PROCEDURE initStdCall(call: PStdCall);
+BEGIN
+    call.args := JsArray.make();
+END initStdCall;
+
+PROCEDURE hasArgument(call: PStdCall; type: Types.PType);
+VAR
+    a: Types.PProcedureArgument;
+BEGIN
+    NEW(a);
+    a.type := type;
+    JsArray.add(call.args, a);
+END hasArgument;
+
+PROCEDURE hasVarArgument(call: PStdCall; type: Types.PType);
+VAR
+    a: Types.PProcedureArgument;
+BEGIN
+    NEW(a);
+    a.isVar := TRUE;
+    a.type := type;
+    JsArray.add(call.args, a);
+END hasVarArgument;
+
+PROCEDURE hasArgumentWithCustomType(call: PStdCall);
+VAR
+    a: Types.PProcedureArgument;
+BEGIN
+    NEW(a);
+    JsArray.add(call.args, a);
+END hasArgumentWithCustomType;
+
+PROCEDURE hasVarArgumnetWithCustomType(call: PStdCall);
+VAR
+    a: Types.PProcedureArgument;
+BEGIN
+    NEW(a);
+    a.isVar := TRUE;
+    JsArray.add(call.args, a);
+END hasVarArgumnetWithCustomType;
+
+PROCEDURE checkSingleArgument(actual: JsArray.Type; expected: JsArray.Type): Code.PExpression;
+BEGIN
+    ASSERT(JsArray.len(expected) = 1);
+    checkArguments(actual, expected);
+    ASSERT(JsArray.len(actual) = 1);
+    RETURN nthArgument(actual, 0)
+END checkSingleArgument;
+
+PROCEDURE makeNew(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        argType: Types.PType;
+        baseType: Types.PRecord;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        argType := arg.type();
+        IF ~(argType IS Types.PPointer) THEN
+            Errors.raise(JsString.concat(JsString.concat(
+                JsString.make("POINTER variable expected, got '"),
+                argType.description()),
+                JsString.make("'")));
+        END;
+        baseType := Types.pointerBase(argType(Types.PPointer)^);
+        IF baseType IS Types.PNonExportedRecord THEN
+            Errors.raise(JsString.make("non-exported RECORD type cannot be used in NEW"));
+        END;
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(
+                arg.code(), 
+                JsString.make(" = ")),
+                baseType.initializer(cx)),
+            NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasVarArgumnetWithCustomType(call);
+    RETURN makeSymbol(makeStd(JsString.make("NEW"), call))
+END makeNew;
+
+PROCEDURE makeLen(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        argType: Types.PType;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        argType := arg.type();
+        IF ~(argType IS Types.PArray) & ~(argType IS Types.PString) THEN
+            Errors.raise(JsString.concat(JsString.concat(
+                JsString.make("ARRAY or string is expected as an argument of LEN, got '"),
+                argType.description()),
+                JsString.make("'")));
+        END;
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(arg.code(), JsString.make(".length")),
+            Types.basic.integer)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgumentWithCustomType(call);
+    (*call.args[0].type := Types.makeArray(
+        JsString.make("ARRAY OF any type"), 
+        NIL, 
+        NIL,
+        Types.openArrayLength);*)
+    RETURN makeSymbol(makeStd(JsString.make("LEN"), call))
+END makeLen;
+
+PROCEDURE makeOdd(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        code: JsString.Type;
+        constValue: Code.PConst;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        code := Code.adjustPrecedence(arg, Precedence.bitAnd);
+        
+        constValue := arg.constValue();
+        IF constValue # NIL THEN
+            constValue := Code.makeIntConst(
+                ORD(ODD(constValue^(Code.IntConst).value)));
+        END;
+
+        RETURN Code.makeExpressionWithPrecedence(
+            JsString.concat(code, JsString.make(" & 1")),
+            Types.basic.bool,
+            NIL,
+            constValue,
+            Precedence.bitAnd)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(JsString.make("ODD"), call))
+END makeOdd;
+
+PROCEDURE makeAssert(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        rtl: Context.PRtl;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        rtl := cx.rtl();
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(JsString.concat(
+                rtl.assertId(), 
+                JsString.make("(")), 
+                arg.code()), 
+                JsString.make(")")),
+            NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgument(call, Types.basic.bool);
+    RETURN makeSymbol(makeStd(JsString.make("ASSERT"), call))
+END makeAssert;
+
+PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+            name: JsString.Type;
+            bitOp: BinaryOpStr
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        x, y: Code.PExpression;
+        yValue: INTEGER;
+        value: Code.PConst;
+        valueCodeExp: Code.PExpression;
+        valueCode: JsString.Type;
+        comment: JsString.Type;
+    BEGIN
+        checkArguments(args, SELF.args);
+        ASSERT(JsArray.len(args) = 2);
+        x := nthArgument(args, 0);
+        y := nthArgument(args, 1);
+        value := y.constValue();
+        IF value = NIL THEN
+            valueCodeExp := Operator.lsl(
+                Code.makeExpression(
+                    JsString.make("1"), 
+                    Types.basic.integer,
+                    NIL,
+                    Code.makeIntConst(1)), 
+                y,
+                cx);
+            valueCode := valueCodeExp.code();
+        ELSE
+            yValue := value^(Code.IntConst).value;
+            IF (yValue < 0) OR (yValue > 31) THEN
+                Errors.raise(JsString.concat(JsString.concat(JsString.concat(
+                    JsString.make("value (0..31) expected as a second argument of "),
+                    SELF.name),
+                    JsString.make(", got ")),
+                    JsString.fromInt(yValue)));
+            END;
+            comment := JsString.make("bit: ");
+            IF y.isTerm() THEN
+                comment := JsString.concat(comment, JsString.fromInt(yValue));
+            ELSE
+                comment := JsString.concat(comment, Code.adjustPrecedence(y, Precedence.shift));
+            END;
+            yValue := LSL(1, yValue);
+            valueCode := JsString.concat(JsString.concat(JsString.concat(
+                JsString.fromInt(yValue), 
+                JsString.make("/*")),
+                comment),
+                JsString.make("*/"));
+        END;
+
+        RETURN Code.makeSimpleExpression(
+            SELF.bitOp(Code.adjustPrecedence(x, Precedence.assignment), valueCode),
+            NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    call.name := JsString.make(name);
+    call.bitOp := bitOp;
+    hasVarArgument(call, Types.basic.set);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(call.name, call))
+END setBitImpl;
+
+PROCEDURE checkVariableArgumentsCount(min, max: INTEGER; actual: JsArray.Type);
+VAR
+    len: INTEGER;
+BEGIN
+    len := JsArray.len(actual);
+    IF len < min THEN
+        Errors.raise(JsString.concat(JsString.concat(JsString.concat(
+            JsString.make("at least "), 
+            JsString.fromInt(min)), 
+            JsString.make(" argument expected, got ")), 
+            JsString.fromInt(len)));
+    ELSIF len > max THEN
+        Errors.raise(JsString.concat(JsString.concat(JsString.concat(
+            JsString.make("at most "),
+            JsString.fromInt(max)),
+            JsString.make(" arguments expected, got ")),
+            JsString.fromInt(len)));
+    END;
+END checkVariableArgumentsCount;
+
+PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+            name: JsString.Type;
+            unary: JsString.Type;
+            incOp: BinaryOpStr
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        x, y: Code.PExpression;
+        code: JsString.Type;
+        value: Code.PConst;
+        valueCode: JsString.Type;
+    BEGIN
+        checkVariableArgumentsCount(1, 2, args);
+        checkArgumentsType(args, SELF.args, NIL);
+        x := nthArgument(args, 0);
+        IF JsArray.len(args) = 1 THEN
+            code := JsString.concat(SELF.unary, x.code());
+        ELSE
+            y := nthArgument(args, 1);
+            value := y.constValue();
+            IF value = NIL THEN
+                valueCode := y.code();
+            ELSE
+                valueCode := JsString.fromInt(value^(Code.IntConst).value);
+                IF ~y.isTerm() THEN
+                    valueCode := JsString.concat(JsString.concat(JsString.concat(
+                        valueCode, 
+                        JsString.make("/*")), 
+                        y.code()), 
+                        JsString.make("*/"));
+                END;
+            END;
+            code := SELF.incOp(x.code(), valueCode);
+        END;
+        RETURN Code.makeSimpleExpression(code, NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    call.name := JsString.make(name);
+    call.unary := JsString.make(unary);
+    call.incOp := incOp;
+    hasVarArgument(call, Types.basic.integer);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(call.name, call))
+END incImpl;
+
+PROCEDURE inclOp(x, y: JsString.Type): JsString.Type;
+    RETURN JsString.concat(JsString.concat(x, JsString.make(" |= ")), y)
+END inclOp;
+
+PROCEDURE exclOp(x, y: JsString.Type): JsString.Type;
+    RETURN JsString.concat(JsString.concat(JsString.concat(
+        x, 
+        JsString.make(" &= ~(" )), 
+        y), 
+        JsString.make(")"))
+END exclOp;
+
+PROCEDURE incOp(x, y: JsString.Type): JsString.Type;
+    RETURN JsString.concat(JsString.concat(x, JsString.make(" += ")), y)
+END incOp;
+
+PROCEDURE decOp(x, y: JsString.Type): JsString.Type;
+    RETURN JsString.concat(JsString.concat(x, JsString.make(" -= ")), y)
+END decOp;
+
+PROCEDURE makeAbs(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        argType: Types.PType;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        argType := arg.type();
+        IF ~JsArray.contains(Types.numeric, argType) THEN
+            Errors.raise(JsString.concat(JsString.concat(
+                JsString.make("type mismatch: expected numeric type, got '"),
+                argType.description()),
+                JsString.make("'")));
+        END;
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(
+                JsString.make("Math.abs("), 
+                arg.code()), 
+                JsString.make(")")),
+            argType)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgumentWithCustomType(call);
+    RETURN makeSymbol(makeStd(JsString.make("ABS"), call))
+END makeAbs;
+
+PROCEDURE makeFloor(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(
+                JsString.make("Math.floor("), 
+                arg.code()), 
+                JsString.make(")")),
+            Types.basic.integer)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgument(call, Types.basic.real);
+    RETURN makeSymbol(makeStd(JsString.make("FLOOR"), call))
+END makeFloor;
+
+PROCEDURE makeFlt(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        value: Code.PConst;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        value := arg.constValue();
+        IF value # NIL THEN
+            value := Code.makeRealConst(FLT(value^(Code.IntConst).value));
+        END;
+        RETURN Code.makeExpressionWithPrecedence(
+                arg.code(), 
+                Types.basic.real,
+                NIL,
+                value,
+                arg.maxPrecedence())
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(JsString.make("FLT"), call))
+END makeFlt;
+
+PROCEDURE bitShiftImpl(name: ARRAY OF CHAR; op: BinaryOp): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+            name: JsString.Type;
+            op: BinaryOp
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        x, y: Code.PExpression;
+    BEGIN
+        checkArguments(args, SELF.args);
+        ASSERT(JsArray.len(args) = 2);
+        x := nthArgument(args, 0);
+        y := nthArgument(args, 1);
+        RETURN SELF.op(x, y, cx)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    call.name := JsString.make(name);
+    call.op := op;
+    hasArgument(call, Types.basic.integer);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(call.name, call))
+END bitShiftImpl;
+
+PROCEDURE makeOrd(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+        argType: Types.PType;
+        value: Code.PConst;
+        code: JsString.Type;
+        ch: CHAR;
+        result: Code.PExpression;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        argType := arg.type();
+        IF (argType = Types.basic.ch) OR (argType = Types.basic.set) THEN
+            value := arg.constValue();
+            IF (value # NIL) & (argType = Types.basic.set) THEN
+                value := Code.makeIntConst(ORD(value^(Code.SetConst).value));
+            END;
+            result := Code.makeExpression(arg.code(), Types.basic.integer, NIL, value);
+        ELSIF argType = Types.basic.bool THEN
+            code := JsString.concat(
+                Code.adjustPrecedence(arg, Precedence.conditional), 
+                JsString.make(" ? 1 : 0"));
+            result := Code.makeExpressionWithPrecedence(
+                code, 
+                Types.basic.integer, 
+                NIL, 
+                arg.constValue(), 
+                Precedence.conditional);
+        ELSIF (argType IS Types.PString) 
+            & (Types.stringAsChar(argType(Types.PString)^, ch)) THEN
+            result := Code.makeExpression(
+                JsString.fromInt(ORD(ch)), 
+                Types.basic.integer,
+                NIL,
+                Code.makeIntConst(ORD(ch)));
+        ELSE
+            Errors.raise(JsString.concat(JsString.concat(
+                JsString.make("ORD function expects CHAR or BOOLEAN or SET as an argument, got '"),
+                argType.description()),
+                JsString.make("'")));
+        END;
+        RETURN result
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgumentWithCustomType(call);
+    RETURN makeSymbol(makeStd(JsString.make("ORD"), call))
+END makeOrd;
+
+PROCEDURE makeChr(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        arg: Code.PExpression;
+    BEGIN
+        arg := checkSingleArgument(args, SELF.args);
+        RETURN Code.makeSimpleExpression(arg.code(), Types.basic.ch)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(JsString.make("CHR"), call))
+END makeChr;
+
+PROCEDURE makePack(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        x, y: Code.PExpression;
+    BEGIN
+        checkArguments(args, SELF.args);
+        x := nthArgument(args, 0);
+        y := nthArgument(args, 1);
+        RETURN Code.makeSimpleExpression(
+            Operator.mulInplace(x, Operator.pow2(y), cx),
+            NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasVarArgument(call, Types.basic.real);
+    hasArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(JsString.make("PACK"), call))
+END makePack;
+
+PROCEDURE makeUnpk(): Symbols.PSymbol;
+    TYPE
+        CallImpl = RECORD(StdCall)
+        END;
+    VAR
+        call: POINTER TO CallImpl;
+
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    VAR
+        x, y: Code.PExpression;
+    BEGIN
+        checkArguments(args, SELF.args);
+        x := nthArgument(args, 0);
+        y := nthArgument(args, 1);
+        RETURN Code.makeSimpleExpression(
+            JsString.concat(JsString.concat(
+                Operator.assign(y, Operator.log2(x), cx),
+                JsString.make("; ")),
+                Operator.divInplace(x, Operator.pow2(y), cx)),
+            NIL)
+    END CallImpl.make;
+BEGIN
+    NEW(call);
+    initStdCall(call);
+    hasVarArgument(call, Types.basic.real);
+    hasVarArgument(call, Types.basic.integer);
+    RETURN makeSymbol(makeStd(JsString.make("UNPK"), call))
+END makeUnpk;
+
+PROCEDURE dumpProcArgs(proc: Type): JsString.Type;
+VAR
+    result: JsString.Type;
+    len: INTEGER;
+    i: INTEGER;
+    arg: Object.PType;
+BEGIN
+    len := JsArray.len(proc.mArgs);
+    IF len = 0 THEN
+        IF proc.mResult # NIL THEN
+            result := JsString.make("()");
+        ELSE
+            result := JsString.makeEmpty();
+        END;
+    ELSE
+        result := JsString.make("(");
+        FOR i := 0 TO len - 1 DO
+            IF i # 0 THEN
+                result := JsString.concat(result, JsString.make(", "));
+            END;
+            arg := JsArray.at(proc.mArgs, i);
+            result := JsString.concat(
+                result, 
+                arg(Types.PProcedureArgument).type.description());
+        END;
+        result := JsString.concat(result, JsString.make(")"));
+    END;
+    RETURN result
+END dumpProcArgs;
+
+PROCEDURE Type.description(): JsString.Type;
+VAR
+    result: JsString.Type;
+BEGIN
+    result := Types.typeName(SELF);
+    IF result = NIL THEN
+        result := JsString.concat(
+            JsString.make("PROCEDURE"), 
+            dumpProcArgs(SELF));
+        IF SELF.mResult # NIL THEN
+            result := JsString.concat(JsString.concat(
+                result, 
+                JsString.make(": ")), 
+                SELF.mResult.description());
+        END;
+    END;
+    RETURN result
+END Type.description;
+
+PROCEDURE Type.callGenerator(cx: Context.PType; id: JsString.Type): PCallGenerator;
+    RETURN makeProcCallGenerator(cx, id, SELF)
+END Type.callGenerator;
+
+PROCEDURE Type.define(args: JsArray.Type; result: Types.PType);
+BEGIN
+    SELF.mArgs := args;
+    SELF.mResult := result;
+END Type.define;
+
+PROCEDURE Type.args(): JsArray.Type;
+    RETURN SELF.mArgs
+END Type.args;
+
+PROCEDURE Type.result(): Types.PType;
+    RETURN SELF.mResult
+END Type.result;
+
+PROCEDURE make*(name: JsString.Type): PType;
+VAR
+    result: PType;
+BEGIN
+    NEW(result);
+    result.name := name;
+    RETURN result
+END make;
+
+BEGIN
+    predefined := JsArray.make();
+    JsArray.add(predefined, makeNew());
+    JsArray.add(predefined, makeLen());
+    JsArray.add(predefined, makeOdd());
+    JsArray.add(predefined, makeAssert());
+    JsArray.add(predefined, setBitImpl("INCL", inclOp));
+    JsArray.add(predefined, setBitImpl("EXCL", exclOp));
+    JsArray.add(predefined, incImpl("INC", "++", incOp));
+    JsArray.add(predefined, incImpl("DEC", "--", decOp));
+    JsArray.add(predefined, makeAbs());
+    JsArray.add(predefined, makeFloor());
+    JsArray.add(predefined, makeFlt());
+    JsArray.add(predefined, bitShiftImpl("LSL", Operator.lsl));
+    JsArray.add(predefined, bitShiftImpl("ASR", Operator.asr));
+    JsArray.add(predefined, bitShiftImpl("ROR", Operator.ror));
+    JsArray.add(predefined, makeOrd());
+    JsArray.add(predefined, makeChr());
+    JsArray.add(predefined, makePack());
+    JsArray.add(predefined, makeUnpk());
+END Procedure.

+ 10 - 9
src/ob/Types.ob

@@ -17,7 +17,7 @@ TYPE
     PType* = POINTER TO Type;
 
     StorageType = RECORD(Type)    
-        PROCEDURE initializer(cx: Context.Type): JsString.Type
+        PROCEDURE initializer*(cx: Context.Type): JsString.Type
     END;
 
     TypeId* = RECORD(Id)
@@ -78,8 +78,8 @@ TYPE
 
     PString* = POINTER TO String;
 
-    NamedType = RECORD(StorageType)
-        name: JsString.Type
+    NamedType* = RECORD(StorageType)
+        name*: JsString.Type
     END;
 
     Array* = RECORD(NamedType)
@@ -99,11 +99,14 @@ TYPE
     PPointer* = POINTER TO Pointer;
 
     Procedure* = RECORD(NamedType)
+    END;
+    PProcedure* = POINTER TO Procedure;
+
+    DefinedProcedure* = RECORD(Procedure)
         PROCEDURE args*(): JsArray.Type;
         PROCEDURE result*(): PType
     END;
-
-    PProcedure* = POINTER TO Procedure;
+    PDefinedProcedure* = POINTER TO DefinedProcedure;
 
     ProcedureArgument* = RECORD (Object.Type)
         PROCEDURE description(): JsString.Type;
@@ -114,8 +117,6 @@ TYPE
 
     PProcedureArgument* = POINTER TO ProcedureArgument;
 
-
-
     BasicType = RECORD(NamedType)
         mInitializer: JsString.Type
     END;
@@ -141,7 +142,7 @@ TYPE
     
     NonExportedRecord* = RECORD(Record)
     END;
-    PNonExportedRecord = POINTER TO NonExportedRecord;
+    PNonExportedRecord* = POINTER TO NonExportedRecord;
 
     Nil = RECORD(Type)
     END;
@@ -570,7 +571,7 @@ BEGIN
 END makeProcedureArgument;
 
 PROCEDURE Module.idType(): JsString.Type;
-    RETURN JsString.make("module")
+    RETURN JsString.make("MODULE")
 END Module.idType;
 
 PROCEDURE makeTypeId*(type: PType): PTypeId;

+ 0 - 592
src/procedure.js

@@ -1,592 +0,0 @@
-"use strict";
-
-var Cast = require("js/Cast.js");
-var Class = require("rtl.js").Class;
-var Code = require("js/Code.js");
-var Errors = require("js/Errors.js");
-var op = require("js/Operator.js");
-var precedence = require("js/CodePrecedence.js");
-var Symbol = require("js/Symbols.js");
-var Type = require("js/Types.js");
-
-var basicTypes = Type.basic();
-
-var Arg = Type.ProcedureArgument;
-var makeArg = Type.makeProcedureArgument;
-
-var CheckArgumentResult = Arg.extend({
-    init: function(type, isVar, convert){
-        Arg.prototype.init.call(this);
-        this.type = type;
-        this.isVar = isVar;
-        this.convert = convert;
-    }
-});
-
-var ProcCallGenerator = Class.extend({
-    init: function ProcCallGenerator(context, id, type){
-        this.__context = context;
-        this.__id = id;
-        this.__type = type;
-        this.__argumentsCount = 0;
-        this.__code = Code.makeSimpleGenerator();
-        this.writeCode(this.prolog());
-    },
-    //id: function(){return this.__id;},
-    context: function(){return this.__context;},
-    handleArgument: function(e){
-        var pos = this.__argumentsCount++;
-        var isVarArg = false;
-        var convert;
-        if (this.__type.args){
-            var expectedArguments = this.__type.args();
-            if (pos >= expectedArguments.length )
-                // ignore, handle error after parsing all arguments
-                return;
-            
-            var arg = this.checkArgument(pos, e);
-            isVarArg = arg.isVar;
-            convert = arg.convert;
-        }
-        this.writeArgumentCode(e, pos, isVarArg, convert);
-    },
-    writeArgumentCode: function(e, pos, isVar, convert){
-        e = (isVar ? Code.refExpression : Code.derefExpression)(e);
-        var code = (convert ? convert(this.__context, e) : e).code();
-        var prefix = pos ? ", " : "";
-        this.writeCode(prefix + code);
-    },
-    end: function(){
-        if (this.__type.args)
-            this.checkArgumentsCount(this.__argumentsCount);
-        return this.callExpression();
-    },
-    callExpression: function(){
-        this.writeCode(this.epilog());
-        return Code.makeExpression(this.__code.result(),
-                                   this.resultType());
-    },
-    resultType: function(){return this.__type ? this.__type.result() : undefined;},
-    prolog: function(){return this.__id + "(";},
-    checkArgumentsCount: function(count){
-        var procArgs = this.__type.args();
-        if (count != procArgs.length)
-            throw new Errors.Error(procArgs.length + " argument(s) expected, got "
-                                 + this.__argumentsCount);
-    },
-    checkArgument: function(pos, e){
-        var arg = this.__type.args()[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, op.castOperations());
-            if (!castOperation)
-                throw new Errors.Error(
-                      "type mismatch for argument " + (pos + 1) + ": '" + type.description()
-                    + "' cannot be converted to '" + expectType.description() + "'");
-            castOperation = castOperation.make.bind(castOperation);
-            if (arg.isVar && expectType != type && Type.isInt(type))
-                throw new Errors.Error(
-                      "type mismatch for argument " + (pos + 1) + ": cannot pass '" 
-                    + type.description() + "' as VAR parameter of type '" + 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();
-            if (info instanceof Type.Const)
-                throw new Errors.Error("constant cannot be used as VAR parameter");
-            if (Type.isVariableReadOnly(info))
-                throw new Errors.Error(info.idType() + " cannot be used as VAR parameter");
-        }
-        return new CheckArgumentResult(arg.type, arg.isVar, castOperation);
-    },
-    epilog: function(){return ")";},
-    writeCode: function(s){this.__code.write(s);}
-});
-
-var DefinedProc = Type.Procedure.extend({
-    init: function DefinedProc(name){
-        Type.Procedure.prototype.init.call(this);
-        Type.initProcedure(this, name);
-        this.__arguments = undefined;
-        this.__result = undefined;
-    },
-    define: function(args, result){
-        this.__arguments = args;
-        this.__result = result;
-    },
-    args: function(){return this.__arguments;},
-    result: function(){return this.__result;},
-    description: function(){
-        var name = Type.typeName(this);
-        if (name)
-            return name;
-        return 'PROCEDURE' + this.__dumpProcArgs()
-            + (this.__result ? ": " + Type.typeName(this.__result) : "");
-        },
-    callGenerator: function(context, id){
-        return new ProcCallGenerator(context, id, this);
-    },
-    __dumpProcArgs: function(){
-        if (!this.__arguments.length)
-            return this.__result ? "()" : "";
-        
-        var result = "(";
-        for(var i = 0; i < this.__arguments.length; ++i){
-            if (i)
-                result += ", ";
-            result += this.__arguments[i].description();
-        }
-        result += ")";
-        return result;
-    }
-});
-
-var Std = Type.Procedure.extend({
-    init: function Std(name, args, result, callGeneratorFactory){
-        Type.Procedure.prototype.init.call(this);
-        Type.initProcedure(this, name);
-        this.__arguments = args;
-        this.__result = result;
-        this.__callGeneratorFactory = callGeneratorFactory;
-    },
-    description: function(){return "standard procedure " + Type.typeName(this);},
-    args: function(){return this.__arguments;},
-    result: function(){return this.__result;},
-    callGenerator: function(context, id){
-        return this.__callGeneratorFactory(context, id, this);
-    }
-});
-
-var ExpCallGenerator = ProcCallGenerator.extend({
-    init: function ExpCallGenerator(context, id, type){
-        ProcCallGenerator.prototype.init.call(this, context, id, type);
-        this.__args = [];
-    },
-    prolog: function(){return "";},
-    epilog: function(){return "";},
-    writeArgumentCode: function(){},
-    checkArgument: function(pos, e){
-        this.__args.push(e);
-        return ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
-    },
-    args: function(){return this.__args;}
-});
-
-var TwoArgToOperatorProcCallGenerator = ExpCallGenerator.extend({
-    init: function TwoArgToOperatorProcCallGenerator(context, id, type, operator){
-        ExpCallGenerator.prototype.init.call(this, context, id, type);
-        this.__operator = operator;
-    },
-    callExpression: function(){
-        var args = this.args();
-        return Code.makeExpression(this.__operator(args[0], args[1]));
-    }
-});
-
-function makeProcSymbol(name, proc){
-    return Symbol.makeSymbol(name, Type.makeProcedure(proc));
-}
-
-function setBitImpl(name, bitOp){
-    var args = [makeArg(basicTypes.set, true),
-                makeArg(basicTypes.integer, false)];
-    function operator(x, y){
-        var value = y.constValue();
-        var valueCode;
-        if (!value)
-            valueCode = op.lsl(Code.makeExpression("1"), y).code();
-        else {
-            if (value.value < 0 || value.value > 31)
-                throw new Errors.Error("value (0..31) expected as a second argument of " + name + ", got " + value.value);
-            var comment = "bit: " + (y.isTerm() ? value.value : Code.adjustPrecedence(y, precedence.shift));
-            value = 1 << value.value;
-            valueCode = value + "/*" + comment + "*/";
-        }
-        return bitOp(Code.adjustPrecedence(x, precedence.assignment), valueCode);
-    }
-    var proc = new Std(
-        name,
-        args,
-        undefined,
-        function(context, id, type){
-            return new TwoArgToOperatorProcCallGenerator(
-                context, id, type, operator);
-            });
-    var symbol = makeProcSymbol(name, proc);
-    return symbol;
-}
-
-function incImpl(name, unary, op){
-    var args = [makeArg(basicTypes.integer, true),
-                makeArg(basicTypes.integer, false)];
-    function operator(x, y){
-        if (!y)
-            return unary + x.code();
-
-        var value = y.constValue();
-        var valueCode;
-        if (!value)
-            valueCode = y.code();
-        else {
-            var comment = y.isTerm() ? "" : "/*" + y.code() + "*/";
-            valueCode = value.value + comment;
-        }
-        return op(x.code(), valueCode);
-    }
-    var CallGenerator = TwoArgToOperatorProcCallGenerator.extend({
-        init: function IncDecProcCallGenerator(context, id, type, operator){
-            TwoArgToOperatorProcCallGenerator.prototype.init.call(this, context, id, type, operator);
-        },
-        checkArgumentsCount: function(count){
-            checkVariableArgumentsCount(1, 2, count);
-        }
-    });
-    var proc = new Std(
-        name,
-        args,
-        undefined,
-        function(context, id, type){
-            return new CallGenerator(
-                context, id, type, operator);
-            });
-    var symbol = makeProcSymbol(name, proc);
-    return symbol;
-}
-
-function bitShiftImpl(name, op){
-    var CallGenerator = ExpCallGenerator.extend({
-        init: function ShiftProcCallGenerator(context, id, type){
-            ExpCallGenerator.prototype.init.call(this, context, id, type);
-        },
-        callExpression: function(){
-            var args = this.args();
-            return op(args[0], args[1]);
-        }
-    });
-    var args = [makeArg(basicTypes.integer, false),
-                makeArg(basicTypes.integer, false)
-               ];
-    var proc = new Std(
-        name,
-        args,
-        basicTypes.integer,
-        function(context, id, type){
-            return new CallGenerator(context, id, type);
-        });
-    var symbol = makeProcSymbol(name, proc);
-    return symbol;
-}
-
-function checkVariableArgumentsCount(min, max, actual){
-    if (actual < min)
-        throw new Errors.Error("at least " + min + " argument expected, got " + actual);
-    if (actual > max )
-        throw new Errors.Error("at most " + max + " arguments expected, got " + actual);
-}
-
-exports.predefined = [
-    function(){
-        var NewProcCallGenerator = ProcCallGenerator.extend({
-            init: function NewProcCallGenerator(context, id, type){
-                ProcCallGenerator.prototype.init.call(this, context, id, type);
-                this.__baseType = undefined;
-            },
-            prolog: function(id){return "";},
-            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.typeName(type) + "'");
-                this.__baseType = Type.pointerBase(type);
-                if (this.__baseType instanceof Type.NonExportedRecord)
-                    throw new Errors.Error("non-exported RECORD type cannot be used in NEW");
-                return new CheckArgumentResult(type, false);
-            },
-            epilog: function(){
-                return " = " + this.__baseType.initializer(this.context());
-            }
-        });
-
-        var name = "NEW";
-        var args = [makeArg(undefined, true)];
-        var type = new Std(
-            name,
-            args,
-            undefined,
-            function(context, id, type){
-                return new NewProcCallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol(name, type);
-        return symbol;
-    }(),
-    function(){
-        var LenProcCallGenerator = ProcCallGenerator.extend({
-            init: function LenProcCallGenerator(context, id, type){
-                ProcCallGenerator.prototype.init.call(this, context, id, type);
-            },
-            prolog: function(id){return "";},
-            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
-                return ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
-            },
-            epilog: function(){return ".length";}
-        });
-
-        var name = "LEN";
-        var args = [makeArg(Type.makeArray("ARRAY OF any type", undefined, undefined, 0), false)];
-        var type = new Std(
-            name,
-            args,
-            basicTypes.integer,
-            function(context, id, type){
-                return new LenProcCallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol(name, type);
-        return symbol;
-    }(),
-    function(){
-        var CallGenerator = ExpCallGenerator.extend({
-            init: function OddProcCallGenerator(context, id, type){
-                ExpCallGenerator.prototype.init.call(this, context, id, type);
-            },
-            callExpression: function(){
-                var e = this.args()[0];
-                var code = Code.adjustPrecedence(e, precedence.bitAnd);
-                return Code.makeExpressionWithPrecedence(
-                    code + " & 1", basicTypes.bool, undefined, e.constValue(), precedence.bitAnd);
-            }
-        });
-        var name = "ODD";
-        var args = [makeArg(basicTypes.integer, false)];
-        var type = new Std(
-            "ODD",
-            args,
-            basicTypes.bool,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol(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() + "(";}
-        });
-
-        var args = [makeArg(basicTypes.bool)];
-        var proc = new Std(
-            "ASSERT",
-            args,
-            undefined,
-            function(context, id, type){
-                return new AssertProcCallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol("ASSERT", proc);
-        return symbol;
-    }(),
-    setBitImpl("INCL", function(x, y){return x + " |= " + y;}),
-    setBitImpl("EXCL", function(x, y){return x + " &= ~(" + y + ")";}),
-    incImpl("INC", "++", function(x, y){return x + " += " + y;}),
-    incImpl("DEC", "--", function(x, y){return x + " -= " + y;}),
-    function(){
-        var CallGenerator = ProcCallGenerator.extend({
-            init: function AbsProcCallGenerator(context, id, type){
-                ProcCallGenerator.prototype.init.call(this, context, id, type);
-                this.__argType = undefined;
-            },
-            prolog: function(){return "Math.abs(";},
-            checkArgument: function(pos, e){
-                var type = e.type();
-                if (Type.numeric().indexOf(type) == -1)
-                    throw new Errors.Error("type mismatch: expected numeric type, got '" +
-                                           type.description() + "'");
-                this.__argType = type;
-                return ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
-            },
-            resultType: function(){return this.__argType;}
-        });
-        var args = [makeArg(undefined, false)];
-        var proc = new Std(
-            "ABS",
-            args,
-            undefined,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol("ABS", proc);
-        return symbol;
-    }(),
-    function(){
-        var CallGenerator = ProcCallGenerator.extend({
-            init: function FloorProcCallGenerator(context, id, type){
-                ProcCallGenerator.prototype.init.call(this, context, id, type);
-            },
-            prolog: function(){return "Math.floor(";}
-        });
-        var args = [makeArg(basicTypes.real, false)];
-        var proc = new Std(
-            "FLOOR",
-            args,
-            basicTypes.integer,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol("FLOOR", proc);
-        return symbol;
-    }(),
-    function(){
-        var CallGenerator = ExpCallGenerator.extend({
-            init: function FltProcCallGenerator(context, id, type){
-                ExpCallGenerator.prototype.init.call(this, context, id, type);
-            },
-            callExpression: function(){
-                var e = this.args()[0];
-                var value = e.constValue();
-                return Code.makeExpressionWithPrecedence(
-                    e.code(), 
-                    basicTypes.real, 
-                    undefined, 
-                    value ? Code.makeRealConst(value.value) : value, 
-                    e.maxPrecedence()
-                    );
-            }
-        });
-        var args = [makeArg(basicTypes.integer, false)];
-        var proc = new Std(
-            "FLT",
-            args,
-            basicTypes.real,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol("FLT", proc);
-        return symbol;
-    }(),
-    bitShiftImpl("LSL", op.lsl),
-    bitShiftImpl("ASR", op.asr),
-    bitShiftImpl("ROR", op.ror),
-    function(){
-        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, e){
-                var type = e.type();
-                if (type == basicTypes.ch || type == basicTypes.set)
-                    this.__callExpression = Code.makeExpression(
-                        e.code(), basicTypes.integer, undefined, e.constValue());
-                else if (type == basicTypes.bool){
-                    var code = Code.adjustPrecedence(e, precedence.conditional) + " ? 1 : 0";
-                    var value = e.constValue();
-                    if (value)
-                        value = Code.makeIntConst(value.value ? 1 : 0);
-                    this.__callExpression = Code.makeExpressionWithPrecedence(
-                        code, basicTypes.integer, undefined, value, precedence.conditional);
-                }
-                else if (type instanceof Type.String){
-                    var ch;
-                    if (Type.stringAsChar(type, {set: function(v){ch = v;}}))
-                        this.__callExpression = Code.makeExpression(
-                            "" + ch, basicTypes.integer);
-                }
-                
-                if (this.__callExpression)
-                    return new CheckArgumentResult(type, false);
-
-                throw new Errors.Error(
-                      "ORD function expects CHAR or BOOLEAN or SET as an argument, got '" + type.description()+ "'");
-            },
-            callExpression: function(){return this.__callExpression;}
-        });
-        var name = "ORD";
-        //var argType = new basicTypes("CHAR or BOOLEAN or SET");
-        var args = [makeArg(undefined, false)];
-        var type = new Std(
-            name,
-            args,
-            basicTypes.integer,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol(name, type);
-        return symbol;
-    }(),
-    function(){
-        var CallGenerator = ExpCallGenerator.extend({
-            init: function ChrProcCallGenerator(context, id, type){
-                ExpCallGenerator.prototype.init.call(this, context, id, type);
-            },
-            callExpression: function(){
-                return Code.makeExpression(this.args()[0].code(), basicTypes.ch);
-            }
-        });
-        var name = "CHR";
-        var type = new Std(
-            name,
-            [makeArg(basicTypes.integer, false)],
-            basicTypes.ch,
-            function(context, id, type){
-                return new CallGenerator(context, id, type);
-            });
-        var symbol = makeProcSymbol(name, type);
-        return symbol;
-    }(),
-    function(){
-        var args = [makeArg(basicTypes.real, true),
-                    makeArg(basicTypes.integer, false)];
-        function operator(x, y){
-            return op.mulInplace(x, op.pow2(y));
-        }
-        var name = "PACK";
-        var proc = new Std(
-            name,
-            args,
-            undefined,
-            function(context, id, type){
-                return new TwoArgToOperatorProcCallGenerator(
-                    context, id, type, operator);
-                });
-        var symbol = makeProcSymbol(name, proc);
-        return symbol;
-    }(),
-    function(){
-        var args = [makeArg(basicTypes.real, true),
-                    makeArg(basicTypes.integer, true)];
-        function operator(x, y){
-            return op.assign(y, op.log2(x)) +
-                   "; " +
-                   op.divInplace(x, op.pow2(y));
-        }
-        var name = "UNPK";
-        var proc = new Std(
-            name,
-            args,
-            undefined,
-            function(context, id, type){
-                return new TwoArgToOperatorProcCallGenerator(
-                    context, id, type, operator);
-                });
-        var symbol = makeProcSymbol(name, proc);
-        return symbol;
-    }()
-    ];
-
-exports.CallGenerator = ProcCallGenerator;
-exports.Type = DefinedProc;
-exports.Std = Std;

+ 2 - 0
src/rtl.js

@@ -27,6 +27,8 @@ Class.extend = function extend(methods){
 var impl = {
     extend: Class.extend,
     typeGuard: function(from, to){
+        if (!from)
+            return from;
         if (!(from instanceof to)){
             var fromStr;
             var toStr;

+ 2 - 2
src/scope.js

@@ -2,7 +2,7 @@
 
 var Class = require("rtl.js").Class;
 var Errors = require("js/Errors.js");
-var Procedure = require("procedure.js");
+var Procedure = require("js/Procedure.js");
 var Symbol = require("js/Symbols.js");
 var Type = require("js/Types.js");
 
@@ -15,7 +15,7 @@ var stdSymbols = function(){
         symbols[name] = Symbol.makeSymbol(name, Type.makeTypeId(type));
     }
     
-    var predefined = Procedure.predefined;
+    var predefined = Procedure.predefined();
     for(var i = 0; i < predefined.length; ++i){
         var s = predefined[i];
         symbols[s.id()] = s;

+ 2 - 0
test/expected/cast.js

@@ -14,6 +14,8 @@ var RTL$ = {
         return result;
     },
     typeGuard: function (from, to){
+        if (!from)
+            return from;
         if (!(from instanceof to)){
             var fromStr;
             var toStr;

+ 2 - 0
test/expected/modules.js

@@ -14,6 +14,8 @@ var RTL$ = {
         return result;
     },
     typeGuard: function (from, to){
+        if (!from)
+            return from;
         if (!(from instanceof to)){
             var fromStr;
             var toStr;

+ 30 - 4
test/test_unit.js

@@ -327,12 +327,13 @@ return {
 "NEW": testWithContext(
     context(grammar.statement,
             "TYPE P = POINTER TO RECORD END;"
-            + "VAR p: P; i: INTEGER;"
+            + "VAR p: P; i: INTEGER; r: RECORD END;"
             + "PROCEDURE proc(): P; RETURN NIL END proc;"
             ),
     pass("NEW(p)"),
     fail(["NEW.NEW(p)", "cannot designate 'standard procedure NEW'"],
          ["NEW(i)", "POINTER variable expected, got 'INTEGER'"],
+         ["NEW(r)", "POINTER variable expected, got 'anonymous RECORD'"],
          ["NEW()", "1 argument(s) expected, got 0"],
          ["NEW(p, p)", "1 argument(s) expected, got 2"],
          ["NEW(proc())", "expression cannot be used as VAR parameter"])
@@ -400,6 +401,15 @@ return {
          ["b := ODD(TRUE)", "type mismatch for argument 1: 'BOOLEAN' cannot be converted to 'INTEGER'"]
          )
 ),
+"ODD const expression": testWithGrammar(
+    grammar.typeDeclaration,
+    pass("T = ARRAY ORD(ODD(1)) OF INTEGER",
+         "T = ARRAY ORD(ODD(3)) OF INTEGER"
+         ),
+    fail(["T = ARRAY ORD(ODD(0)) OF INTEGER", "array size must be greater than 0, got 0"],
+         ["T = ARRAY ORD(ODD(2)) OF INTEGER", "array size must be greater than 0, got 0"]
+         )
+),
 "ORD": testWithContext(
     context(grammar.statement, "VAR ch: CHAR; i: INTEGER; b: BOOLEAN;"),
     pass("i := ORD(ch)",
@@ -413,6 +423,20 @@ return {
          ["i := ORD(\"abc\")", "ORD function expects CHAR or BOOLEAN or SET as an argument, got 'multi-character string'"]
          )
 ),
+"ORD const expression": testWithGrammar(
+    grammar.typeDeclaration,
+    pass("T = ARRAY ORD({0}) OF INTEGER",
+         "T = ARRAY ORD({0}) + 1 OF INTEGER",
+         "T = ARRAY ORD(TRUE) OF INTEGER",
+         "T = ARRAY ORD(TRUE) + 1 OF INTEGER",
+         "T = ARRAY ORD(\"A\") OF INTEGER",
+         "T = ARRAY ORD(\"A\") + 1 OF INTEGER"
+         ),
+    fail(["T = ARRAY ORD({}) OF INTEGER", "array size must be greater than 0, got 0"],
+         ["T = ARRAY ORD(FALSE) OF INTEGER", "array size must be greater than 0, got 0"],
+         ["T = ARRAY ORD(0X) OF INTEGER", "array size must be greater than 0, got 0"]
+         )
+),
 "CHR": testWithContext(
     context(grammar.statement, "VAR i: INTEGER; ch: CHAR;"),
     pass("ch := CHR(i)"),
@@ -891,7 +915,7 @@ return {
     pass("ASSERT(TRUE)"),
     fail(["ASSERT()", "1 argument(s) expected, got 0"],
          ["ASSERT(TRUE, 123)", "1 argument(s) expected, got 2"],
-         ["ASSERT(123, TRUE)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'BOOLEAN'"])
+         ["ASSERT(123)", "type mismatch for argument 1: 'INTEGER' cannot be converted to 'BOOLEAN'"])
     ),
 "imported module without exports": testWithModule(
     "MODULE test; END test.",
@@ -899,7 +923,9 @@ return {
     fail(["MODULE m; IMPORT test; BEGIN test.p(); END m.",
           "identifier 'p' is not exported by module 'test'"],
          ["MODULE m; IMPORT t := test; BEGIN t.p(); END m.",
-          "identifier 'p' is not exported by module 'test'"]
+          "identifier 'p' is not exported by module 'test'"],
+         ["MODULE m; IMPORT test; BEGIN test(); END m.",
+          "PROCEDURE expected, got 'MODULE'"]
         )),
 "import record type": testWithModule(
     "MODULE test; TYPE T* = RECORD f*: INTEGER; notExported: BOOLEAN END; END test.",
@@ -1040,7 +1066,7 @@ return {
          "PROCEDURE p(VAR a: ARRAY OF BOOLEAN): INTEGER; RETURN LEN(a) END p",
          "PROCEDURE p(): INTEGER; RETURN LEN(\"abc\") END p"),
     fail(["PROCEDURE p(a: ARRAY OF INTEGER): INTEGER; RETURN LEN(a[0]) END p",
-          "type mismatch for argument 1: 'INTEGER' cannot be converted to 'ARRAY OF any type'"])
+          "ARRAY or string is expected as an argument of LEN, got 'INTEGER'"])
     ),
 "array expression": testWithGrammar(
     grammar.procedureBody,

+ 4 - 0
test/test_unit_eberon.js

@@ -35,6 +35,10 @@ exports.suite = {
           "cannot instantiate 'T2' because it has abstract method(s): p1, p2"],
          ["PROCEDURE p(); VAR p: POINTER TO T; BEGIN NEW(p); END p;",
           "cannot instantiate 'T' because it has abstract method(s): p"],
+         ["PROCEDURE p(); TYPE LocalT = RECORD(T) END; VAR r: LocalT; END p;",
+          "cannot instantiate 'LocalT' because it has abstract method(s): p"],
+         ["PROCEDURE p(); TYPE LocalT = RECORD(T) END; VAR p: POINTER TO LocalT; BEGIN NEW(p) END p;",
+          "cannot instantiate 'LocalT' because it has abstract method(s): p"],
          ["VAR r: D;",
           "cannot instantiate 'D' because it has abstract method(s): p"],
          ["PROCEDURE p(); VAR p: POINTER TO D; BEGIN NEW(p); END p;",