Browse Source

STRING type - first approach

Vladislav Folts 11 years ago
parent
commit
d618f06d1a
50 changed files with 1426 additions and 769 deletions
  1. 3 3
      browser/oberonjs.html
  2. 1 1
      build.py
  3. 177 125
      src/context.js
  4. 20 0
      src/eberon/EberonCast.ob
  5. 63 0
      src/eberon/EberonOperator.ob
  6. 7 0
      src/eberon/EberonString.ob
  7. 63 2
      src/eberon/eberon_context.js
  8. 23 9
      src/eberon/eberon_grammar.js
  9. 20 0
      src/eberon/js/EberonCast.js
  10. 69 0
      src/eberon/js/EberonOperator.js
  11. 4 0
      src/eberon/js/EberonString.js
  12. 11 3
      src/grammar.js
  13. 38 25
      src/js/Cast.js
  14. 17 2
      src/js/Code.js
  15. 2 17
      src/js/Context.js
  16. 9 0
      src/js/Language.js
  17. 8 0
      src/js/LanguageContext.js
  18. 13 0
      src/js/OberonRtl.js
  19. 148 128
      src/js/Operator.js
  20. 38 34
      src/js/Procedure.js
  21. 20 14
      src/js/Scope.js
  22. 7 0
      src/js/ScopeBase.js
  23. 2 2
      src/js/Symbols.js
  24. 5 3
      src/js/Types.js
  25. 19 7
      src/nodejs.js
  26. 44 28
      src/ob/Cast.ob
  27. 18 5
      src/ob/Code.ob
  28. 4 20
      src/ob/Context.ob
  29. 13 0
      src/ob/Language.ob
  30. 10 0
      src/ob/LanguageContext.ob
  31. 13 0
      src/ob/OberonRtl.ob
  32. 150 137
      src/ob/Operator.ob
  33. 90 76
      src/ob/Procedure.ob
  34. 21 18
      src/ob/Scope.ob
  35. 11 0
      src/ob/ScopeBase.ob
  36. 5 5
      src/ob/Symbols.ob
  37. 7 7
      src/ob/Types.ob
  38. 1 1
      src/oberon/oberon_context.js
  39. 21 9
      src/oberon/oberon_grammar.js
  40. 29 10
      src/oc.js
  41. 9 3
      src/oc_nodejs.js
  42. 15 0
      test/expected/eberon/string.js
  43. 19 0
      test/input/eberon/string.ob
  44. 1 1
      test/test_compile.cmd
  45. 20 20
      test/test_compile.js
  46. 1 1
      test/test_unit.cmd
  47. 36 22
      test/test_unit.js
  48. 27 24
      test/test_unit_common.js
  49. 46 5
      test/test_unit_eberon.js
  50. 28 2
      test/test_unit_oberon.js

+ 3 - 3
browser/oberonjs.html

@@ -81,11 +81,11 @@ END test.
         var errors = "";
         var start = new Date();
         try {
-            var grammar = require(
+            var language = require(
                 document.getElementById("eberon").checked
                     ? "eberon/eberon_grammar.js"
-                    : "oberon/oberon_grammar.js").grammar;
-            result = require("oc.js").compile(src, grammar, function(e){
+                    : "oberon/oberon_grammar.js").language;
+            result = require("oc.js").compile(src, language, function(e){
                 errors += e;
             });
             }

+ 1 - 1
build.py

@@ -76,7 +76,7 @@ def build(out, use_git):
 
     link(['oc.js', 'oberon/oberon_grammar.js', 'eberon/eberon_grammar.js'],
          os.path.join(out, 'oc.js'),
-         ['src', 'src/js'],
+         ['src', 'src/eberon'],
          version)
     copy('browser/oberonjs.html', out)
     for d in ['codemirror', 'jslibs']:

+ 177 - 125
src/context.js

@@ -56,11 +56,9 @@ function checkTypeMatch(from, to){
         throwTypeMismatch(from, to);
 }
 
-function checkImplicitCast(from, to){
-    var result = Cast.implicit(from, to, castOperations);
-    if (!result)
+function checkImplicitCast(types, from, to){
+    if (types.implicitCast(from, to, false, castOperations, {set: function(){}}))
         throwTypeMismatch(from, to);
-    return result.make.bind(result);
 }
 
 function promoteTypeInExpression(e, type){
@@ -82,7 +80,7 @@ function promoteExpressionType(context, left, right){
     if (!rightType)
         return;
 
-    checkImplicitCast(rightType, leftType);
+    checkImplicitCast(context.language().types, rightType, leftType);
 }
 
 function checkTypeCast(from, to, msg){
@@ -103,6 +101,7 @@ var ChainedContext = Class.extend({
     init: function ChainedContext(parent){this.__parent = parent;},
     parent: function(){return this.__parent;},
     handleMessage: function(msg){return this.__parent.handleMessage(msg);},
+    language: function(){return this.__parent.language();},
     codeGenerator: function(){return this.__parent.codeGenerator();},
     findSymbol: function(id){return this.__parent.findSymbol(id);},
     currentScope: function(s){return this.__parent.currentScope();},
@@ -115,8 +114,7 @@ var ChainedContext = Class.extend({
     handleConst: function(type, value, code){this.__parent.handleConst(type, value, code);},
     genTypeName: function(){return this.__parent.genTypeName();},
     genVarName: function(id){return this.__parent.genVarName(id);},
-    qualifyScope: function(scope){return this.__parent.qualifyScope(scope);},
-    rtl: function(){return this.__parent.rtl();}
+    qualifyScope: function(scope){return this.__parent.qualifyScope(scope);}
 });
 
 exports.Integer = ChainedContext.extend({
@@ -377,7 +375,7 @@ exports.Designator = ChainedContext.extend({
 
         checkTypeCast(this.__currentType, type, "invalid type cast");
 
-        var code = this.rtl().typeGuard(this.__code, castCode(type, this));
+        var code = this.language().rtl.typeGuard(this.__code, castCode(type, this));
         this.__code = code;
 
         this.__currentType = type;
@@ -409,7 +407,7 @@ exports.Designator = ChainedContext.extend({
             || this.__info instanceof Type.VariableRef)
             return code;
         if (this.__derefCode)
-            return this.rtl().makeRef(this.__derefCode, this.__propCode);
+            return this.language().rtl.makeRef(this.__derefCode, this.__propCode);
         return "{set: function($v){" + code + " = $v;}, get: function(){return " + code + ";}}";
     }
 });
@@ -523,11 +521,12 @@ exports.ProcDecl = ChainedContext.extend({
         this.__type = undefined;
         this.__returnParsed = false;
         this.__outerScope = this.parent().currentScope();
+        this.__stdSymbols = this.language().stdSymbols;
     },
     handleIdentdef: function(id){
         this.__id = id;
         this.codeGenerator().write(this._prolog());
-        this.parent().pushScope(Scope.makeProcedure());
+        this.parent().pushScope(Scope.makeProcedure(this.__stdSymbols));
     },
     handleIdent: function(id){
         if (this.__id.id() != id)
@@ -586,7 +585,7 @@ exports.ProcDecl = ChainedContext.extend({
         var result = this.__type.result();
         if (!result)
             throw new Errors.Error("unexpected RETURN in PROCEDURE declared with no result type");
-        if (!Cast.implicit(type, result, castOperations))
+        if (this.language().types.implicitCast(type, result, false, castOperations, {set: function(){}}))
             throw new Errors.Error(
                 "RETURN '" + result.description() + "' expected, got '"
                 + type.description() + "'");
@@ -701,9 +700,10 @@ exports.ArrayDecl = HandleSymbolAsType.extend({
         for(var i = this.__dimensions.length; i-- ;){
             var length = this.__dimensions[i];
             dimensions = length + (dimensions.length ? ", " + dimensions : "");
+            var rtl = this.language().rtl;
             var arrayInit = !i
-                ? isCharArray ? this.rtl().makeCharArray(dimensions)
-                              : this.rtl().makeArray(dimensions + ", " + initializer)
+                ? isCharArray ? rtl.makeCharArray(dimensions)
+                              : rtl.makeArray(dimensions + ", " + initializer)
                 : undefined;
             type = Type.makeArray("ARRAY OF " + Type.typeName(type),
                                   arrayInit,
@@ -744,46 +744,26 @@ var numericOpTypeCheck = {
     check: function(t){return Type.numeric().indexOf(t) != -1;}
 };
 
+var numericOrSetOpTypeCheck = {
+    expect: numericOpTypeCheck.expect + " or SET",
+    check: function(t){return numericOpTypeCheck.check(t) || t == basicTypes.set;}
+};
+
 var intOpTypeCheck = {
     expect: Type.intsDescription(),
     check: Type.isInt
 };
 
-var orderOpTypeCheck = {
-    expect: "numeric type or CHAR or character array",
-    check: function(t){
-        return [basicTypes.integer,
-                basicTypes.uint8,
-                basicTypes.real,
-                basicTypes.ch
-               ].indexOf(t) != -1
-            || Type.isString(t);
-    }
-};
-
-var equalOpTypeCheck = {
-    expect: "numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE",
-    check: function(t){
-        return [basicTypes.integer,
-                basicTypes.uint8,
-                basicTypes.real,
-                basicTypes.set,
-                basicTypes.bool,
-                basicTypes.ch
-               ].indexOf(t) != -1
-            || Type.isString(t)
-            || t instanceof Type.Pointer
-            || t instanceof Type.Procedure
-            || t == nilType;
-    }
-};
+function throwOperatorTypeMismatch(op, expect, type){
+    throw new Errors.Error(
+        "operator '" + op +
+        "' type mismatch: " + expect + " expected, got '" +
+        type.description() + "'");
+}
 
 function assertOpType(type, check, literal){
     if (!check.check(type))
-        throw new Errors.Error(
-            "operator '" + literal +
-            "' type mismatch: " + check.expect + " expected, got '" +
-            type.description() + "'");
+        throwOperatorTypeMismatch(literal, check.expect, type);
 }
 
 function assertNumericOp(type, literal, op, intOp){
@@ -792,6 +772,11 @@ function assertNumericOp(type, literal, op, intOp){
            ? intOp : op;
 }
 
+function assertNumericOrSetOp(type, literal, op, intOp, setOp){
+    assertOpType(type, numericOrSetOpTypeCheck, literal);
+    return Type.isInt(type) ? intOp : type == basicTypes.set ? setOp : op;
+}
+
 function assertIntOp(type, literal, op){
     assertOpType(type, intOpTypeCheck, literal);
     return op;
@@ -814,58 +799,107 @@ function useTypeInRelation(leftType, rightType){
     return leftType;
 }
 
-function relationOp(leftType, rightType, literal){
-    var o;
-    var check;
+function useIntOrderOp(t){
+    return Type.isInt(t) || t == basicTypes.ch;
+}
+
+function useIntEqOp(t){
+    return Type.isInt(t)
+        || t == basicTypes.bool
+        || t == basicTypes.ch
+        || t instanceof Type.Pointer
+        || t instanceof Type.Procedure
+        || t == nilType;
+}
+
+var RelationOps = Class.extend({
+    init: function RelationOps(){
+    },
+    eq: function(type){
+        return useIntEqOp(type)         ? op.equalInt 
+             : Type.isString(type)      ? op.equalStr
+             : type == basicTypes.real  ? op.equalReal
+             : type == basicTypes.set   ? op.equalSet
+                                        : undefined;
+    },
+    notEq: function(type){
+        return useIntEqOp(type)         ? op.notEqualInt 
+             : Type.isString(type)      ? op.notEqualStr
+             : type == basicTypes.real  ? op.notEqualReal
+             : type == basicTypes.set   ? op.notEqualSet
+                                        : undefined;
+    },
+    less: function(type){
+        return useIntOrderOp(type) ?    op.lessInt
+             : Type.isString(type)      ? op.lessStr 
+             : type == basicTypes.real  ? op.lessReal
+                                        : undefined;
+    },
+    greater: function(type){
+        return useIntOrderOp(type) ?    op.greaterInt
+             : Type.isString(type)      ? op.greaterStr 
+             : type == basicTypes.real  ? op.greaterReal
+                                        : undefined;
+    },
+    lessEq: function(type){
+        return useIntOrderOp(type) ?    op.eqLessInt
+             : Type.isString(type)      ? op.eqLessStr 
+             : type == basicTypes.real  ? op.eqLessReal
+             : type == basicTypes.set   ? op.setInclL
+                                        : undefined;
+    },
+    greaterEq: function(type){
+        return useIntOrderOp(type)      ? op.eqGreaterInt
+             : Type.isString(type)      ? op.eqGreaterStr 
+             : type == basicTypes.real  ? op.eqGreaterReal
+             : type == basicTypes.set   ? op.setInclR
+                                        : undefined;
+    },
+    eqExpect: function(){return "numeric type or SET or BOOLEAN or CHAR or character array or POINTER or PROCEDURE";},
+    strongRelExpect: function(){return "numeric type or CHAR or character array";},
+    relExpect: function(){return "numeric type or SET or CHAR or character array";}
+});
+
+var relationOps = new RelationOps();
+
+function relationOp(leftType, rightType, literal, ops){
     var type = useTypeInRelation(leftType, rightType);
+    var o;
+    var mismatch;
     switch (literal){
         case "=":
-            o = Type.isString(type) ? op.equalStr 
-              : Type.isInt(type)    ? op.equalInt 
-                                    : op.equalReal;
-            check = equalOpTypeCheck;
+            o = ops.eq(type);
+            if (!o)
+                mismatch = ops.eqExpect();
             break;
         case "#":
-            o = Type.isString(type) ? op.notEqualStr 
-              : Type.isInt(type)    ? op.notEqualInt
-                                    : op.notEqualReal;
-            check = equalOpTypeCheck;
+            o = ops.notEq(type);
+            if (!o)
+                mismatch = ops.eqExpect();
             break;
         case "<":
-            o = Type.isString(type) ? op.lessStr 
-              : Type.isInt(type)    ? op.lessInt
-                                    : op.lessReal;
-            check = orderOpTypeCheck;
+            o = ops.less(type);
+            if (!o)
+                mismatch = ops.strongRelExpect();
             break;
         case ">":
-            o = Type.isString(type) ? op.greaterStr 
-              : Type.isInt(type)    ? op.greaterInt
-                                    : op.greaterReal;
-            check = orderOpTypeCheck;
+            o = ops.greater(type);
+            if (!o)
+                mismatch = ops.strongRelExpect();
             break;
         case "<=":
-            if (type == basicTypes.set)
-                o = op.setInclL;
-            else {
-                o = Type.isString(type) ? op.eqLessStr 
-                  : Type.isInt(type)    ? op.eqLessInt
-                                        : op.eqLessReal;
-                check = orderOpTypeCheck;
-            }
+            o = ops.lessEq(type);
+            if (!o)
+                mismatch = ops.relExpect();
             break;
         case ">=":
-            if (type == basicTypes.set)
-                o = op.setInclR;
-            else {
-                o = Type.isString(type) ? op.eqGreaterStr 
-                  : Type.isInt(type)    ? op.eqGreaterInt
-                                        : op.eqGreaterReal;
-                check = orderOpTypeCheck;
-            }
+            o = ops.greaterEq(type);
+            if (!o)
+                mismatch = ops.relExpect();
             break;
         }
-    if (check)
-        assertOpType(type, check, literal);
+    if (mismatch)
+        throwOperatorTypeMismatch(literal, mismatch, type);
     return o;
 }
 
@@ -876,22 +910,38 @@ exports.AddOperator = ChainedContext.extend({
     handleLiteral: function(s){
         var parent = this.parent();
         var type = parent.type();
-        var o;
-        if (s == "+")
-            o = (type == basicTypes.set) ? op.setUnion
-                                         : assertNumericOp(type, s, op.addReal, op.addInt);
-        else if (s == "-")
-            o = (type == basicTypes.set) ? op.setDiff
-                                         : assertNumericOp(type, s, op.subReal, op.subInt);
-        else if (s == "OR"){
-            if (type != basicTypes.bool)
-                throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
-                                     + type.description() + "'");
-            o = op.or;
-        }
+        var o = this.__matchOperator(s, type);
         if (o)
             parent.handleBinaryOperator(o);
-    }
+    },
+    __matchOperator: function(s, type){
+        var result;
+        switch (s){
+            case "+":
+                result = this._matchPlusOperator(type);
+                if (!result)
+                    throwOperatorTypeMismatch(s, this._expectPlusOperator(), type);
+                break;
+            case "-":
+                return assertNumericOrSetOp(type, s, op.subReal, op.subInt, op.setDiff);
+            case "OR":
+                if (type != basicTypes.bool)
+                    throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
+                                         + type.description() + "'");
+                return op.or;
+            }
+        return result;
+    },
+    _matchPlusOperator: function(type){
+        if (type == basicTypes.set)
+            return op.setUnion;
+        if (Type.isInt(type))
+            return op.addInt;
+        if (type == basicTypes.real)
+            return op.addReal;
+        return undefined;
+    },
+    _expectPlusOperator: function(){return "numeric type or SET";}
 });
 
 exports.MulOperator = ChainedContext.extend({
@@ -903,15 +953,11 @@ exports.MulOperator = ChainedContext.extend({
         var type = parent.type();
         var o;
         if (s == "*")
-            o = (type == basicTypes.set) ? op.setIntersection
-                                         : assertNumericOp(type, s, op.mulReal, op.mulInt);
+            o = assertNumericOrSetOp(type, s, op.mulReal, op.mulInt, op.setIntersection);
         else if (s == "/"){
-            if (type == basicTypes.set)
-                o = op.setSymmetricDiff;
-            else if (Type.isInt(type))
+            if (Type.isInt(type))
                 throw new Errors.Error("operator DIV expected for integer division");
-            else
-                o = assertNumericOp(type, s, op.divReal);
+            o = assertNumericOrSetOp(type, s, op.divReal, undefined, op.setSymmetricDiff);
         }
         else if (s == "DIV")
             o = assertIntOp(type, s, op.divInt);
@@ -1022,7 +1068,7 @@ exports.Set = ChainedContext.extend({
         if (!this.__expr.length)
             parent.handleConst(basicTypes.set, Code.makeSetConst(this.__value), this.__value.toString());
         else{
-            var code = this.rtl().makeSet(this.__expr);
+            var code = this.language().rtl.makeSet(this.__expr);
             if (this.__value)
                 code += " | " + this.__value;
             var e = Code.makeExpression(code, basicTypes.set);
@@ -1080,8 +1126,7 @@ exports.SimpleExpression = ChainedContext.extend({
         var o;
         switch(this.__unaryOperator){
             case "-":
-                o = (type == basicTypes.set) ? op.setComplement
-                                             : assertNumericOp(type, this.__unaryOperator, op.negateReal, op.negateInt);
+                o = assertNumericOrSetOp(type, this.__unaryOperator, op.negateReal, op.negateInt, op.setComplement);
                 break;
             case "+":
                 o = assertNumericOp(type, this.__unaryOperator, op.unaryPlus);
@@ -1100,7 +1145,7 @@ exports.SimpleExpression = ChainedContext.extend({
         if (type === undefined || this.__type === undefined)
             this.__type = type;
         else
-            checkImplicitCast(type, this.__type);
+            checkImplicitCast(this.language().types, type, this.__type);
     },
     handleBinaryOperator: function(o){this.__binaryOperator = o;},
     endParse: function(){
@@ -1109,8 +1154,9 @@ exports.SimpleExpression = ChainedContext.extend({
 });
 
 exports.Expression = ChainedContext.extend({
-    init: function ExpressionContext(context){
+    init: function ExpressionContext(context, relOps){
         ChainedContext.prototype.init.call(this, context);
+        this.__relOps = relOps || relationOps;
         this.__relation = undefined;
         this.__expression = undefined;
     },
@@ -1133,7 +1179,7 @@ exports.Expression = ChainedContext.extend({
             if (!Type.isInt(leftType))
                 throw new Errors.Error(
                     Type.intsDescription() + " expected as an element of SET, got '" + Type.typeName(leftType) + "'");
-            checkImplicitCast(rightType, basicTypes.set);
+            checkImplicitCast(this.language().types, rightType, basicTypes.set);
 
             code = "1 << " + leftCode + " & " + rightCode;
         }
@@ -1164,8 +1210,8 @@ exports.Expression = ChainedContext.extend({
 
         var value;
         if (!code){
-            var o = relationOp(leftExpression.type(), rightExpression.type(), this.__relation);
-            var oResult = o(leftExpression, rightExpression, this);
+            var o = relationOp(leftExpression.type(), rightExpression.type(), this.__relation, this.__relOps);
+            var oResult = o(leftExpression, rightExpression, this.language().rtl);
             code = oResult.code();
             value = oResult.constValue();
         }
@@ -1480,7 +1526,7 @@ exports.Assignment = ChainedContext.extend({
         this.__left = Code.makeExpression(d.code(), d.type(), d);
     },
     handleExpression: function(e){
-        this.parent().codeGenerator().write(op.assign(this.__left, e, this));
+        this.parent().codeGenerator().write(op.assign(this.__left, e, this.language().rtl));
     }
 });
 
@@ -1601,7 +1647,14 @@ var ProcedureCall = ChainedContext.extend({
     },
     setDesignator: function(d){
         this.__type = assertProcType(d);
-        this.__procCall = this.__type.callGenerator(this, d.code());
+        var l = this.language();
+        this.__procCall = this.__type.callGenerator(
+              { 
+                types: function(){return l.types;}
+              , rtl: function(){return l.rtl;}
+              , qualifyScope: this.qualifyScope.bind(this)
+              }
+            , d.code());
         this.__callExpression = undefined;
     },
     codeGenerator: function(){return this.__code;},
@@ -1714,7 +1767,7 @@ exports.RecordDecl = ChainedContext.extend({
         var gen = this.codeGenerator();
         var qualifiedBase = baseType ? this.qualifyScope(Type.recordScope(baseType)) + Type.typeName(baseType) : undefined; 
         gen.write((baseType ? qualifiedBase + ".extend" 
-                            : this.rtl().extendId())
+                            : this.language().rtl.extendId())
                 + "(");
         gen.openScope();
         gen.write("init: function " + Type.recordConstructor(this.__type) + "()");
@@ -1795,12 +1848,13 @@ exports.ModuleDeclaration = ChainedContext.extend({
         this.__imports = {};
         this.__moduleScope = undefined;
         this.__moduleGen = undefined;
+        this.__stdSymbols = this.language().stdSymbols;
     },
     handleIdent: function(id){
         var parent = this.parent();
         if (this.__name === undefined ) {
             this.__name = id;
-            this.__moduleScope = Scope.makeModule(id);
+            this.__moduleScope = Scope.makeModule(id, this.__stdSymbols);
             parent.pushScope(this.__moduleScope);
         }
         else if (id === this.__name){
@@ -1896,22 +1950,20 @@ var ModuleImport = ChainedContext.extend({
 exports.ModuleImport = ModuleImport;
 
 exports.Context = Class.extend({
-    init: function Context(code, moduleGeneratorFactory, rtl, moduleResolver){
-        this.__code = code;
-        this.__moduleGeneratorFactory = moduleGeneratorFactory;
+    init: function Context(language){
+        this.__language = language;
         this.__scopes = [];
         this.__gen = 0;
         this.__vars = [];
-        this.__rtl = rtl;
-        this.__moduleResolver = moduleResolver;
     },
+    language: function(){return this.__language;},
     genTypeName: function(){
         ++this.__gen;
         return "anonymous$" + this.__gen;
     },
     genVarName: function(id){
         if (this.__vars.indexOf(id) === -1) {
-            this.__code.write("var " + id + ";\n");
+            this.codeGenerator().write("var " + id + ";\n");
             this.__vars.push(id);
         }
     },
@@ -1933,16 +1985,15 @@ exports.Context = Class.extend({
     },
     handleExpression: function(){},
     handleLiteral: function(){},
-    codeGenerator: function(){return this.__code;},
+    codeGenerator: function(){return this.__language.codeGenerator;},
     makeModuleGenerator: function(name, imports){
-        return this.__moduleGeneratorFactory(name, imports);
+        return this.__language.moduleGenerator(name, imports);
     },
-    rtl: function(){return this.__rtl;},
     findModule: function(name){
         if (name == "JS"){
             return Module.makeJS();
         }
-        return this.__moduleResolver ? this.__moduleResolver(name) : undefined;
+        return this.__language.moduleResolver ? this.__language.moduleResolver(name) : undefined;
     }
 });
 
@@ -1952,3 +2003,4 @@ exports.endParametersMsg = endParametersMsg;
 exports.getSymbolAndScope = getSymbolAndScope;
 exports.unwrapType = unwrapType;
 exports.IdentdefInfo = IdentdefInfo;
+exports.RelationOps = RelationOps;

+ 20 - 0
src/eberon/EberonCast.ob

@@ -0,0 +1,20 @@
+MODULE EberonCast;
+IMPORT Cast, EberonString, Types;
+
+PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER;
+VAR
+    result: INTEGER;
+BEGIN
+    IF (from = EberonString.string) & Types.isString(to) THEN
+        IF toVar THEN 
+            result := Cast.errVarParameter;
+        ELSE
+            result := Cast.errNo;
+        END;
+    ELSE
+        result := Cast.implicit(from, to, toVar, ops, op);
+    END;
+    RETURN result
+END implicit;
+
+END EberonCast.

+ 63 - 0
src/eberon/EberonOperator.ob

@@ -0,0 +1,63 @@
+MODULE EberonOperator;
+IMPORT Code, CodePrecedence, JsString, OberonRtl, Operator;
+
+PROCEDURE opAddStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeStrConst(JsString.concat(left^(Code.StrConst).value, 
+                                             right^(Code.StrConst).value))
+END opAddStr;
+
+PROCEDURE opEqualStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(ORD(left^(Code.StrConst).value
+                                 = right^(Code.StrConst).value))
+END opEqualStr;
+
+PROCEDURE opNotEqualStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(ORD(left^(Code.StrConst).value
+                                 # right^(Code.StrConst).value))
+END opNotEqualStr;
+
+PROCEDURE opLessStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(0) (*to fix*)
+END opLessStr;
+
+PROCEDURE opGreaterStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(0) (*to fix*)
+END opGreaterStr;
+
+PROCEDURE opLessEqualStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(0) (*to fix*)
+END opLessEqualStr;
+
+PROCEDURE opGraterEqualStr(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(0) (*to fix*)
+END opGraterEqualStr;
+
+PROCEDURE addStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opAddStr, " + ", CodePrecedence.addSub)
+END addStr;
+
+PROCEDURE equalStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opEqualStr, " == ", CodePrecedence.equal)
+END equalStr;
+
+PROCEDURE notEqualStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opNotEqualStr, " != ", CodePrecedence.equal)
+END notEqualStr;
+
+PROCEDURE lessStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opLessStr, " < ", CodePrecedence.relational)
+END lessStr;
+
+PROCEDURE greaterStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opGreaterStr, " > ", CodePrecedence.relational)
+END greaterStr;
+
+PROCEDURE lessEqualStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opLessEqualStr, " <= ", CodePrecedence.relational)
+END lessEqualStr;
+
+PROCEDURE greaterEqualStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN Operator.binaryWithCode(left, right, rtl, opGraterEqualStr, " >= ", CodePrecedence.relational)
+END greaterEqualStr;
+
+END EberonOperator.

+ 7 - 0
src/eberon/EberonString.ob

@@ -0,0 +1,7 @@
+MODULE EberonString;
+IMPORT Types;
+VAR
+    string*: POINTER TO Types.BasicType;
+BEGIN
+    string := Types.makeBasic("STRING", "''");
+END EberonString.

+ 63 - 2
src/eberon/eberon_context.js

@@ -3,7 +3,10 @@
 var Cast = require("js/Cast.js");
 var Code = require("js/Code.js");
 var Context = require("context.js");
+var EberonString = require("eberon/js/EberonString.js");
 var Errors = require("js/Errors.js");
+var op = require("js/Operator.js");
+var eOp = require("eberon/js/EberonOperator.js");
 var Symbol = require("js/Symbols.js");
 var Procedure = require("js/Procedure.js");
 var Type = require("js/Types.js");
@@ -276,8 +279,8 @@ var RecordDecl = Context.RecordDecl.extend({
 });
 
 var ProcOrMethodDecl = Context.ProcDecl.extend({
-    init: function EberonContext$ProcOrMethodDecl(parent){
-        Context.ProcDecl.prototype.init.call(this, parent);
+    init: function EberonContext$ProcOrMethodDecl(parent, stdSymbols){
+        Context.ProcDecl.prototype.init.call(this, parent, stdSymbols);
         this.__selfSymbol = undefined;
         this.__methodId = undefined;
         this.__methodType = undefined;
@@ -374,7 +377,65 @@ var ProcOrMethodDecl = Context.ProcDecl.extend({
     }
 });
 
+var AddOperator = Context.AddOperator.extend({
+    init: function EberonContext$AddOperator(context){
+        Context.AddOperator.prototype.init.call(this, context);
+    },
+    _matchPlusOperator: function(type){
+        if (type == EberonString.string())
+            return eOp.addStr;
+        return Context.AddOperator.prototype._matchPlusOperator.call(this, type);
+    },
+    _expectPlusOperator: function(){return "numeric type or SET or STRING";}
+});
+
+var RelationOps = Context.RelationOps.extend({
+    init: function EberonContext$RelationOps(){
+        Context.RelationOps.prototype.init.call(this);
+    },
+    eq: function(type){
+        return type == EberonString.string() 
+            ? eOp.equalStr
+            : Context.RelationOps.prototype.eq.call(this, type);
+    },
+    notEq: function(type){
+        return type == EberonString.string() 
+            ? eOp.notEqualStr
+            : Context.RelationOps.prototype.notEq.call(this, type);
+    },
+    less: function(type){
+        return type == EberonString.string() 
+            ? eOp.lessStr
+            : Context.RelationOps.prototype.less.call(this, type);
+    },
+    greater: function(type){
+        return type == EberonString.string() 
+            ? eOp.greaterStr
+            : Context.RelationOps.prototype.greater.call(this, type);
+    },
+    lessEq: function(type){
+        return type == EberonString.string() 
+            ? eOp.lessEqualStr
+            : Context.RelationOps.prototype.lessEq.call(this, type);
+    },
+    greaterEq: function(type){
+        return type == EberonString.string() 
+            ? eOp.greaterEqualStr
+            : Context.RelationOps.prototype.greaterEq.call(this, type);
+    }
+});
+
+var relationOps = new RelationOps();
+
+var Expression = Context.Expression.extend({
+    init: function EberonContext$Expression(context){
+        Context.Expression.prototype.init.call(this, context, relationOps);
+    }
+});
+
+exports.AddOperator = AddOperator;
 exports.Designator = Designator;
+exports.Expression = Expression;
 exports.MethodHeading = MethodHeading;
 exports.ProcOrMethodId = ProcOrMethodId;
 exports.ProcOrMethodDecl = ProcOrMethodDecl;

+ 23 - 9
src/eberon/eberon_grammar.js

@@ -1,9 +1,12 @@
 "use strict";
 
+var Cast = require("eberon/js/EberonCast.js");
 var Context = require("context.js");
 var EbContext = require("eberon/eberon_context.js");
+var EberonString = require("eberon/js/EberonString.js");
 var Grammar = require("grammar.js");
 var Parser = require("parser.js");
+var Scope = require("js/Scope.js");
 
 var and = Parser.and;
 var context = Parser.context;
@@ -45,12 +48,23 @@ function makeFieldList(identdef, identList, type, formalParameters){
         Context.FieldListDeclaration);
 }
 
-exports.grammar = Grammar.make(
-    makeDesignator,
-    makeProcedureHeading,
-    makeProcedureDeclaration,
-    makeFieldList,
-    EbContext.RecordDecl,
-    Context.VariableDeclaration,
-    Grammar.reservedWords + " SELF SUPER"
-    );
+var stdSymbols = Scope.makeStdSymbols();
+Scope.addSymbolForType(EberonString.string(), stdSymbols);
+
+exports.language = {
+  grammar: Grammar.make(
+      makeDesignator,
+      makeProcedureHeading,
+      makeProcedureDeclaration,
+      makeFieldList,
+      EbContext.RecordDecl,
+      Context.VariableDeclaration,
+      EbContext.AddOperator,
+      EbContext.Expression,
+      Grammar.reservedWords + " SELF SUPER"
+      ),
+    stdSymbols: stdSymbols,
+    types: {
+        implicitCast: Cast.implicit
+    }
+};

+ 20 - 0
src/eberon/js/EberonCast.js

@@ -0,0 +1,20 @@
+var Cast = require("js/Cast.js");
+var EberonString = require("js/EberonString.js");
+var Types = require("js/Types.js");
+
+function implicit(from/*PType*/, to/*PType*/, toVar/*BOOLEAN*/, ops/*Operations*/, op/*VAR PCastOp*/){
+	var result = 0;
+	if (from == EberonString.string() && Types.isString(to)){
+		if (toVar){
+			result = Cast.errVarParameter;
+		}
+		else {
+			result = Cast.errNo;
+		}
+	}
+	else {
+		result = Cast.implicit(from, to, toVar, ops, op);
+	}
+	return result;
+}
+exports.implicit = implicit;

+ 69 - 0
src/eberon/js/EberonOperator.js

@@ -0,0 +1,69 @@
+var RTL$ = require("rtl.js");
+var Code = require("js/Code.js");
+var CodePrecedence = require("js/CodePrecedence.js");
+var JsString = require("js/JsString.js");
+var OberonRtl = require("js/OberonRtl.js");
+var Operator = require("js/Operator.js");
+
+function opAddStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeStrConst(JsString.concat(RTL$.typeGuard(left, Code.StrConst).value, RTL$.typeGuard(right, Code.StrConst).value));
+}
+
+function opEqualStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(RTL$.typeGuard(left, Code.StrConst).value == RTL$.typeGuard(right, Code.StrConst).value ? 1 : 0);
+}
+
+function opNotEqualStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(RTL$.typeGuard(left, Code.StrConst).value != RTL$.typeGuard(right, Code.StrConst).value ? 1 : 0);
+}
+
+function opLessStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(0);
+}
+
+function opGreaterStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(0);
+}
+
+function opLessEqualStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(0);
+}
+
+function opGraterEqualStr(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(0);
+}
+
+function addStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opAddStr, " + ", CodePrecedence.addSub);
+}
+
+function equalStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opEqualStr, " == ", CodePrecedence.equal);
+}
+
+function notEqualStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opNotEqualStr, " != ", CodePrecedence.equal);
+}
+
+function lessStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opLessStr, " < ", CodePrecedence.relational);
+}
+
+function greaterStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opGreaterStr, " > ", CodePrecedence.relational);
+}
+
+function lessEqualStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opLessEqualStr, " <= ", CodePrecedence.relational);
+}
+
+function greaterEqualStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Operator.binaryWithCode(left, right, rtl, opGraterEqualStr, " >= ", CodePrecedence.relational);
+}
+exports.addStr = addStr;
+exports.equalStr = equalStr;
+exports.notEqualStr = notEqualStr;
+exports.lessStr = lessStr;
+exports.greaterStr = greaterStr;
+exports.lessEqualStr = lessEqualStr;
+exports.greaterEqualStr = greaterEqualStr;

+ 4 - 0
src/eberon/js/EberonString.js

@@ -0,0 +1,4 @@
+var Types = require("js/Types.js");
+var string = null;
+string = Types.makeBasic("STRING", "''");
+exports.string = function(){return string;};

+ 11 - 3
src/grammar.js

@@ -28,6 +28,8 @@ function make(makeDesignator,
               makeFieldList,
               recordDeclContext,
               varDeclContext,
+              addOperatorContext,
+              expressionContext,
               reservedWords
               ){
 var result = {};
@@ -36,6 +38,12 @@ var ident = function(stream, context){
     return Lexer.ident(stream, context, reservedWords);
 };
 
+var ModuleDeclContext = Context.ModuleDeclaration.extend({
+    init: function Grammar$ModuleDeclContext(context){
+        Context.ModuleDeclaration.prototype.init.call(this, context);
+    }
+});
+
 var qualident = context(and(optional(and(ident, ".")), ident),
                         Context.QualifiedIdentificator);
 var identdef = context(and(ident, optional("*")),
@@ -81,7 +89,7 @@ var factor = context(
      )
     , Context.Factor);
 
-var addOperator = context(or("+", "-", "OR"), Context.AddOperator);
+var addOperator = context(or("+", "-", "OR"), addOperatorContext);
 var mulOperator = context(or("*", "/", "DIV", "MOD", "&"), Context.MulOperator);
 var term = context(and(factor, repeat(and(mulOperator, factor))), Context.Term);
 var simpleExpression = context(
@@ -91,7 +99,7 @@ var simpleExpression = context(
       , Context.SimpleExpression);
 var relation = or("=", "#", "<=", "<", ">=", ">", "IN", "IS");
 var expression = context(and(simpleExpression, optional(and(relation, simpleExpression)))
-                       , Context.Expression);
+                       , expressionContext);
 var constExpression = expression;
 
 var element = context(and(expression, optional(and("..", expression))), Context.SetElement);
@@ -213,7 +221,7 @@ result.module
                   result.declarationSequence,
                   optional(and("BEGIN", statementSequence)),
                   required("END", "END expected (MODULE)"), ident, point),
-              Context.ModuleDeclaration);
+              ModuleDeclContext);
 return result;
 }
 

+ 38 - 25
src/js/Cast.js

@@ -1,20 +1,18 @@
 var RTL$ = require("rtl.js");
 var Code = require("js/Code.js");
-var Context = require("js/Context.js");
+var OberonRtl = require("js/OberonRtl.js");
 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 errNo = 0;
+var err = 1;
+var errVarParameter = 2;
 var CastOp = Object.Type.extend({
 	init: function CastOp(){
 		Object.Type.prototype.init.call(this);
 	}
 });
-var CastOpDoNothing = CastOp.extend({
-	init: function CastOpDoNothing(){
-		CastOp.prototype.init.call(this);
-	}
-});
 var CastOpStrToChar = CastOp.extend({
 	init: function CastOpStrToChar(){
 		CastOp.prototype.init.call(this);
@@ -28,9 +26,6 @@ var Operations = RTL$.extend({
 });
 var areTypesExactlyMatch = null;
 var doNothing = null;
-CastOpDoNothing.prototype.make = function(c/*Type*/, e/*PExpression*/){
-	return e;
-}
 
 function findBaseType(base/*PRecord*/, type/*PRecord*/){
 	while (true){
@@ -105,7 +100,7 @@ function areTypesExactlyMatchImpl(t1/*PType*/, t2/*PType*/){
 	}
 	return result;
 }
-CastOpStrToChar.prototype.make = function(c/*Type*/, e/*PExpression*/){
+CastOpStrToChar.prototype.make = function(rtl/*PType*/, e/*PExpression*/){
 	return Code.makeSimpleExpression(JsString.fromInt(this.c), Types.basic().ch);
 }
 
@@ -116,57 +111,75 @@ function makeCastOpStrToChar(c/*CHAR*/){
 	return result;
 }
 
-function implicit(from/*PType*/, to/*PType*/, ops/*Operations*/){
-	var result = null;
+function implicit(from/*PType*/, to/*PType*/, toVar/*BOOLEAN*/, ops/*Operations*/, op/*VAR PCastOp*/){
+	var result = 0;
 	var c = 0;
+	var ignore = false;
+	result = err;
+	op.set(null);
 	if (from == to){
-		result = doNothing;
+		result = errNo;
 	}
 	else if (from == Types.basic().uint8 && to == Types.basic().integer){
-		result = doNothing;
+		if (toVar){
+			result = errVarParameter;
+		}
+		else {
+			result = errNo;
+		}
 	}
 	else if (from == Types.basic().integer && to == Types.basic().uint8){
-		result = ops.castToUint8;
+		if (toVar){
+			result = errVarParameter;
+		}
+		else {
+			op.set(ops.castToUint8);
+			result = errNo;
+		}
 	}
 	else if (from instanceof Types.String){
 		if (to == Types.basic().ch){
 			if (Types.stringAsChar(RTL$.typeGuard(from, Types.String), {set: function($v){c = $v;}, get: function(){return c;}})){
-				result = makeCastOpStrToChar(c);
+				op.set(makeCastOpStrToChar(c));
+				result = errNo;
 			}
 		}
-		else if (to instanceof Types.Array && Types.arrayElementsType(RTL$.typeGuard(to, Types.Array)) == Types.basic().ch){
-			result = doNothing;
+		else if (Types.isString(to)){
+			result = errNo;
 		}
 	}
 	else if (from instanceof Types.Array && to instanceof Types.Array){
-		if (Types.arrayLength(RTL$.typeGuard(to, Types.Array)) == Types.openArrayLength || Types.arrayLength(RTL$.typeGuard(to, Types.Array)) == Types.arrayLength(RTL$.typeGuard(from, Types.Array))){
-			result = implicit(Types.arrayElementsType(RTL$.typeGuard(from, Types.Array)), Types.arrayElementsType(RTL$.typeGuard(to, Types.Array)), ops);
+		if ((Types.arrayLength(RTL$.typeGuard(from, Types.Array)) == Types.arrayLength(RTL$.typeGuard(to, Types.Array)) || Types.arrayLength(RTL$.typeGuard(to, Types.Array)) == Types.openArrayLength) && areTypesExactlyMatch(Types.arrayElementsType(RTL$.typeGuard(from, Types.Array)), Types.arrayElementsType(RTL$.typeGuard(to, Types.Array)))){
+			result = errNo;
 		}
 	}
 	else if (from instanceof Types.Pointer && to instanceof Types.Pointer){
 		if (findPointerBaseType(RTL$.typeGuard(to, Types.Pointer), RTL$.typeGuard(from, Types.Pointer)) != null){
-			result = doNothing;
+			result = errNo;
 		}
 	}
 	else if (from instanceof Types.Record && to instanceof Types.Record){
 		if (findBaseType(RTL$.typeGuard(to, Types.Record), RTL$.typeGuard(from, Types.Record)) != null){
-			result = doNothing;
+			result = errNo;
 		}
 	}
 	else if (from == Types.nil() && matchesToNIL(to)){
-		result = doNothing;
+		result = errNo;
 	}
 	else if (from instanceof Types.DefinedProcedure && to instanceof Types.DefinedProcedure){
 		if (areProceduresMatch(RTL$.typeGuard(from, Types.DefinedProcedure), RTL$.typeGuard(to, Types.DefinedProcedure))){
-			result = doNothing;
+			result = errNo;
 		}
 	}
 	return result;
 }
 areTypesExactlyMatch = areTypesExactlyMatchImpl;
-doNothing = new CastOpDoNothing();
+exports.errNo = errNo;
+exports.err = err;
+exports.errVarParameter = errVarParameter;
 exports.CastOp = CastOp;
 exports.Operations = Operations;
+exports.doNothing = function(){return doNothing;};
 exports.findPointerBaseType = findPointerBaseType;
 exports.areTypesMatch = areTypesMatch;
 exports.areProceduresMatch = areProceduresMatch;

+ 17 - 2
src/js/Code.js

@@ -1,9 +1,9 @@
 var RTL$ = require("rtl.js");
 var JsMap = require("js/JsMap.js");
 var JsString = require("js/JsString.js");
-var Context = require("js/Context.js");
 var Object = require("js/Object.js");
 var Stream = require("js/Stream.js");
+var ScopeBase = require("js/ScopeBase.js");
 var Symbols = require("js/Symbols.js");
 var Precedence = require("js/CodePrecedence.js");
 var Types = require("js/Types.js");
@@ -61,6 +61,12 @@ var SetConst = Const.extend({
 		this.value = 0;
 	}
 });
+var StrConst = Const.extend({
+	init: function StrConst(){
+		Const.prototype.init.call(this);
+		this.value = null;
+	}
+});
 var StringConst = Const.extend({
 	init: function StringConst(){
 		Const.prototype.init.call(this);
@@ -196,6 +202,13 @@ function makeSetConst(s/*SET*/){
 	return result;
 }
 
+function makeStrConst(s/*Type*/){
+	var result = null;
+	result = new StrConst();
+	result.value = s;
+	return result;
+}
+
 function makeStringConst(s/*Type*/){
 	var result = null;
 	result = new StringConst();
@@ -241,7 +254,7 @@ Designator.prototype.scope = function(){
 	return this.mScope;
 }
 
-function makeDesignator(code/*Type*/, lval/*Type*/, refCode/*RefCodeProc*/, type/*PType*/, info/*PId*/, scope/*PScope*/){
+function makeDesignator(code/*Type*/, lval/*Type*/, refCode/*RefCodeProc*/, type/*PType*/, info/*PId*/, scope/*PType*/){
 	var result = null;
 	result = new Designator();
 	result.mCode = code;
@@ -388,11 +401,13 @@ exports.Const = Const;
 exports.IntConst = IntConst;
 exports.RealConst = RealConst;
 exports.SetConst = SetConst;
+exports.StrConst = StrConst;
 exports.Expression = Expression;
 exports.nullGenerator = function(){return nullGenerator;};
 exports.makeIntConst = makeIntConst;
 exports.makeRealConst = makeRealConst;
 exports.makeSetConst = makeSetConst;
+exports.makeStrConst = makeStrConst;
 exports.makeStringConst = makeStringConst;
 exports.makeExpressionWithPrecedence = makeExpressionWithPrecedence;
 exports.makeExpression = makeExpression;

+ 2 - 17
src/js/Context.js

@@ -1,20 +1,8 @@
 var RTL$ = require("rtl.js");
 var JsString = require("js/JsString.js");
+var OberonRtl = require("js/OberonRtl.js");
 var Object = require("js/Object.js");
-var Scope = RTL$.extend({
-	init: function Scope(){
-	}
-});
-var Rtl = RTL$.extend({
-	init: function Rtl(){
-		this.copy = null;
-		this.strCmp = null;
-		this.assignArrayFromString = null;
-		this.setInclL = null;
-		this.setInclR = null;
-		this.assertId = null;
-	}
-});
+var ScopeBase = require("js/ScopeBase.js");
 var Type = RTL$.extend({
 	init: function Type(){
 		this.handleChar = null;
@@ -23,9 +11,6 @@ var Type = RTL$.extend({
 		this.handleIdent = null;
 		this.isLexem = null;
 		this.qualifyScope = null;
-		this.rtl = null;
 	}
 });
-exports.Scope = Scope;
-exports.Rtl = Rtl;
 exports.Type = Type;

+ 9 - 0
src/js/Language.js

@@ -0,0 +1,9 @@
+var RTL$ = require("rtl.js");
+var Cast = require("js/Cast.js");
+var JsString = require("js/JsString.js");
+var T = require("js/Types.js");
+var Types = RTL$.extend({
+	init: function Types(){
+	}
+});
+exports.Types = Types;

+ 8 - 0
src/js/LanguageContext.js

@@ -0,0 +1,8 @@
+var Context = require("js/Context.js");
+var Language = require("js/Language.js");
+var Type = Context.Type.extend({
+	init: function Type(){
+		Context.Type.prototype.init.call(this);
+	}
+});
+exports.Type = Type;

+ 13 - 0
src/js/OberonRtl.js

@@ -0,0 +1,13 @@
+var RTL$ = require("rtl.js");
+var JsString = require("js/JsString.js");
+var Type = RTL$.extend({
+	init: function Type(){
+		this.copy = null;
+		this.strCmp = null;
+		this.assignArrayFromString = null;
+		this.setInclL = null;
+		this.setInclR = null;
+		this.assertId = null;
+	}
+});
+exports.Type = Type;

+ 148 - 128
src/js/Operator.js

@@ -1,9 +1,9 @@
 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 JsString = require("js/JsString.js");
+var OberonRtl = require("js/OberonRtl.js");
 var Precedence = require("js/CodePrecedence.js");
 var Types = require("js/Types.js");
 var CodeMaker = RTL$.extend({
@@ -36,7 +36,7 @@ var openArrayChar = null;
 var castOperations = new Cast.Operations();
 var castToUint8 = null;
 
-function binary(left/*PExpression*/, right/*PExpression*/, context/*Type*/, op/*BinaryOp*/, code/*PCodeMaker*/, precedence/*INTEGER*/, optResultType/*PType*/, optResultPrecedence/*INTEGER*/){
+function binary(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, op/*BinaryOp*/, code/*PCodeMaker*/, precedence/*INTEGER*/, optResultType/*PType*/, optResultPrecedence/*INTEGER*/){
 	var result = null;
 	var leftValue = null;var rightValue = null;var resultValue = null;
 	var leftCode = null;var rightCode = null;var resultCode = null;
@@ -56,7 +56,7 @@ function binary(left/*PExpression*/, right/*PExpression*/, context/*Type*/, op/*
 	else {
 		rightCode = rightExpDeref.code();
 	}
-	resultCode = code.make(leftCode, rightCode, context);
+	resultCode = code.make(leftCode, rightCode, rtl);
 	if (optResultType != null){
 		resultType = optResultType;
 	}
@@ -71,14 +71,14 @@ function binary(left/*PExpression*/, right/*PExpression*/, context/*Type*/, op/*
 	}
 	return Code.makeExpressionWithPrecedence(resultCode, resultType, null, resultValue, resultPrecedence);
 }
-SimpleCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, c/*Type*/){
+SimpleCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, rtl/*PType*/){
 	return JsString.concat(JsString.concat(left, this.code), right);
 }
-IntCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, c/*Type*/){
-	return JsString.concat(SimpleCodeMaker.prototype.make.call(this, left, right, c), JsString.make(" | 0"));
+IntCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, rtl/*PType*/){
+	return JsString.concat(SimpleCodeMaker.prototype.make.call(this, left, right, rtl), JsString.make(" | 0"));
 }
-PredCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, c/*Type*/){
-	return this.pred(left, right, c);
+PredCodeMaker.prototype.make = function(left/*Type*/, right/*Type*/, rtl/*PType*/){
+	return this.pred(left, right, rtl);
 }
 
 function makeSimpleCodeMaker(code/*ARRAY OF CHAR*/){
@@ -102,12 +102,12 @@ function makePredCodeMaker(pred/*CodePredicate*/){
 	return result;
 }
 
-function binaryWithCodeEx(left/*PExpression*/, right/*PExpression*/, context/*Type*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/, optResultType/*PType*/, optResultPrecedence/*INTEGER*/){
-	return binary(left, right, context, op, makeSimpleCodeMaker(code), precedence, optResultType, optResultPrecedence);
+function binaryWithCodeEx(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/, optResultType/*PType*/, optResultPrecedence/*INTEGER*/){
+	return binary(left, right, rtl, op, makeSimpleCodeMaker(code), precedence, optResultType, optResultPrecedence);
 }
 
-function binaryWithCode(left/*PExpression*/, right/*PExpression*/, c/*Type*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/){
-	return binaryWithCodeEx(left, right, c, op, code, precedence, null, Precedence.none);
+function binaryWithCode(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/){
+	return binaryWithCodeEx(left, right, rtl, op, code, precedence, null, Precedence.none);
 }
 
 function promoteToWideIfNeeded(e/*PExpression*/){
@@ -121,12 +121,12 @@ function promoteToWideIfNeeded(e/*PExpression*/){
 	return result;
 }
 
-function binaryInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/){
-	return promoteToWideIfNeeded(binary(left, right, c, op, makeIntCodeMaker(code), precedence, null, Precedence.bitOr));
+function binaryInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, op/*BinaryOp*/, code/*ARRAY OF CHAR*/, precedence/*INTEGER*/){
+	return promoteToWideIfNeeded(binary(left, right, rtl, op, makeIntCodeMaker(code), precedence, null, Precedence.bitOr));
 }
 
-function binaryPred(left/*PExpression*/, right/*PExpression*/, c/*Type*/, op/*BinaryOp*/, pred/*CodePredicate*/){
-	return binary(left, right, c, op, makePredCodeMaker(pred), Precedence.none, null, Precedence.none);
+function binaryPred(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, op/*BinaryOp*/, pred/*CodePredicate*/){
+	return binary(left, right, rtl, op, makePredCodeMaker(pred), Precedence.none, null, Precedence.none);
 }
 
 function unary(e/*PExpression*/, op/*UnaryOp*/, code/*ARRAY OF CHAR*/){
@@ -140,11 +140,17 @@ function unary(e/*PExpression*/, op/*UnaryOp*/, code/*ARRAY OF CHAR*/){
 	return Code.makeExpression(resultCode, e.type(), null, value);
 }
 
-function castToStr(e/*PExpression*/, context/*Type*/){
+function castToStr(e/*PExpression*/, rtl/*PType*/){
 	var resultExpression = null;
 	var op = null;
-	op = Cast.implicit(e.type(), openArrayChar, castOperations);
-	resultExpression = op.make(context, e);
+	var ignored = 0;
+	ignored = Cast.implicit(e.type(), openArrayChar, false, castOperations, {set: function($v){op = $v;}, get: function(){return op;}});
+	if (op != null){
+		resultExpression = op.make(rtl, e);
+	}
+	else {
+		resultExpression = e;
+	}
 	return resultExpression.code();
 }
 
@@ -224,6 +230,10 @@ function opEqualReal(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.RealConst).value == RTL$.typeGuard(right, Code.RealConst).value ? 1 : 0);
 }
 
+function opEqualSet(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(RTL$.typeGuard(left, Code.SetConst).value == RTL$.typeGuard(right, Code.SetConst).value ? 1 : 0);
+}
+
 function opNotEqualInt(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.IntConst).value != RTL$.typeGuard(right, Code.IntConst).value ? 1 : 0);
 }
@@ -232,6 +242,10 @@ function opNotEqualReal(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.RealConst).value != RTL$.typeGuard(right, Code.RealConst).value ? 1 : 0);
 }
 
+function opNotEqualSet(left/*PConst*/, right/*PConst*/){
+	return Code.makeIntConst(RTL$.typeGuard(left, Code.SetConst).value != RTL$.typeGuard(right, Code.SetConst).value ? 1 : 0);
+}
+
 function opLessInt(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.IntConst).value < RTL$.typeGuard(right, Code.IntConst).value ? 1 : 0);
 }
@@ -296,44 +310,36 @@ function opRor(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.IntConst).value >>> RTL$.typeGuard(right, Code.IntConst).value);
 }
 
-function codeSetInclL(left/*Type*/, right/*Type*/, c/*Type*/){
-	var rtl = null;
-	rtl = c.rtl();
+function codeSetInclL(left/*Type*/, right/*Type*/, rtl/*PType*/){
 	return rtl.setInclL(left, right);
 }
 
-function codeSetInclR(left/*Type*/, right/*Type*/, c/*Type*/){
-	var rtl = null;
-	rtl = c.rtl();
+function codeSetInclR(left/*Type*/, right/*Type*/, rtl/*PType*/){
 	return rtl.setInclR(left, right);
 }
 
-function strCmp(op/*ARRAY OF CHAR*/, left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	var rtl = null;
-	rtl = c.rtl();
-	return Code.makeSimpleExpression(JsString.concat(JsString.concat(rtl.strCmp(castToStr(left, c), castToStr(right, c)), JsString.make(op)), JsString.make("0")), Types.basic().bool);
+function strCmp(op/*ARRAY OF CHAR*/, left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return Code.makeSimpleExpression(JsString.concat(JsString.concat(rtl.strCmp(castToStr(left, rtl), castToStr(right, rtl)), JsString.make(op)), JsString.make("0")), Types.basic().bool);
 }
 
-function assign(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
+function assign(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
 	var designator = null;
 	var info = null;
 	var leftCode = null;var rightCode = null;
 	var leftType = null;var rightType = null;
 	var isArray = false;
 	var castOperation = null;
-	var rtl = null;
 	var castExp = null;
+	var ignored = false;
 	var result = null;
 	
 	function assignArrayFromString(a/*Array*/, s/*String*/){
-		var rtl = null;
 		if (Types.arrayLength(a) == Types.openArrayLength){
 			Errors.raise(JsString.concat(JsString.make("string cannot be assigned to open "), a.description()));
 		}
 		else if (Types.stringLen(s) > Types.arrayLength(a)){
 			Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.fromInt(Types.arrayLength(a)), JsString.make("-character ARRAY is too small for ")), JsString.fromInt(Types.stringLen(s))), JsString.make("-character string")));
 		}
-		rtl = c.rtl();
 		return rtl.assignArrayFromString(leftCode, rightCode);
 	}
 	designator = left.designator();
@@ -350,19 +356,22 @@ function assign(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
 		result = assignArrayFromString(RTL$.typeGuard(leftType, Types.Array), RTL$.typeGuard(rightType, Types.String));
 	}
 	else {
-		castOperation = Cast.implicit(rightType, leftType, castOperations);
-		if (castOperation == null){
+		if (Cast.implicit(rightType, leftType, false, castOperations, {set: function($v){castOperation = $v;}, get: function(){return castOperation;}}) != Cast.errNo){
 			Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.make("type mismatch: '"), leftCode), JsString.make("' is '")), leftType.description()), JsString.make("' and cannot be assigned to '")), rightType.description()), JsString.make("' expression")));
 		}
 		if (isArray && rightType instanceof Types.Array && Types.arrayLength(RTL$.typeGuard(leftType, Types.Array)) == Types.openArrayLength){
 			Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.make("'"), leftCode), JsString.make("' is open '")), leftType.description()), JsString.make("' and cannot be assigned")));
 		}
 		if (isArray || rightType instanceof Types.Record){
-			rtl = c.rtl();
 			result = rtl.copy(rightCode, leftCode);
 		}
 		else {
-			castExp = castOperation.make(c, Code.derefExpression(right));
+			if (castOperation != null){
+				castExp = castOperation.make(rtl, Code.derefExpression(right));
+			}
+			else {
+				castExp = Code.derefExpression(right);
+			}
 			rightCode = castExp.code();
 			if (info instanceof Types.VariableRef){
 				rightCode = JsString.concat(JsString.concat(JsString.make(".set("), rightCode), JsString.make(")"));
@@ -376,13 +385,13 @@ function assign(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
 	return result;
 }
 
-function inplace(left/*PExpression*/, right/*PExpression*/, c/*Type*/, code/*ARRAY OF CHAR*/, altOp/*BinaryProc*/){
+function inplace(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/, code/*ARRAY OF CHAR*/, altOp/*BinaryProc*/){
 	var designator = null;
 	var rightExp = null;
 	var result = null;
 	designator = left.designator();
 	if (designator.info() instanceof Types.VariableRef){
-		result = assign(left, altOp(left, right, c), c);
+		result = assign(left, altOp(left, right, rtl), rtl);
 	}
 	else {
 		rightExp = Code.derefExpression(right);
@@ -391,188 +400,196 @@ function inplace(left/*PExpression*/, right/*PExpression*/, c/*Type*/, code/*ARR
 	return result;
 }
 
-function addReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opAddReal, " + ", Precedence.addSub);
+function addReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opAddReal, " + ", Precedence.addSub);
+}
+
+function addInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryInt(left, right, rtl, opAddInt, " + ", Precedence.addSub);
+}
+
+function subReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opSubReal, " - ", Precedence.addSub);
 }
 
-function addInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryInt(left, right, c, opAddInt, " + ", Precedence.addSub);
+function subInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryInt(left, right, rtl, opSubInt, " - ", Precedence.addSub);
 }
 
-function subReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opSubReal, " - ", Precedence.addSub);
+function mulReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opMulReal, " * ", Precedence.mulDivMod);
 }
 
-function subInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryInt(left, right, c, opSubInt, " - ", Precedence.addSub);
+function mulInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryInt(left, right, rtl, opMulInt, " * ", Precedence.mulDivMod);
 }
 
-function mulReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opMulReal, " * ", Precedence.mulDivMod);
+function divReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opDivReal, " / ", Precedence.mulDivMod);
 }
 
-function mulInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryInt(left, right, c, opMulInt, " * ", Precedence.mulDivMod);
+function divInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryInt(left, right, rtl, opDivInt, " / ", Precedence.mulDivMod);
 }
 
-function divReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opDivReal, " / ", Precedence.mulDivMod);
+function mod(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opMod, " % ", Precedence.mulDivMod);
 }
 
-function divInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryInt(left, right, c, opDivInt, " / ", Precedence.mulDivMod);
+function setUnion(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opSetUnion, " | ", Precedence.bitOr);
 }
 
-function mod(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opMod, " % ", Precedence.mulDivMod);
+function setDiff(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opSetDiff, " & ~", Precedence.bitAnd);
 }
 
-function setUnion(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opSetUnion, " | ", Precedence.bitOr);
+function setIntersection(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opSetIntersection, " & ", Precedence.bitAnd);
 }
 
-function setDiff(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opSetDiff, " & ~", Precedence.bitAnd);
+function setSymmetricDiff(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opSetSymmetricDiff, " ^ ", Precedence.bitXor);
 }
 
-function setIntersection(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opSetIntersection, " & ", Precedence.bitAnd);
+function setInclL(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryPred(left, right, rtl, opSetInclL, codeSetInclL);
 }
 
-function setSymmetricDiff(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opSetSymmetricDiff, " ^ ", Precedence.bitXor);
+function setInclR(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryPred(left, right, rtl, opSetInclR, codeSetInclR);
 }
 
-function setInclL(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryPred(left, right, c, opSetInclL, codeSetInclL);
+function or(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opOr, " || ", Precedence.or);
 }
 
-function setInclR(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryPred(left, right, c, opSetInclR, codeSetInclR);
+function and(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opAnd, " && ", Precedence.and);
 }
 
-function or(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opOr, " || ", Precedence.or);
+function equalInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqualInt, " == ", Precedence.equal);
 }
 
-function and(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opAnd, " && ", Precedence.and);
+function equalReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqualReal, " == ", Precedence.equal);
 }
 
-function equalInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqualInt, " == ", Precedence.equal);
+function equalSet(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqualSet, " == ", Precedence.equal);
 }
 
-function equalReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqualReal, " == ", Precedence.equal);
+function equalStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" == ", left, right, rtl);
 }
 
-function equalStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" == ", left, right, c);
+function notEqualInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opNotEqualInt, " != ", Precedence.equal);
 }
 
-function notEqualInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opNotEqualInt, " != ", Precedence.equal);
+function notEqualReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opNotEqualReal, " != ", Precedence.equal);
 }
 
-function notEqualReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opNotEqualReal, " != ", Precedence.equal);
+function notEqualSet(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opNotEqualSet, " != ", Precedence.equal);
 }
 
-function notEqualStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" != ", left, right, c);
+function notEqualStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" != ", left, right, rtl);
 }
 
-function is(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCodeEx(left, right, c, null, " instanceof ", Precedence.relational, Types.basic().bool, Precedence.none);
+function is(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCodeEx(left, right, rtl, null, " instanceof ", Precedence.relational, Types.basic().bool, Precedence.none);
 }
 
-function lessInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opLessInt, " < ", Precedence.relational);
+function lessInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opLessInt, " < ", Precedence.relational);
 }
 
-function lessReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opLessReal, " < ", Precedence.relational);
+function lessReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opLessReal, " < ", Precedence.relational);
 }
 
-function lessStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" < ", left, right, c);
+function lessStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" < ", left, right, rtl);
 }
 
-function greaterInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opGreaterInt, " > ", Precedence.relational);
+function greaterInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opGreaterInt, " > ", Precedence.relational);
 }
 
-function greaterReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opGreaterReal, " > ", Precedence.relational);
+function greaterReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opGreaterReal, " > ", Precedence.relational);
 }
 
-function greaterStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" > ", left, right, c);
+function greaterStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" > ", left, right, rtl);
 }
 
-function eqLessInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqLessInt, " <= ", Precedence.relational);
+function eqLessInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqLessInt, " <= ", Precedence.relational);
 }
 
-function eqLessReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqLessReal, " <= ", Precedence.relational);
+function eqLessReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqLessReal, " <= ", Precedence.relational);
 }
 
-function eqLessStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" <= ", left, right, c);
+function eqLessStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" <= ", left, right, rtl);
 }
 
-function eqGreaterInt(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqGreaterInt, " >= ", Precedence.relational);
+function eqGreaterInt(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqGreaterInt, " >= ", Precedence.relational);
 }
 
-function eqGreaterReal(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opEqGreaterReal, " >= ", Precedence.relational);
+function eqGreaterReal(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opEqGreaterReal, " >= ", Precedence.relational);
 }
 
-function eqGreaterStr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return strCmp(" >= ", left, right, c);
+function eqGreaterStr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return strCmp(" >= ", left, right, rtl);
 }
 
-function not(x/*PExpression*/, c/*Type*/){
+function not(x/*PExpression*/, rtl/*PType*/){
 	return unary(x, opNot, "!");
 }
 
-function negateInt(x/*PExpression*/, c/*Type*/){
+function negateInt(x/*PExpression*/, rtl/*PType*/){
 	return promoteToWideIfNeeded(unary(x, opNegateInt, "-"));
 }
 
-function negateReal(x/*PExpression*/, c/*Type*/){
+function negateReal(x/*PExpression*/, rtl/*PType*/){
 	return promoteToWideIfNeeded(unary(x, opNegateReal, "-"));
 }
 
-function unaryPlus(x/*PExpression*/, c/*Type*/){
+function unaryPlus(x/*PExpression*/, rtl/*PType*/){
 	return unary(x, opUnaryPlus, "");
 }
 
-function setComplement(x/*PExpression*/, c/*Type*/){
+function setComplement(x/*PExpression*/, rtl/*PType*/){
 	return unary(x, opSetComplement, "~");
 }
 
-function lsl(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opLsl, " << ", Precedence.shift);
+function lsl(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opLsl, " << ", Precedence.shift);
 }
 
-function asr(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opAsr, " >> ", Precedence.shift);
+function asr(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opAsr, " >> ", Precedence.shift);
 }
 
-function ror(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return binaryWithCode(left, right, c, opRor, " >>> ", Precedence.shift);
+function ror(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return binaryWithCode(left, right, rtl, opRor, " >>> ", Precedence.shift);
 }
 
-function mulInplace(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return inplace(left, right, c, " *= ", mulReal);
+function mulInplace(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return inplace(left, right, rtl, " *= ", mulReal);
 }
 
-function divInplace(left/*PExpression*/, right/*PExpression*/, c/*Type*/){
-	return inplace(left, right, c, " /= ", divReal);
+function divInplace(left/*PExpression*/, right/*PExpression*/, rtl/*PType*/){
+	return inplace(left, right, rtl, " /= ", divReal);
 }
 
 function pow2(e/*PExpression*/){
@@ -590,13 +607,14 @@ function log2(e/*PExpression*/){
 function opCastToUint8(left/*PConst*/, right/*PConst*/){
 	return Code.makeIntConst(RTL$.typeGuard(left, Code.IntConst).value * RTL$.typeGuard(right, Code.IntConst).value | 0);
 }
-CastToUint8.prototype.make = function(context/*Type*/, e/*PExpression*/){
-	return binaryWithCode(e, Code.makeExpression(JsString.make("0xFF"), Types.basic().integer, null, Code.makeIntConst(255)), context, opCastToUint8, " & ", Precedence.bitAnd);
+CastToUint8.prototype.make = function(rtl/*PType*/, e/*PExpression*/){
+	return binaryWithCode(e, Code.makeExpression(JsString.make("0xFF"), Types.basic().integer, null, Code.makeIntConst(255)), rtl, opCastToUint8, " & ", Precedence.bitAnd);
 }
 openArrayChar = Types.makeArray(null, null, Types.basic().ch, Types.openArrayLength);
 castToUint8 = new CastToUint8();
 castOperations.castToUint8 = castToUint8;
 exports.castOperations = function(){return castOperations;};
+exports.binaryWithCode = binaryWithCode;
 exports.assign = assign;
 exports.addReal = addReal;
 exports.addInt = addInt;
@@ -617,9 +635,11 @@ exports.or = or;
 exports.and = and;
 exports.equalInt = equalInt;
 exports.equalReal = equalReal;
+exports.equalSet = equalSet;
 exports.equalStr = equalStr;
 exports.notEqualInt = notEqualInt;
 exports.notEqualReal = notEqualReal;
+exports.notEqualSet = notEqualSet;
 exports.notEqualStr = notEqualStr;
 exports.is = is;
 exports.lessInt = lessInt;

+ 38 - 34
src/js/Procedure.js

@@ -5,6 +5,9 @@ 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 Language = require("js/Language.js");
+var LanguageContext = require("js/LanguageContext.js");
+var OberonRtl = require("js/OberonRtl.js");
 var Object = require("js/Object.js");
 var Operator = require("js/Operator.js");
 var Precedence = require("js/CodePrecedence.js");
@@ -58,21 +61,22 @@ var GenArgCode = ArgumentsCode.extend({
 });
 var predefined = null;
 
-function checkArgument(actual/*PExpression*/, expected/*PProcedureArgument*/, pos/*INTEGER*/, code/*PArgumentsCode*/){
+function checkArgument(actual/*PExpression*/, expected/*PProcedureArgument*/, pos/*INTEGER*/, code/*PArgumentsCode*/, types/*PTypes*/){
 	var actualType = null;var expectType = null;
 	var designator = null;
 	var info = null;
 	var result = null;
+	var castErr = 0;
 	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)){
+		castErr = types.implicitCast(actualType, expectType, expected.isVar, Operator.castOperations(), {set: function($v){result = $v;}, get: function(){return result;}});
+		if (castErr == Cast.errVarParameter){
 			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("'")));
 		}
+		else if (castErr != Cast.errNo){
+			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){
 		designator = actual.designator();
@@ -92,7 +96,7 @@ function checkArgument(actual/*PExpression*/, expected/*PProcedureArgument*/, po
 	}
 }
 
-function checkArgumentsType(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/){
+function checkArgumentsType(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/, types/*PTypes*/){
 	var actualLen = 0;
 	var i = 0;
 	var actualExp = null;
@@ -102,7 +106,7 @@ function checkArgumentsType(actual/*Type*/, expected/*Type*/, code/*PArgumentsCo
 		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);
+			checkArgument(RTL$.typeGuard(actualExp, Code.Expression), RTL$.typeGuard(expectedArg, Types.ProcedureArgument), i, code, types);
 			++i;
 		} else break;
 	}
@@ -114,13 +118,13 @@ function checkArgumentsCount(actual/*INTEGER*/, expected/*INTEGER*/){
 	}
 }
 
-function processArguments(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/){
+function processArguments(actual/*Type*/, expected/*Type*/, code/*PArgumentsCode*/, types/*PTypes*/){
 	checkArgumentsCount(JsArray.len(actual), JsArray.len(expected));
-	checkArgumentsType(actual, expected, code);
+	checkArgumentsType(actual, expected, code, types);
 }
 
-function checkArguments(actual/*Type*/, expected/*Type*/){
-	processArguments(actual, expected, null);
+function checkArguments(actual/*Type*/, expected/*Type*/, types/*PTypes*/){
+	processArguments(actual, expected, null, types);
 }
 
 function initStd(name/*Type*/, call/*PCall*/, result/*Std*/){
@@ -162,7 +166,7 @@ GenArgCode.prototype.write = function(actual/*PExpression*/, expected/*PProcedur
 		this.code = JsString.concat(this.code, JsString.make(", "));
 	}
 	if (cast != null){
-		e = cast.make(this.cx, actual);
+		e = cast.make(this.cx.rtl(), actual);
 	}
 	else {
 		e = actual;
@@ -190,7 +194,7 @@ function makeProcCallGeneratorWithCustomArgs(cx/*PType*/, id/*Type*/, type/*Defi
 		var i = 0;
 		expectedArgs = this.args;
 		if (expectedArgs != null){
-			processArguments(args, expectedArgs, this.argumentsCode);
+			processArguments(args, expectedArgs, this.argumentsCode, cx.types());
 		}
 		else {
 			for (i = 0; i <= JsArray.len(args) - 1 | 0; ++i){
@@ -268,9 +272,9 @@ function hasVarArgumnetWithCustomType(call/*PStdCall*/){
 	JsArray.add(call.args, a);
 }
 
-function checkSingleArgument(actual/*Type*/, call/*StdCall*/){
+function checkSingleArgument(actual/*Type*/, call/*StdCall*/, types/*PTypes*/){
 	RTL$.assert(JsArray.len(call.args) == 1);
-	checkArguments(actual, call.args);
+	checkArguments(actual, call.args, types);
 	RTL$.assert(JsArray.len(actual) == 1);
 	return nthArgument(actual, 0);
 }
@@ -286,7 +290,7 @@ function makeNew(){
 		var arg = null;
 		var argType = null;
 		var baseType = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		argType = arg.type();
 		if (!(argType instanceof Types.Pointer)){
 			Errors.raise(JsString.concat(JsString.concat(JsString.make("POINTER variable expected, got '"), argType.description()), JsString.make("'")));
@@ -313,7 +317,7 @@ function makeLen(){
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
 		var argType = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		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("'")));
@@ -337,7 +341,7 @@ function makeOdd(){
 		var arg = null;
 		var code = null;
 		var constValue = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		code = Code.adjustPrecedence(arg, Precedence.bitAnd);
 		constValue = arg.constValue();
 		if (constValue != null){
@@ -361,7 +365,7 @@ function makeAssert(){
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
 		var rtl = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		rtl = cx.rtl();
 		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.concat(rtl.assertId(), JsString.make("(")), arg.code()), JsString.make(")")), null);
 	}
@@ -387,13 +391,13 @@ function setBitImpl(name/*ARRAY OF CHAR*/, bitOp/*BinaryOpStr*/){
 		var valueCodeExp = null;
 		var valueCode = null;
 		var comment = null;
-		checkArguments(args, this.args);
+		checkArguments(args, this.args, cx.types());
 		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);
+			valueCodeExp = Operator.lsl(Code.makeExpression(JsString.make("1"), Types.basic().integer, null, Code.makeIntConst(1)), y, cx.rtl());
 			valueCode = valueCodeExp.code();
 		}
 		else {
@@ -449,7 +453,7 @@ function incImpl(name/*ARRAY OF CHAR*/, unary/*ARRAY OF CHAR*/, incOp/*BinaryOpS
 		var value = null;
 		var valueCode = null;
 		checkVariableArgumentsCount(1, 2, args);
-		checkArgumentsType(args, this.args, null);
+		checkArgumentsType(args, this.args, null, cx.types());
 		x = nthArgument(args, 0);
 		if (JsArray.len(args) == 1){
 			code = JsString.concat(this.unary, x.code());
@@ -506,7 +510,7 @@ function makeAbs(){
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
 		var argType = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		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("'")));
@@ -528,7 +532,7 @@ function makeFloor(){
 	var call = null;
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		return Code.makeSimpleExpression(JsString.concat(JsString.concat(JsString.make("Math.floor("), arg.code()), JsString.make(")")), Types.basic().integer);
 	}
 	call = new CallImpl();
@@ -547,7 +551,7 @@ function makeFlt(){
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
 		var value = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		value = arg.constValue();
 		if (value != null){
 			value = Code.makeRealConst(RTL$.typeGuard(value, Code.IntConst).value);
@@ -571,11 +575,11 @@ function bitShiftImpl(name/*ARRAY OF CHAR*/, op/*BinaryOp*/){
 	var call = null;
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var x = null;var y = null;
-		checkArguments(args, this.args);
+		checkArguments(args, this.args, cx.types());
 		RTL$.assert(JsArray.len(args) == 2);
 		x = nthArgument(args, 0);
 		y = nthArgument(args, 1);
-		return this.op(x, y, cx);
+		return this.op(x, y, cx.rtl());
 	}
 	call = new CallImpl();
 	initStdCall(call);
@@ -600,7 +604,7 @@ function makeOrd(){
 		var code = null;
 		var ch = 0;
 		var result = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		argType = arg.type();
 		if (argType == Types.basic().ch || argType == Types.basic().set){
 			value = arg.constValue();
@@ -636,7 +640,7 @@ function makeChr(){
 	var call = null;
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var arg = null;
-		arg = checkSingleArgument(args, this);
+		arg = checkSingleArgument(args, this, cx.types());
 		return Code.makeSimpleExpression(arg.code(), Types.basic().ch);
 	}
 	call = new CallImpl();
@@ -654,10 +658,10 @@ function makePack(){
 	var call = null;
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var x = null;var y = null;
-		checkArguments(args, this.args);
+		checkArguments(args, this.args, cx.types());
 		x = nthArgument(args, 0);
 		y = nthArgument(args, 1);
-		return Code.makeSimpleExpression(Operator.mulInplace(x, Operator.pow2(y), cx), null);
+		return Code.makeSimpleExpression(Operator.mulInplace(x, Operator.pow2(y), cx.rtl()), null);
 	}
 	call = new CallImpl();
 	initStdCall(call);
@@ -675,10 +679,10 @@ function makeUnpk(){
 	var call = null;
 	CallImpl.prototype.make = function(args/*Type*/, cx/*Type*/){
 		var x = null;var y = null;
-		checkArguments(args, this.args);
+		checkArguments(args, this.args, cx.types());
 		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);
+		return Code.makeSimpleExpression(JsString.concat(JsString.concat(Operator.assign(y, Operator.log2(x), cx.rtl()), JsString.make("; ")), Operator.divInplace(x, Operator.pow2(y), cx.rtl())), null);
 	}
 	call = new CallImpl();
 	initStdCall(call);

+ 20 - 14
src/js/Scope.js

@@ -1,16 +1,17 @@
 var RTL$ = require("rtl.js");
-var Context = require("js/Context.js");
 var Errors = require("js/Errors.js");
 var JsArray = require("js/JsArray.js");
 var JsMap = require("js/JsMap.js");
 var JsString = require("js/JsString.js");
 var Object = require("js/Object.js");
 var Procedures = require("js/Procedure.js");
+var ScopeBase = require("js/ScopeBase.js");
 var Symbols = require("js/Symbols.js");
 var Types = require("js/Types.js");
-var Type = Context.Scope.extend({
+var Type = ScopeBase.Type.extend({
 	init: function Type(){
-		Context.Scope.prototype.init.call(this);
+		ScopeBase.Type.prototype.init.call(this);
+		this.stdSymbols = null;
 		this.symbols = null;
 		this.unresolved = null;
 		this.finalizers = null;
@@ -41,7 +42,12 @@ var Finalizer = Object.Type.extend({
 		this.closure = null;
 	}
 });
-var stdSymbols = null;
+
+function addSymbolForType(t/*PBasicType*/, result/*Type*/){
+	var name = null;
+	name = Types.typeName(t);
+	JsMap.put(result, name, Symbols.makeSymbol(name, Types.makeTypeId(t)));
+}
 
 function makeStdSymbols(){
 	var result = null;
@@ -49,9 +55,7 @@ function makeStdSymbols(){
 	var proc = null;
 	
 	function addSymbol(t/*PBasicType*/){
-		var name = null;
-		name = Types.typeName(t);
-		JsMap.put(result, name, Symbols.makeSymbol(name, Types.makeTypeId(t)));
+		addSymbolForType(t, result);
 	}
 	result = JsMap.make();
 	addSymbol(Types.basic().bool);
@@ -67,7 +71,8 @@ function makeStdSymbols(){
 	return result;
 }
 
-function init(scope/*Type*/){
+function init(scope/*Type*/, stdSymbols/*Type*/){
+	scope.stdSymbols = stdSymbols;
 	scope.symbols = JsMap.make();
 	scope.unresolved = JsArray.makeStrings();
 	scope.finalizers = JsArray.make();
@@ -81,10 +86,10 @@ function makeCompiledModule(name/*Type*/){
 	return result;
 }
 
-function makeModule(name/*Type*/){
+function makeModule(name/*Type*/, stdSymbols/*Type*/){
 	var result = null;
 	result = new Module();
-	init(result);
+	init(result, stdSymbols);
 	result.exports = JsMap.make();
 	result.symbol = Symbols.makeSymbol(name, makeCompiledModule(name));
 	result.addSymbol(result.symbol, false);
@@ -153,7 +158,7 @@ Type.prototype.findSymbol = function(id/*Type*/){
 	var result = null;
 	var void$ = false;
 	if (!JsMap.find(this.symbols, id, {set: function($v){result = $v;}, get: function(){return result;}})){
-		void$ = JsMap.find(stdSymbols, id, {set: function($v){result = $v;}, get: function(){return result;}});
+		void$ = JsMap.find(this.stdSymbols, id, {set: function($v){result = $v;}, get: function(){return result;}});
 	}
 	return RTL$.typeGuard(result, Symbols.Symbol);
 }
@@ -166,10 +171,10 @@ Procedure.prototype.addSymbol = function(s/*PSymbol*/, exported/*BOOLEAN*/){
 	Type.prototype.addSymbol.call(this, s, exported);
 }
 
-function makeProcedure(){
+function makeProcedure(stdSymbols/*Type*/){
 	var result = null;
 	result = new Procedure();
-	init(result);
+	init(result, stdSymbols);
 	return result;
 }
 
@@ -209,9 +214,10 @@ function moduleSymbol(m/*Module*/){
 function moduleExports(m/*Module*/){
 	return m.exports;
 }
-stdSymbols = makeStdSymbols();
 exports.Procedure = Procedure;
 exports.Module = Module;
+exports.addSymbolForType = addSymbolForType;
+exports.makeStdSymbols = makeStdSymbols;
 exports.makeModule = makeModule;
 exports.addUnresolved = addUnresolved;
 exports.resolve = resolve;

+ 7 - 0
src/js/ScopeBase.js

@@ -0,0 +1,7 @@
+var RTL$ = require("rtl.js");
+var Object = require("js/Object.js");
+var Type = RTL$.extend({
+	init: function Type(){
+	}
+});
+exports.Type = Type;

+ 2 - 2
src/js/Symbols.js

@@ -1,7 +1,7 @@
 var RTL$ = require("rtl.js");
-var Context = require("js/Context.js");
 var JsString = require("js/JsString.js");
 var Object = require("js/Object.js");
+var ScopeBase = require("js/ScopeBase.js");
 var Types = require("js/Types.js");
 var Symbol = Object.Type.extend({
 	init: function Symbol(){
@@ -52,7 +52,7 @@ function makeSymbol(id/*Type*/, info/*PId*/){
 	return result;
 }
 
-function makeFound(s/*PSymbol*/, scope/*PScope*/){
+function makeFound(s/*PSymbol*/, scope/*PType*/){
 	var result = null;
 	result = new FoundSymbol();
 	result.mSymbol = s;

+ 5 - 3
src/js/Types.js

@@ -6,6 +6,7 @@ var JsArray = require("js/JsArray.js");
 var JsMap = require("js/JsMap.js");
 var JsString = require("js/JsString.js");
 var Object = require("js/Object.js");
+var ScopeBase = require("js/ScopeBase.js");
 var openArrayLength = 0;
 var Id = Object.Type.extend({
 	init: function Id(){
@@ -183,7 +184,7 @@ Record.prototype.finalize = function(){
 	this.notExported = null;
 }
 
-function initRecord(r/*PRecord*/, name/*Type*/, cons/*Type*/, scope/*PScope*/){
+function initRecord(r/*PRecord*/, name/*Type*/, cons/*Type*/, scope/*PType*/){
 	r.name = name;
 	r.cons = cons;
 	r.scope = scope;
@@ -192,7 +193,7 @@ function initRecord(r/*PRecord*/, name/*Type*/, cons/*Type*/, scope/*PScope*/){
 	scope.addFinalizer(finalizeRecord, r);
 }
 
-function makeNonExportedRecord(cons/*Type*/, scope/*PScope*/, base/*PRecord*/){
+function makeNonExportedRecord(cons/*Type*/, scope/*PType*/, base/*PRecord*/){
 	var result = null;
 	result = new NonExportedRecord();
 	initRecord(result, null, cons, scope);
@@ -513,7 +514,7 @@ function makePointer(name/*Type*/, base/*PTypeId*/){
 	return result;
 }
 
-function makeRecord(name/*Type*/, cons/*Type*/, scope/*PScope*/){
+function makeRecord(name/*Type*/, cons/*Type*/, scope/*PType*/){
 	var result = null;
 	result = new Record();
 	initRecord(result, name, cons, scope);
@@ -617,6 +618,7 @@ exports.isInt = isInt;
 exports.intsDescription = intsDescription;
 exports.isString = isString;
 exports.moduleName = moduleName;
+exports.makeBasic = makeBasic;
 exports.recordBase = recordBase;
 exports.setRecordBase = setRecordBase;
 exports.recordScope = recordScope;

+ 19 - 7
src/nodejs.js

@@ -53,7 +53,7 @@ function writeCompiledModule(name, code, outDir){
     fs.writeFileSync(filePath, code);
     }
 
-function compile(sources, grammar, handleErrors, outDir, importDir){
+function compile(sources, language, handleErrors, includeDirs, outDir, importDir){
     var rtlCodeWatcher = new RtlCodeUsingWatcher();
     var rtl = new RTL(rtlCodeWatcher.using.bind(rtlCodeWatcher));
     var moduleCode = function(name, imports){
@@ -67,14 +67,26 @@ function compile(sources, grammar, handleErrors, outDir, importDir){
                 if (!path.extname(fileName).length)
                     fileName += ".ob";
                 compiledFilesStack.push(fileName);
-                return fs.readFileSync(fileName, "utf8");
+
+                var readPath = fileName;
+                var i = 0;
+                while (!fs.existsSync(readPath) && i < includeDirs.length){
+                    readPath = path.join(includeDirs[i], fileName);
+                    ++i;
+                }
+                if (!fs.existsSync(readPath))
+                    throw new Error("cannot find file: '" + fileName + "' in " + includeDirs);
+                return fs.readFileSync(readPath, "utf8");
             },
-            grammar,
+            language.grammar,
             function(moduleResolver){return new Context.Context(
-                Code.makeGenerator(),
-                moduleCode,
-                rtl,
-                moduleResolver);},
+                { codeGenerator: Code.makeGenerator(),
+                  moduleGenerator: moduleCode,
+                  rtl: rtl,
+                  types: language.types,
+                  stdSymbols: language.stdSymbols,
+                  moduleResolver: moduleResolver
+                });},
             function(e){handleErrors("File \"" + compiledFilesStack[compiledFilesStack.length - 1] + "\", " + e);},
             function(name, code){
                 if (rtlCodeWatcher.used()){

+ 44 - 28
src/ob/Cast.ob

@@ -1,16 +1,19 @@
 MODULE Cast;
-IMPORT Code, Context, JsArray, JsString, Object, Types;
-
+IMPORT Code, OberonRtl, JsArray, JsString, Object, Types;
+CONST
+    errNo* = 0;
+    err* = 1;
+    errVarParameter* = 2;
 TYPE
     CastOp* = RECORD(Object.Type)
-        PROCEDURE make*(c: Context.Type; e: Code.PExpression): Code.PExpression
+        PROCEDURE make*(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression
     END;
 
     PCastOp* = POINTER TO CastOp;
-
+(*
     CastOpDoNothing = RECORD (CastOp)
     END;
-
+*)
     CastOpStrToChar = RECORD (CastOp)
         c: CHAR
     END;
@@ -22,12 +25,12 @@ TYPE
 VAR
     (*workaround recursive usage*)
     areTypesExactlyMatch: PROCEDURE (t1: Types.PType; t2: Types.PType): BOOLEAN;
-    doNothing: POINTER TO CastOpDoNothing;
-
-PROCEDURE CastOpDoNothing.make(c: Context.Type; e: Code.PExpression): Code.PExpression;
+    doNothing*: POINTER TO CastOpDoNothing;
+(*
+PROCEDURE CastOpDoNothing.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
     RETURN e
 END CastOpDoNothing.make;
-
+*)
 PROCEDURE findBaseType(base: Types.PRecord; type: Types.PRecord): Types.PRecord;
 BEGIN
     WHILE (type # NIL) & (type # base) DO
@@ -114,7 +117,7 @@ BEGIN
     RETURN result
 END areTypesExactlyMatchImpl;
 
-PROCEDURE CastOpStrToChar.make(c: Context.Type; e: Code.PExpression): Code.PExpression;
+PROCEDURE CastOpStrToChar.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
     RETURN Code.makeSimpleExpression(JsString.fromInt(ORD(SELF.c)), Types.basic.ch)
 END CastOpStrToChar.make;
 
@@ -127,45 +130,58 @@ BEGIN
     RETURN result
 END makeCastOpStrToChar;
 
-PROCEDURE implicit*(from, to: Types.PType; ops: Operations): PCastOp;
+PROCEDURE implicit*(from, to: Types.PType; toVar: BOOLEAN; ops: Operations; VAR op: PCastOp): INTEGER;
 VAR
-    result: PCastOp;
+    result: INTEGER;
     c: CHAR;
+    ignore: BOOLEAN;
 BEGIN
+    result := err;
+    op := NIL;
     IF from = to THEN
-        result := doNothing;
+        result := errNo;
     ELSIF (from = Types.basic.uint8) & (to = Types.basic.integer) THEN
-        result := doNothing;
+        IF toVar THEN
+            result := errVarParameter;
+        ELSE
+            result := errNo;
+        END;
     ELSIF (from = Types.basic.integer) & (to = Types.basic.uint8) THEN
-        result := ops.castToUint8;
+        IF toVar THEN
+            result := errVarParameter;
+        ELSE
+            op := ops.castToUint8;
+            result := errNo;
+        END;
     ELSIF from IS Types.PString THEN
         IF to = Types.basic.ch THEN
             IF Types.stringAsChar(from(Types.PString)^, c) THEN
-                result := makeCastOpStrToChar(c);
+                op := makeCastOpStrToChar(c);
+                result := errNo;
             END;
-        ELSIF (to IS Types.PArray) & (Types.arrayElementsType(to(Types.PArray)^) = Types.basic.ch) THEN
-            result := doNothing;
+        ELSIF Types.isString(to) THEN
+            result := errNo;
         END;
     ELSIF (from IS Types.PArray) & (to IS Types.PArray) THEN
-        IF     (Types.arrayLength(to(Types.PArray)^) = Types.openArrayLength)
-            OR (Types.arrayLength(to(Types.PArray)^) = Types.arrayLength(from(Types.PArray)^)) THEN
-            result := implicit(Types.arrayElementsType(from(Types.PArray)^), 
-                               Types.arrayElementsType(to(Types.PArray)^),
-                               ops);
+        IF ((Types.arrayLength(from(Types.PArray)^) = Types.arrayLength(to(Types.PArray)^))
+                OR (Types.arrayLength(to(Types.PArray)^) = Types.openArrayLength))
+            & areTypesExactlyMatch(Types.arrayElementsType(from(Types.PArray)^), 
+                                   Types.arrayElementsType(to(Types.PArray)^)) THEN
+            result := errNo;
         END;
     ELSIF (from IS Types.PPointer) & (to IS Types.PPointer) THEN
         IF findPointerBaseType(to(Types.PPointer), from(Types.PPointer)^) # NIL THEN
-            result := doNothing;
+            result := errNo;
         END;
     ELSIF (from IS Types.PRecord) & (to IS Types.PRecord) THEN
         IF findBaseType(to(Types.PRecord), from(Types.PRecord)) # NIL THEN
-            result := doNothing;
+            result := errNo;
         END;
     ELSIF (from = Types.nil) & matchesToNIL(to^) THEN
-        result := doNothing;
+        result := errNo;
     ELSIF (from IS Types.PDefinedProcedure) & (to IS Types.PDefinedProcedure) THEN
         IF areProceduresMatch(from(Types.PDefinedProcedure), to(Types.PDefinedProcedure)) THEN
-            result := doNothing;
+            result := errNo;
         END
     END;
     RETURN result
@@ -173,5 +189,5 @@ END implicit;
 
 BEGIN
     areTypesExactlyMatch := areTypesExactlyMatchImpl;
-    NEW(doNothing);
+(*)    NEW(doNothing);*)
 END Cast.

+ 18 - 5
src/ob/Code.ob

@@ -1,5 +1,5 @@
 MODULE Code;
-IMPORT JsMap, JsString, Context, Object, Stream, Symbols, Precedence := CodePrecedence, Types;
+IMPORT JsMap, JsString, Object, Stream, ScopeBase, Symbols, Precedence := CodePrecedence, Types;
 
 CONST
     kTab = 09X;
@@ -33,14 +33,14 @@ TYPE
         PROCEDURE refCode(): RefCodeProc;
         PROCEDURE type(): Types.PType;
         PROCEDURE info*(): Types.PId;
-        PROCEDURE scope(): Context.PScope;
+        PROCEDURE scope(): ScopeBase.PType;
 
         mCode: JsString.Type;
         mLval: JsString.Type;
         mRefCode: RefCodeProc;
         mType: Types.PType;
         mInfo: Types.PId;
-        mScope: Context.PScope
+        mScope: ScopeBase.PType
     END;
 
     PDesignator* = POINTER TO Designator;
@@ -61,6 +61,10 @@ TYPE
         value*: SET
     END;
 
+    StrConst* = RECORD(Const)
+        value*: JsString.Type
+    END;
+
     StringConst = RECORD (Const)
         value: JsString.Type
     END;
@@ -233,6 +237,15 @@ BEGIN
     RETURN result
 END makeSetConst;
 
+PROCEDURE makeStrConst*(s: JsString.Type): PConst;
+VAR
+    result: POINTER TO StrConst;
+BEGIN
+    NEW(result);
+    result.value := s;
+    RETURN result
+END makeStrConst;
+
 PROCEDURE makeStringConst*(s: JsString.Type): PConst;
 VAR
     result: POINTER TO StringConst;
@@ -297,11 +310,11 @@ PROCEDURE Designator.info(): Types.PId;
     RETURN SELF.mInfo
 END Designator.info;
 
-PROCEDURE Designator.scope(): Context.PScope;
+PROCEDURE Designator.scope(): ScopeBase.PType;
     RETURN SELF.mScope
 END Designator.scope;
 
-PROCEDURE makeDesignator*(code: JsString.Type; lval: JsString.Type; refCode: RefCodeProc; type: Types.PType; info: Types.PId; scope: Context.PScope): PDesignator;
+PROCEDURE makeDesignator*(code: JsString.Type; lval: JsString.Type; refCode: RefCodeProc; type: Types.PType; info: Types.PId; scope: ScopeBase.PType): PDesignator;
 VAR
     result: PDesignator;
 BEGIN

+ 4 - 20
src/ob/Context.ob

@@ -1,31 +1,15 @@
 MODULE Context;
-IMPORT JsString, Object;
+IMPORT JsString, OberonRtl, Object, ScopeBase;
 TYPE
-    FinalizerProc* = PROCEDURE(closure: Object.PType);
-
-    Scope* = RECORD
-        PROCEDURE addFinalizer*(finalizer: FinalizerProc; closure: Object.PType)
-    END;
-    PScope* = POINTER TO Scope;
-
-    Rtl* = RECORD
-        copy*: PROCEDURE(s1, s2: JsString.Type): JsString.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;
-        assertId*: PROCEDURE(): JsString.Type
-    END;
-    PRtl* = POINTER TO Rtl;
-
     Type* = RECORD
         handleChar*:    PROCEDURE(c: CHAR);
         handleLiteral*: PROCEDURE(s: JsString.Type): BOOLEAN;
         handleString*:  PROCEDURE(s: JsString.Type);
         handleIdent*:   PROCEDURE(s: JsString.Type);
         isLexem*:       PROCEDURE(): BOOLEAN;
-        qualifyScope*:  PROCEDURE(scope: PScope): JsString.Type;
-        rtl*:           PROCEDURE(): PRtl
+        qualifyScope*:  PROCEDURE(scope: ScopeBase.PType): JsString.Type;
+        
+        PROCEDURE rtl*(): OberonRtl.PType
     END;
     PType* = POINTER TO Type;
 

+ 13 - 0
src/ob/Language.ob

@@ -0,0 +1,13 @@
+MODULE Language;
+IMPORT Cast, JsString, T := Types;
+TYPE
+    Types* = RECORD         
+        PROCEDURE implicitCast*(from, to: T.PType; toVar: BOOLEAN; ops: Cast.Operations; VAR op: Cast.PCastOp): INTEGER
+    END;
+    PTypes* = POINTER TO Types;
+(*)
+    Type* = RECORD
+        types*: PTypes;
+        rtl*:   PRtl
+    END;*)
+END Language.

+ 10 - 0
src/ob/LanguageContext.ob

@@ -0,0 +1,10 @@
+MODULE LanguageContext;
+IMPORT Context, Language;
+
+TYPE
+    Type* = RECORD(Context.Type)
+        PROCEDURE types*(): Language.PTypes
+    END;
+    PType* = POINTER TO Type;
+
+END LanguageContext.

+ 13 - 0
src/ob/OberonRtl.ob

@@ -0,0 +1,13 @@
+MODULE OberonRtl;
+IMPORT JsString;
+TYPE
+    Type* = RECORD
+        copy*: PROCEDURE(s1, s2: JsString.Type): JsString.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;
+        assertId*: PROCEDURE(): JsString.Type
+    END;
+    PType* = POINTER TO Type;
+END OberonRtl.

+ 150 - 137
src/ob/Operator.ob

@@ -1,15 +1,15 @@
 MODULE Operator;
-IMPORT Cast, Code, Context, Errors, JsString, Precedence := CodePrecedence, Types;
+IMPORT Cast, Code, Errors, JsString, OberonRtl, Precedence := CodePrecedence, Types;
 TYPE
-    BinaryProc = PROCEDURE(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
+    BinaryProc = PROCEDURE(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
 
     BinaryOp = PROCEDURE(left, right: Code.PConst): Code.PConst;
-    CodePredicate = PROCEDURE(left, right: JsString.Type; c: Context.Type): JsString.Type;
+    CodePredicate = PROCEDURE(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
 
     UnaryOp = PROCEDURE(value: Code.PConst): Code.PConst;
 
     CodeMaker = RECORD
-        PROCEDURE make(left, right: JsString.Type; c: Context.Type): JsString.Type
+        PROCEDURE make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type
     END;
     PCodeMaker = POINTER TO CodeMaker;
 
@@ -34,7 +34,7 @@ VAR
 
 PROCEDURE binary(
     left, right: Code.PExpression; 
-    context: Context.Type;
+    rtl: OberonRtl.PType;
     op: BinaryOp;
     code: PCodeMaker;
     precedence: INTEGER;
@@ -65,7 +65,7 @@ BEGIN
         rightCode := rightExpDeref.code();
     END;
 
-    resultCode := code.make(leftCode, rightCode, context);
+    resultCode := code.make(leftCode, rightCode, rtl);
 
     IF optResultType # NIL THEN
         resultType := optResultType;
@@ -81,21 +81,21 @@ BEGIN
     RETURN Code.makeExpressionWithPrecedence(resultCode, resultType, NIL, resultValue, resultPrecedence)
 END binary;
 
-PROCEDURE SimpleCodeMaker.make(left, right: JsString.Type; c: Context.Type): JsString.Type;
+PROCEDURE SimpleCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
     RETURN JsString.concat(JsString.concat(
         left, 
         SELF.code), 
         right)
 END SimpleCodeMaker.make;
 
-PROCEDURE IntCodeMaker.make(left, right: JsString.Type; c: Context.Type): JsString.Type;
+PROCEDURE IntCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
 BEGIN
-    RETURN JsString.concat(SUPER(left, right, c), JsString.make(" | 0"))
+    RETURN JsString.concat(SUPER(left, right, rtl), JsString.make(" | 0"))
 END IntCodeMaker.make;
 
-PROCEDURE PredCodeMaker.make(left, right: JsString.Type; c: Context.Type): JsString.Type;
+PROCEDURE PredCodeMaker.make(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
 BEGIN
-    RETURN SELF.pred(left, right, c)
+    RETURN SELF.pred(left, right, rtl)
 END PredCodeMaker.make;
 
 PROCEDURE makeSimpleCodeMaker(code: ARRAY OF CHAR): PCodeMaker;
@@ -127,7 +127,7 @@ END makePredCodeMaker;
 
 PROCEDURE binaryWithCodeEx(
     left, right: Code.PExpression; 
-    context: Context.Type;
+    rtl: OberonRtl.PType;
     op: BinaryOp;
     code: ARRAY OF CHAR;
     precedence: INTEGER;
@@ -137,7 +137,7 @@ PROCEDURE binaryWithCodeEx(
     RETURN binary(
         left, 
         right, 
-        context, 
+        rtl, 
         op, 
         makeSimpleCodeMaker(code), 
         precedence, 
@@ -145,14 +145,14 @@ PROCEDURE binaryWithCodeEx(
         optResultPrecedence)
 END binaryWithCodeEx;
 
-PROCEDURE binaryWithCode(
+PROCEDURE binaryWithCode*(
     left, right: Code.PExpression; 
-    c: Context.Type;
+    rtl: OberonRtl.PType;
     op: BinaryOp;
     code: ARRAY OF CHAR;
     precedence: INTEGER
     ): Code.PExpression;
-    RETURN binaryWithCodeEx(left, right, c, op, code, precedence, NIL, Precedence.none)
+    RETURN binaryWithCodeEx(left, right, rtl, op, code, precedence, NIL, Precedence.none)
 END binaryWithCode;
 
 PROCEDURE promoteToWideIfNeeded(e: Code.PExpression): Code.PExpression;
@@ -174,7 +174,7 @@ END promoteToWideIfNeeded;
 
 PROCEDURE binaryInt(
     left, right: Code.PExpression; 
-    c: Context.Type;
+    rtl: OberonRtl.PType;
     op: BinaryOp;
     code: ARRAY OF CHAR;
     precedence: INTEGER
@@ -182,7 +182,7 @@ PROCEDURE binaryInt(
     RETURN promoteToWideIfNeeded(binary(
         left, 
         right, 
-        c, 
+        rtl, 
         op, 
         makeIntCodeMaker(code), 
         precedence, 
@@ -193,14 +193,14 @@ END binaryInt;
 
 PROCEDURE binaryPred(
     left, right: Code.PExpression; 
-    c: Context.Type;
+    rtl: OberonRtl.PType;
     op: BinaryOp;
     pred: CodePredicate
     ): Code.PExpression;
     RETURN binary(
         left, 
         right, 
-        c, 
+        rtl, 
         op, 
         makePredCodeMaker(pred), 
         Precedence.none, 
@@ -224,13 +224,18 @@ BEGIN
     RETURN Code.makeExpression(resultCode, e.type(), NIL, value)
 END unary;
 
-PROCEDURE castToStr(e: Code.PExpression; context: Context.Type): JsString.Type;
+PROCEDURE castToStr(e: Code.PExpression; rtl: OberonRtl.PType): JsString.Type;
 VAR
     resultExpression: Code.PExpression;
     op: Cast.PCastOp;
+    ignored: INTEGER;
 BEGIN
-    op := Cast.implicit(e.type(), openArrayChar, castOperations);
-    resultExpression := op.make(context, e)
+    ignored := Cast.implicit(e.type(), openArrayChar, FALSE, castOperations, op);
+    IF op # NIL THEN
+        resultExpression := op.make(rtl, e)
+    ELSE
+        resultExpression := e;
+    END;
     RETURN resultExpression.code()
 END castToStr;
 
@@ -329,6 +334,11 @@ PROCEDURE opEqualReal(left, right: Code.PConst): Code.PConst;
                                = right^(Code.RealConst).value))
 END opEqualReal;
 
+PROCEDURE opEqualSet(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(ORD(left^(Code.SetConst).value
+                               = right^(Code.SetConst).value))
+END opEqualSet;
+
 PROCEDURE opNotEqualInt(left, right: Code.PConst): Code.PConst;
     RETURN Code.makeIntConst(ORD(left^(Code.IntConst).value
                                # right^(Code.IntConst).value))
@@ -339,6 +349,11 @@ PROCEDURE opNotEqualReal(left, right: Code.PConst): Code.PConst;
                                # right^(Code.RealConst).value))
 END opNotEqualReal;
 
+PROCEDURE opNotEqualSet(left, right: Code.PConst): Code.PConst;
+    RETURN Code.makeIntConst(ORD(left^(Code.SetConst).value
+                               # right^(Code.SetConst).value))
+END opNotEqualSet;
+
 PROCEDURE opLessInt(left, right: Code.PConst): Code.PConst;
     RETURN Code.makeIntConst(ORD(left^(Code.IntConst).value
                                < right^(Code.IntConst).value))
@@ -414,36 +429,26 @@ PROCEDURE opRor(left, right: Code.PConst): Code.PConst;
                                  right^(Code.IntConst).value))
 END opRor;
 
-PROCEDURE codeSetInclL(left, right: JsString.Type; c: Context.Type): JsString.Type;
-VAR
-    rtl: Context.PRtl;
+PROCEDURE codeSetInclL(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
 BEGIN
-    rtl := c.rtl();
     RETURN rtl.setInclL(left, right)
 END codeSetInclL;
 
-PROCEDURE codeSetInclR(left, right: JsString.Type; c: Context.Type): JsString.Type;
-VAR
-    rtl: Context.PRtl;
-BEGIN
-    rtl := c.rtl();
+PROCEDURE codeSetInclR(left, right: JsString.Type; rtl: OberonRtl.PType): JsString.Type;
     RETURN rtl.setInclR(left, right)
 END codeSetInclR;
 
-PROCEDURE strCmp(op: ARRAY OF CHAR; left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-VAR
-    rtl: Context.PRtl;
+PROCEDURE strCmp(op: ARRAY OF CHAR; left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
 BEGIN   
-    rtl := c.rtl();
     RETURN Code.makeSimpleExpression(
             JsString.concat(JsString.concat(
-                rtl.strCmp(castToStr(left, c), castToStr(right, c)),
+                rtl.strCmp(castToStr(left, rtl), castToStr(right, rtl)),
                 JsString.make(op)), 
                 JsString.make("0")),
             Types.basic.bool)
 END strCmp;
 
-PROCEDURE assign*(left, right: Code.PExpression; c: Context.Type): JsString.Type;
+PROCEDURE assign*(left, right: Code.PExpression; rtl: OberonRtl.PType): JsString.Type;
 VAR
     designator: Code.PDesignator;
     info: Types.PId;
@@ -451,13 +456,11 @@ VAR
     leftType, rightType: Types.PType;
     isArray: BOOLEAN;
     castOperation: Cast.PCastOp;
-    rtl: Context.PRtl;
     castExp: Code.PExpression;
+    ignored: BOOLEAN;
     result: JsString.Type;
 
     PROCEDURE assignArrayFromString(a: Types.Array; s: Types.String): JsString.Type;
-    VAR
-        rtl: Context.PRtl;
     BEGIN
         IF Types.arrayLength(a) = Types.openArrayLength THEN
             Errors.raise(JsString.concat(JsString.make("string cannot be assigned to open "),
@@ -469,7 +472,6 @@ VAR
                 JsString.fromInt(Types.stringLen(s))), 
                 JsString.make("-character string")));
         END;
-        rtl := c.rtl();
         RETURN rtl.assignArrayFromString(leftCode, rightCode)
     END assignArrayFromString;
 BEGIN
@@ -491,8 +493,8 @@ BEGIN
         & (rightType IS Types.PString) THEN
         result := assignArrayFromString(leftType(Types.PArray)^, rightType(Types.PString)^);
     ELSE
-        castOperation := Cast.implicit(rightType, leftType, castOperations);
-        IF castOperation = NIL THEN
+        IF Cast.implicit(rightType, leftType, FALSE, castOperations, castOperation)
+            # Cast.errNo THEN;
             Errors.raise(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(JsString.concat(
                 JsString.make("type mismatch: '"), 
                 leftCode),
@@ -512,10 +514,13 @@ BEGIN
                 JsString.make("' and cannot be assigned")));
         END;
         IF isArray OR (rightType IS Types.PRecord) THEN
-            rtl := c.rtl();
             result := rtl.copy(rightCode, leftCode);
         ELSE
-            castExp := castOperation.make(c, Code.derefExpression(right));
+            IF castOperation # NIL THEN
+                castExp := castOperation.make(rtl, Code.derefExpression(right));
+            ELSE
+                castExp := Code.derefExpression(right);
+            END;
             rightCode := castExp.code();
             IF info IS Types.PVariableRef THEN
                 rightCode := JsString.concat(JsString.concat(
@@ -531,7 +536,7 @@ BEGIN
     RETURN result
 END assign;
     
-PROCEDURE inplace(left, right: Code.PExpression; c: Context.Type; code: ARRAY OF CHAR; altOp: BinaryProc): JsString.Type;
+PROCEDURE inplace(left, right: Code.PExpression; rtl: OberonRtl.PType; code: ARRAY OF CHAR; altOp: BinaryProc): JsString.Type;
 VAR
     designator: Code.PDesignator;
     rightExp: Code.PExpression;
@@ -539,7 +544,7 @@ VAR
 BEGIN
     designator := left.designator();
     IF designator.info() IS Types.PVariableRef THEN
-        result := assign(left, altOp(left, right, c), c);
+        result := assign(left, altOp(left, right, rtl), rtl);
     ELSE
         rightExp := Code.derefExpression(right);
         result := JsString.concat(JsString.concat(
@@ -550,103 +555,111 @@ BEGIN
     RETURN result
 END inplace;
 
-PROCEDURE addReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opAddReal, " + ", Precedence.addSub)
+PROCEDURE addReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opAddReal, " + ", Precedence.addSub)
 END addReal;
 
-PROCEDURE addInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryInt(left, right, c, opAddInt, " + ", Precedence.addSub)
+PROCEDURE addInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryInt(left, right, rtl, opAddInt, " + ", Precedence.addSub)
 END addInt;
 
-PROCEDURE subReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opSubReal, " - ", Precedence.addSub)
+PROCEDURE subReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opSubReal, " - ", Precedence.addSub)
 END subReal;
 
-PROCEDURE subInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryInt(left, right, c, opSubInt, " - ", Precedence.addSub)
+PROCEDURE subInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryInt(left, right, rtl, opSubInt, " - ", Precedence.addSub)
 END subInt;
 
-PROCEDURE mulReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opMulReal, " * ", Precedence.mulDivMod)
+PROCEDURE mulReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opMulReal, " * ", Precedence.mulDivMod)
 END mulReal;
 
-PROCEDURE mulInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryInt(left, right, c, opMulInt, " * ", Precedence.mulDivMod)
+PROCEDURE mulInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryInt(left, right, rtl, opMulInt, " * ", Precedence.mulDivMod)
 END mulInt;
 
-PROCEDURE divReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opDivReal, " / ", Precedence.mulDivMod)
+PROCEDURE divReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opDivReal, " / ", Precedence.mulDivMod)
 END divReal;
 
-PROCEDURE divInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryInt(left, right, c, opDivInt, " / ", Precedence.mulDivMod)
+PROCEDURE divInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryInt(left, right, rtl, opDivInt, " / ", Precedence.mulDivMod)
 END divInt;
 
-PROCEDURE mod*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opMod, " % ", Precedence.mulDivMod)
+PROCEDURE mod*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opMod, " % ", Precedence.mulDivMod)
 END mod;
 
-PROCEDURE setUnion*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opSetUnion, " | ", Precedence.bitOr)
+PROCEDURE setUnion*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opSetUnion, " | ", Precedence.bitOr)
 END setUnion;
 
-PROCEDURE setDiff*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opSetDiff, " & ~", Precedence.bitAnd)
+PROCEDURE setDiff*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opSetDiff, " & ~", Precedence.bitAnd)
 END setDiff;
 
-PROCEDURE setIntersection*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opSetIntersection, " & ", Precedence.bitAnd)
+PROCEDURE setIntersection*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opSetIntersection, " & ", Precedence.bitAnd)
 END setIntersection;
 
-PROCEDURE setSymmetricDiff*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opSetSymmetricDiff, " ^ ", Precedence.bitXor)
+PROCEDURE setSymmetricDiff*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opSetSymmetricDiff, " ^ ", Precedence.bitXor)
 END setSymmetricDiff;
 
-PROCEDURE setInclL*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryPred(left, right, c, opSetInclL, codeSetInclL)
+PROCEDURE setInclL*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryPred(left, right, rtl, opSetInclL, codeSetInclL)
 END setInclL;
 
-PROCEDURE setInclR*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryPred(left, right, c, opSetInclR, codeSetInclR)
+PROCEDURE setInclR*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryPred(left, right, rtl, opSetInclR, codeSetInclR)
 END setInclR;
 
-PROCEDURE or*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opOr, " || ", Precedence.or)
+PROCEDURE or*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opOr, " || ", Precedence.or)
 END or;
 
-PROCEDURE and*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opAnd, " && ", Precedence.and)
+PROCEDURE and*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opAnd, " && ", Precedence.and)
 END and;
 
-PROCEDURE equalInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqualInt, " == ", Precedence.equal)
+PROCEDURE equalInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqualInt, " == ", Precedence.equal)
 END equalInt;
 
-PROCEDURE equalReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqualReal, " == ", Precedence.equal)
+PROCEDURE equalReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqualReal, " == ", Precedence.equal)
 END equalReal;
 
-PROCEDURE equalStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" == ", left, right, c)
+PROCEDURE equalSet*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqualSet, " == ", Precedence.equal)
+END equalSet;
+
+PROCEDURE equalStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" == ", left, right, rtl)
 END equalStr;
 
-PROCEDURE notEqualInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opNotEqualInt, " != ", Precedence.equal)
+PROCEDURE notEqualInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opNotEqualInt, " != ", Precedence.equal)
 END notEqualInt;
 
-PROCEDURE notEqualReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opNotEqualReal, " != ", Precedence.equal)
+PROCEDURE notEqualReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opNotEqualReal, " != ", Precedence.equal)
 END notEqualReal;
 
-PROCEDURE notEqualStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" != ", left, right, c)
+PROCEDURE notEqualSet*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opNotEqualSet, " != ", Precedence.equal)
+END notEqualSet;
+
+PROCEDURE notEqualStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" != ", left, right, rtl)
 END notEqualStr;
 
-PROCEDURE is*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE is*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN binaryWithCodeEx(
         left, 
         right, 
-        c, 
+        rtl, 
         NIL, 
         " instanceof ", 
         Precedence.relational, 
@@ -655,92 +668,92 @@ PROCEDURE is*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
         )
 END is;
 
-PROCEDURE lessInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opLessInt, " < ", Precedence.relational)
+PROCEDURE lessInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opLessInt, " < ", Precedence.relational)
 END lessInt;
 
-PROCEDURE lessReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opLessReal, " < ", Precedence.relational)
+PROCEDURE lessReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opLessReal, " < ", Precedence.relational)
 END lessReal;
 
-PROCEDURE lessStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" < ", left, right, c)
+PROCEDURE lessStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" < ", left, right, rtl)
 END lessStr;
 
-PROCEDURE greaterInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opGreaterInt, " > ", Precedence.relational)
+PROCEDURE greaterInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opGreaterInt, " > ", Precedence.relational)
 END greaterInt;
 
-PROCEDURE greaterReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opGreaterReal, " > ", Precedence.relational)
+PROCEDURE greaterReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opGreaterReal, " > ", Precedence.relational)
 END greaterReal;
 
-PROCEDURE greaterStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" > ", left, right, c)
+PROCEDURE greaterStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" > ", left, right, rtl)
 END greaterStr;
 
-PROCEDURE eqLessInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqLessInt, " <= ", Precedence.relational)
+PROCEDURE eqLessInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqLessInt, " <= ", Precedence.relational)
 END eqLessInt;
 
-PROCEDURE eqLessReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqLessReal, " <= ", Precedence.relational)
+PROCEDURE eqLessReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqLessReal, " <= ", Precedence.relational)
 END eqLessReal;
 
-PROCEDURE eqLessStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" <= ", left, right, c)
+PROCEDURE eqLessStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" <= ", left, right, rtl)
 END eqLessStr;
 
-PROCEDURE eqGreaterInt*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqGreaterInt, " >= ", Precedence.relational)
+PROCEDURE eqGreaterInt*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqGreaterInt, " >= ", Precedence.relational)
 END eqGreaterInt;
 
-PROCEDURE eqGreaterReal*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opEqGreaterReal, " >= ", Precedence.relational)
+PROCEDURE eqGreaterReal*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opEqGreaterReal, " >= ", Precedence.relational)
 END eqGreaterReal;
 
-PROCEDURE eqGreaterStr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN strCmp(" >= ", left, right, c)
+PROCEDURE eqGreaterStr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN strCmp(" >= ", left, right, rtl)
 END eqGreaterStr;
 
-PROCEDURE not*(x: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE not*(x: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN unary(x, opNot, "!")
 END not;
 
-PROCEDURE negateInt*(x: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE negateInt*(x: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN promoteToWideIfNeeded(unary(x, opNegateInt, "-"))
 END negateInt;
 
-PROCEDURE negateReal*(x: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE negateReal*(x: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN promoteToWideIfNeeded(unary(x, opNegateReal, "-"))
 END negateReal;
 
-PROCEDURE unaryPlus*(x: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE unaryPlus*(x: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN unary(x, opUnaryPlus, "")
 END unaryPlus;
 
-PROCEDURE setComplement*(x: Code.PExpression; c: Context.Type): Code.PExpression;
+PROCEDURE setComplement*(x: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     RETURN unary(x, opSetComplement, "~")
 END setComplement;
 
-PROCEDURE lsl*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opLsl, " << ", Precedence.shift)
+PROCEDURE lsl*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opLsl, " << ", Precedence.shift)
 END lsl;
 
-PROCEDURE asr*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opAsr, " >> ", Precedence.shift)
+PROCEDURE asr*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opAsr, " >> ", Precedence.shift)
 END asr;
 
-PROCEDURE ror*(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
-    RETURN binaryWithCode(left, right, c, opRor, " >>> ", Precedence.shift)
+PROCEDURE ror*(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
+    RETURN binaryWithCode(left, right, rtl, opRor, " >>> ", Precedence.shift)
 END ror;
 
-PROCEDURE mulInplace*(left, right: Code.PExpression; c: Context.Type): JsString.Type;
-    RETURN inplace(left, right, c, " *= ", mulReal)
+PROCEDURE mulInplace*(left, right: Code.PExpression; rtl: OberonRtl.PType): JsString.Type;
+    RETURN inplace(left, right, rtl, " *= ", mulReal)
 END mulInplace;
 
-PROCEDURE divInplace*(left, right: Code.PExpression; c: Context.Type): JsString.Type;
-    RETURN inplace(left, right, c, " /= ", divReal)
+PROCEDURE divInplace*(left, right: Code.PExpression; rtl: OberonRtl.PType): JsString.Type;
+    RETURN inplace(left, right, rtl, " /= ", divReal)
 END divInplace;
 
 PROCEDURE pow2*(e: Code.PExpression): Code.PExpression;
@@ -777,14 +790,14 @@ PROCEDURE opCastToUint8(left, right: Code.PConst): Code.PConst;
                            * right^(Code.IntConst).value)
 END opCastToUint8;
 
-PROCEDURE CastToUint8.make(context: Context.Type; e: Code.PExpression): Code.PExpression;
+PROCEDURE CastToUint8.make(rtl: OberonRtl.PType; e: Code.PExpression): Code.PExpression;
     RETURN binaryWithCode(
         e, 
         Code.makeExpression(JsString.make("0xFF"), 
                             Types.basic.integer, 
                             NIL, 
                             Code.makeIntConst(0FFH)), 
-        context, 
+        rtl, 
         opCastToUint8, 
         " & ", 
         Precedence.bitAnd)

+ 90 - 76
src/ob/Procedure.ob

@@ -6,6 +6,9 @@ IMPORT
     Errors, 
     JsArray, 
     JsString, 
+    Language,
+    LanguageContext,
+    OberonRtl,
     Object, 
     Operator, 
     Precedence := CodePrecedence, 
@@ -13,7 +16,7 @@ IMPORT
     Types;
 TYPE
     Call* = RECORD
-        PROCEDURE make*(args: JsArray.Type; cx: Context.Type): Code.PExpression
+        PROCEDURE make*(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression
     END;
     PCall = POINTER TO Call;
 
@@ -27,17 +30,17 @@ TYPE
         PROCEDURE end(): Code.PExpression;
 
         args: JsArray.Type;
-        cx: Context.PType;
+        cx: LanguageContext.PType;
         call: PCall
     END;
     PCallGenerator* = POINTER TO CallGenerator;
 
     Impl = RECORD(Types.Procedure)
-        PROCEDURE callGenerator(cx: Context.PType): PCallGenerator
+        PROCEDURE callGenerator(cx: LanguageContext.PType): PCallGenerator
     END;
 
     Type* = RECORD(Types.DefinedProcedure)
-        PROCEDURE callGenerator(cx: Context.PType; id: JsString.Type): PCallGenerator;
+        PROCEDURE callGenerator(cx: LanguageContext.PType; id: JsString.Type): PCallGenerator;
         PROCEDURE define(args: JsArray.Type; result: Types.PType);
 
         mArgs: JsArray.Type;
@@ -63,7 +66,7 @@ TYPE
         cx: Context.PType
     END;
 
-    BinaryOp = PROCEDURE(left, right: Code.PExpression; c: Context.Type): Code.PExpression;
+    BinaryOp = PROCEDURE(left, right: Code.PExpression; rtl: OberonRtl.PType): Code.PExpression;
     BinaryOpStr = PROCEDURE (x, y: JsString.Type): JsString.Type;
 VAR
     predefined*: JsArray.Type;
@@ -72,37 +75,38 @@ PROCEDURE checkArgument(
     actual: Code.PExpression; 
     expected: Types.PProcedureArgument; 
     pos: INTEGER;
-    code: PArgumentsCode
+    code: PArgumentsCode;
+    types: Language.PTypes
     );
 VAR
     actualType, expectType: Types.PType;
     designator: Code.PDesignator;
     info: Types.PId;
     result: Cast.PCastOp;
+    castErr: INTEGER;
 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("'")));
+        castErr := types.implicitCast(actualType, expectType, expected.isVar, Operator.castOperations, result);
+        IF castErr = Cast.errVarParameter 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("'")));
+        ELSIF castErr # Cast.errNo 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;
     END;
     IF expected.isVar THEN
@@ -124,7 +128,12 @@ BEGIN
     END;
 END checkArgument;
 
-PROCEDURE checkArgumentsType(actual: JsArray.Type; expected: JsArray.Type; code: PArgumentsCode);
+PROCEDURE checkArgumentsType(
+    actual: JsArray.Type; 
+    expected: JsArray.Type; 
+    code: PArgumentsCode;
+    types: Language.PTypes
+    );
 VAR
     actualLen: INTEGER;
     i: INTEGER;
@@ -135,7 +144,7 @@ BEGIN
     WHILE i < actualLen DO
         actualExp := JsArray.at(actual, i);
         expectedArg := JsArray.at(expected, i);
-        checkArgument(actualExp(Code.PExpression), expectedArg(Types.PProcedureArgument), i, code);
+        checkArgument(actualExp(Code.PExpression), expectedArg(Types.PProcedureArgument), i, code, types);
         INC(i);
     END;
 END checkArgumentsType;
@@ -150,15 +159,20 @@ BEGIN
     END;
 END checkArgumentsCount;
 
-PROCEDURE processArguments(actual: JsArray.Type; expected: JsArray.Type; code: PArgumentsCode);
+PROCEDURE processArguments(
+    actual: JsArray.Type; 
+    expected: JsArray.Type; 
+    code: PArgumentsCode;
+    types: Language.PTypes
+    );
 BEGIN
     checkArgumentsCount(JsArray.len(actual), JsArray.len(expected));
-    checkArgumentsType(actual, expected, code);
+    checkArgumentsType(actual, expected, code, types);
 END processArguments;
 
-PROCEDURE checkArguments(actual: JsArray.Type; expected: JsArray.Type);
+PROCEDURE checkArguments(actual: JsArray.Type; expected: JsArray.Type; types: Language.PTypes);
 BEGIN
-    processArguments(actual, expected, NIL);
+    processArguments(actual, expected, NIL, types);
 END checkArguments;
 
 PROCEDURE initStd*(name: JsString.Type; call: PCall; result: Std);
@@ -185,7 +199,7 @@ PROCEDURE CallGenerator.end(): Code.PExpression;
     RETURN SELF.call.make(SELF.args, SELF.cx^)
 END CallGenerator.end;
 
-PROCEDURE makeCallGenerator*(call: PCall; cx: Context.PType): PCallGenerator;
+PROCEDURE makeCallGenerator*(call: PCall; cx: LanguageContext.PType): PCallGenerator;
 VAR
     result: PCallGenerator;
 BEGIN
@@ -210,7 +224,7 @@ BEGIN
         SELF.code := JsString.concat(SELF.code, JsString.make(", "));
     END;
     IF cast # NIL THEN
-        e := cast.make(SELF.cx^, actual);
+        e := cast.make(SELF.cx.rtl(), actual);
     ELSE
         e := actual;
     END;
@@ -222,7 +236,7 @@ PROCEDURE GenArgCode.result(): JsString.Type;
 END GenArgCode.result;
 
 PROCEDURE makeProcCallGeneratorWithCustomArgs*(
-    cx: Context.PType; 
+    cx: LanguageContext.PType; 
     id: JsString.Type; 
     type: Types.DefinedProcedure;
     argumentsCode: PArgumentsCode
@@ -237,7 +251,7 @@ TYPE
 VAR
     call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         expectedArgs: JsArray.Type;
         a: Object.PType;
@@ -246,7 +260,7 @@ VAR
     BEGIN
         expectedArgs := SELF.args;
         IF expectedArgs # NIL THEN
-            processArguments(args, expectedArgs, SELF.argumentsCode);
+            processArguments(args, expectedArgs, SELF.argumentsCode, cx.types());
         ELSE
             FOR i := 0 TO JsArray.len(args) - 1 DO
                 a := JsArray.at(args, i);
@@ -282,7 +296,7 @@ BEGIN
 END makeArgumentsCode;
 
 PROCEDURE makeProcCallGenerator*(
-    cx: Context.PType; 
+    cx: LanguageContext.PType; 
     id: JsString.Type; 
     type: Types.DefinedProcedure
     ) : PCallGenerator;
@@ -293,7 +307,7 @@ 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;
+PROCEDURE Std.callGenerator(cx: LanguageContext.PType): PCallGenerator;
     RETURN makeCallGenerator(SELF.call, cx)
 END Std.callGenerator;
 
@@ -350,10 +364,10 @@ BEGIN
     JsArray.add(call.args, a);
 END hasVarArgumnetWithCustomType;
 
-PROCEDURE checkSingleArgument*(actual: JsArray.Type; call: StdCall): Code.PExpression;
+PROCEDURE checkSingleArgument*(actual: JsArray.Type; call: StdCall; types: Language.PTypes): Code.PExpression;
 BEGIN
     ASSERT(JsArray.len(call.args) = 1);
-    checkArguments(actual, call.args);
+    checkArguments(actual, call.args, types);
     ASSERT(JsArray.len(actual) = 1);
     RETURN nthArgument(actual, 0)
 END checkSingleArgument;
@@ -365,13 +379,13 @@ PROCEDURE makeNew(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         argType: Types.PType;
         baseType: Types.PRecord;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         argType := arg.type();
         IF ~(argType IS Types.PPointer) THEN
             Errors.raise(JsString.concat(JsString.concat(
@@ -404,12 +418,12 @@ PROCEDURE makeLen(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         argType: Types.PType;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         argType := arg.type();
         IF ~(argType IS Types.PArray) & ~(argType IS Types.PString) THEN
             Errors.raise(JsString.concat(JsString.concat(
@@ -435,13 +449,13 @@ PROCEDURE makeOdd(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         code: JsString.Type;
         constValue: Code.PConst;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         code := Code.adjustPrecedence(arg, Precedence.bitAnd);
         
         constValue := arg.constValue();
@@ -471,12 +485,12 @@ PROCEDURE makeAssert(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
-        rtl: Context.PRtl;
+        rtl: OberonRtl.PType;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         rtl := cx.rtl();
         RETURN Code.makeSimpleExpression(
             JsString.concat(JsString.concat(JsString.concat(
@@ -502,7 +516,7 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
         yValue: INTEGER;
@@ -511,7 +525,7 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
         valueCode: JsString.Type;
         comment: JsString.Type;
     BEGIN
-        checkArguments(args, SELF.args);
+        checkArguments(args, SELF.args, cx.types());
         ASSERT(JsArray.len(args) = 2);
         x := nthArgument(args, 0);
         y := nthArgument(args, 1);
@@ -524,7 +538,7 @@ PROCEDURE setBitImpl(name: ARRAY OF CHAR; bitOp: BinaryOpStr): Symbols.PSymbol;
                     NIL,
                     Code.makeIntConst(1)), 
                 y,
-                cx);
+                cx.rtl());
             valueCode := valueCodeExp.code();
         ELSE
             yValue := value^(Code.IntConst).value;
@@ -593,7 +607,7 @@ PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr)
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
         code: JsString.Type;
@@ -601,7 +615,7 @@ PROCEDURE incImpl(name: ARRAY OF CHAR; unary: ARRAY OF CHAR; incOp: BinaryOpStr)
         valueCode: JsString.Type;
     BEGIN
         checkVariableArgumentsCount(1, 2, args);
-        checkArgumentsType(args, SELF.args, NIL);
+        checkArgumentsType(args, SELF.args, NIL, cx.types());
         x := nthArgument(args, 0);
         IF JsArray.len(args) = 1 THEN
             code := JsString.concat(SELF.unary, x.code());
@@ -662,12 +676,12 @@ PROCEDURE makeAbs(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         argType: Types.PType;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         argType := arg.type();
         IF ~JsArray.contains(Types.numeric, argType) THEN
             Errors.raise(JsString.concat(JsString.concat(
@@ -696,11 +710,11 @@ PROCEDURE makeFloor(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         RETURN Code.makeSimpleExpression(
             JsString.concat(JsString.concat(
                 JsString.make("Math.floor("), 
@@ -722,12 +736,12 @@ PROCEDURE makeFlt(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         value: Code.PConst;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         value := arg.constValue();
         IF value # NIL THEN
             value := Code.makeRealConst(FLT(value^(Code.IntConst).value));
@@ -755,15 +769,15 @@ PROCEDURE bitShiftImpl(name: ARRAY OF CHAR; op: BinaryOp): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
     BEGIN
-        checkArguments(args, SELF.args);
+        checkArguments(args, SELF.args, cx.types());
         ASSERT(JsArray.len(args) = 2);
         x := nthArgument(args, 0);
         y := nthArgument(args, 1);
-        RETURN SELF.op(x, y, cx)
+        RETURN SELF.op(x, y, cx.rtl())
     END CallImpl.make;
 BEGIN
     NEW(call);
@@ -782,7 +796,7 @@ PROCEDURE makeOrd(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
         argType: Types.PType;
@@ -791,7 +805,7 @@ PROCEDURE makeOrd(): Symbols.PSymbol;
         ch: CHAR;
         result: Code.PExpression;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         argType := arg.type();
         IF (argType = Types.basic.ch) OR (argType = Types.basic.set) THEN
             value := arg.constValue();
@@ -838,11 +852,11 @@ PROCEDURE makeChr(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         arg: Code.PExpression;
     BEGIN
-        arg := checkSingleArgument(args, SELF);
+        arg := checkSingleArgument(args, SELF, cx.types());
         RETURN Code.makeSimpleExpression(arg.code(), Types.basic.ch)
     END CallImpl.make;
 BEGIN
@@ -859,15 +873,15 @@ PROCEDURE makePack(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
     BEGIN
-        checkArguments(args, SELF.args);
+        checkArguments(args, SELF.args, cx.types());
         x := nthArgument(args, 0);
         y := nthArgument(args, 1);
         RETURN Code.makeSimpleExpression(
-            Operator.mulInplace(x, Operator.pow2(y), cx),
+            Operator.mulInplace(x, Operator.pow2(y), cx.rtl()),
             NIL)
     END CallImpl.make;
 BEGIN
@@ -885,18 +899,18 @@ PROCEDURE makeUnpk(): Symbols.PSymbol;
     VAR
         call: POINTER TO CallImpl;
 
-    PROCEDURE CallImpl.make(args: JsArray.Type; cx: Context.Type): Code.PExpression;
+    PROCEDURE CallImpl.make(args: JsArray.Type; cx: LanguageContext.Type): Code.PExpression;
     VAR
         x, y: Code.PExpression;
     BEGIN
-        checkArguments(args, SELF.args);
+        checkArguments(args, SELF.args, cx.types());
         x := nthArgument(args, 0);
         y := nthArgument(args, 1);
         RETURN Code.makeSimpleExpression(
             JsString.concat(JsString.concat(
-                Operator.assign(y, Operator.log2(x), cx),
+                Operator.assign(y, Operator.log2(x), cx.rtl()),
                 JsString.make("; ")),
-                Operator.divInplace(x, Operator.pow2(y), cx)),
+                Operator.divInplace(x, Operator.pow2(y), cx.rtl())),
             NIL)
     END CallImpl.make;
 BEGIN
@@ -956,7 +970,7 @@ BEGIN
     RETURN result
 END Type.description;
 
-PROCEDURE Type.callGenerator(cx: Context.PType; id: JsString.Type): PCallGenerator;
+PROCEDURE Type.callGenerator(cx: LanguageContext.PType; id: JsString.Type): PCallGenerator;
     RETURN makeProcCallGenerator(cx, id, SELF)
 END Type.callGenerator;
 

+ 21 - 18
src/ob/Scope.ob

@@ -1,20 +1,21 @@
 MODULE Scope;
 IMPORT 
-    Context,
     Errors, 
     JsArray, 
     JsMap, 
     JsString, 
     Object, 
     Procedures := Procedure, 
+    ScopeBase,
     Symbols, 
     Types;
 TYPE
-    Type = RECORD(Context.Scope)
+    Type = RECORD(ScopeBase.Type)
         PROCEDURE addSymbol(s: Symbols.PSymbol; exported: BOOLEAN);
         PROCEDURE findSymbol(id: JsString.Type): Symbols.PSymbol;
         PROCEDURE close();
 
+        stdSymbols: JsMap.Type;
         symbols: JsMap.Type;
         unresolved: JsArray.Strings;
         finalizers: JsArray.Type
@@ -38,24 +39,27 @@ TYPE
     PModule = POINTER TO Module;
 
     Finalizer = POINTER TO RECORD (Object.Type)
-        proc: Context.FinalizerProc;
+        proc: ScopeBase.FinalizerProc;
         closure: Object.PType
     END;
+
+PROCEDURE addSymbolForType*(t: Types.PBasicType; result: JsMap.Type);
 VAR
-    stdSymbols: JsMap.Type;
+    name: JsString.Type;
+BEGIN
+    name := Types.typeName(t^);
+    JsMap.put(result, name, Symbols.makeSymbol(name, Types.makeTypeId(t)));
+END addSymbolForType;
 
-PROCEDURE makeStdSymbols(): JsMap.Type;
+PROCEDURE makeStdSymbols*(): JsMap.Type;
 VAR 
     result: JsMap.Type;
     i: INTEGER;
     proc: Object.PType;
 
     PROCEDURE addSymbol(t: Types.PBasicType);
-    VAR
-        name: JsString.Type;
     BEGIN
-        name := Types.typeName(t^);
-        JsMap.put(result, name, Symbols.makeSymbol(name, Types.makeTypeId(t)));
+        addSymbolForType(t, result);
     END addSymbol;
 BEGIN
     result := JsMap.make();
@@ -73,8 +77,9 @@ BEGIN
     RETURN result
 END makeStdSymbols;
 
-PROCEDURE init(scope: Type);
+PROCEDURE init(scope: Type; stdSymbols: JsMap.Type);
 BEGIN
+    scope.stdSymbols := stdSymbols;
     scope.symbols := JsMap.make();
     scope.unresolved := JsArray.makeStrings();
     scope.finalizers := JsArray.make();
@@ -90,12 +95,12 @@ BEGIN
     RETURN result
 END makeCompiledModule;
 
-PROCEDURE makeModule*(name: JsString.Type): PModule;
+PROCEDURE makeModule*(name: JsString.Type; stdSymbols: JsMap.Type): PModule;
 VAR
     result: PModule;
 BEGIN
     NEW(result);
-    init(result^);
+    init(result^, stdSymbols);
     result.exports := JsMap.make();
     result.symbol := Symbols.makeSymbol(name, makeCompiledModule(name));
     result.addSymbol(result.symbol, FALSE);
@@ -153,7 +158,7 @@ BEGIN
     END;
 END Type.close;
 
-PROCEDURE Type.addFinalizer(proc: Context.FinalizerProc; closure: Object.PType);
+PROCEDURE Type.addFinalizer(proc: ScopeBase.FinalizerProc; closure: Object.PType);
 VAR
     f: Finalizer;
 BEGIN
@@ -187,7 +192,7 @@ VAR
     void: BOOLEAN;
 BEGIN
     IF ~JsMap.find(SELF.symbols, id, result) THEN
-        void := JsMap.find(stdSymbols, id, result);
+        void := JsMap.find(SELF.stdSymbols, id, result);
     END;
     RETURN result(Symbols.PSymbol)
 END Type.findSymbol;
@@ -208,12 +213,12 @@ BEGIN
     SUPER(s, exported);
 END Procedure.addSymbol;
 
-PROCEDURE makeProcedure*(): PType;
+PROCEDURE makeProcedure*(stdSymbols: JsMap.Type): PType;
 VAR
     result: POINTER TO Procedure;
 BEGIN
     NEW(result);
-    init(result^);
+    init(result^, stdSymbols);
     RETURN result
 END makeProcedure;
 
@@ -262,6 +267,4 @@ PROCEDURE moduleExports*(m: Module): JsMap.Type;
     RETURN m.exports
 END moduleExports;
 
-BEGIN
-    stdSymbols := makeStdSymbols();
 END Scope.

+ 11 - 0
src/ob/ScopeBase.ob

@@ -0,0 +1,11 @@
+MODULE ScopeBase;
+IMPORT Object;
+TYPE
+    FinalizerProc* = PROCEDURE(closure: Object.PType);
+
+    Type* = RECORD
+        PROCEDURE addFinalizer*(finalizer: FinalizerProc; closure: Object.PType)
+    END;
+    PType* = POINTER TO Type;
+END ScopeBase.
+

+ 5 - 5
src/ob/Symbols.ob

@@ -1,5 +1,5 @@
 MODULE Symbols;
-IMPORT Context, JsString, Object, Types;
+IMPORT JsString, Object, ScopeBase, Types;
 TYPE
     Symbol* = RECORD(Object.Type)
         PROCEDURE id*(): JsString.Type;
@@ -18,10 +18,10 @@ TYPE
 
     FoundSymbol* = RECORD
         PROCEDURE symbol(): PSymbol;
-        PROCEDURE scope(): Context.PScope;
+        PROCEDURE scope(): ScopeBase.PType;
 
         mSymbol: PSymbol;
-        mScope: Context.PScope
+        mScope: ScopeBase.PType
     END;
 
     PFoundSymbol* = POINTER TO FoundSymbol;
@@ -54,7 +54,7 @@ PROCEDURE Symbol.isProcedure(): BOOLEAN;
     RETURN SELF.mInfo IS Types.PProcedureId
 END Symbol.isProcedure;
 
-PROCEDURE FoundSymbol.scope(): Context.PScope;
+PROCEDURE FoundSymbol.scope(): ScopeBase.PType;
     RETURN SELF.mScope
 END FoundSymbol.scope;
 
@@ -72,7 +72,7 @@ BEGIN
     RETURN result
 END makeSymbol;
 
-PROCEDURE makeFound*(s: PSymbol; scope: Context.PScope): PFoundSymbol;
+PROCEDURE makeFound*(s: PSymbol; scope: ScopeBase.PType): PFoundSymbol;
 VAR
     result: PFoundSymbol;
 BEGIN

+ 7 - 7
src/ob/Types.ob

@@ -1,6 +1,6 @@
 MODULE Types;
 IMPORT
-    Context, Errors, JS, JsArray, JsMap, JsString, Object;
+    Context, Errors, JS, JsArray, JsMap, JsString, Object, ScopeBase;
 CONST
     openArrayLength* = 0;
 
@@ -136,7 +136,7 @@ TYPE
         fields: JsMap.Type;
         base:   PRecord;
         cons:   JsString.Type;
-        scope:  Context.PScope;
+        scope:  ScopeBase.PType;
         notExported: JsArray.Strings
     END;
     
@@ -188,7 +188,7 @@ BEGIN
     SELF.notExported := NIL;
 END Record.finalize;
 
-PROCEDURE initRecord*(r: PRecord; name: JsString.Type; cons: JsString.Type; scope: Context.PScope);
+PROCEDURE initRecord*(r: PRecord; name: JsString.Type; cons: JsString.Type; scope: ScopeBase.PType);
 BEGIN
     r.name := name;
     r.cons := cons;
@@ -198,7 +198,7 @@ BEGIN
     scope.addFinalizer(finalizeRecord, r);
 END initRecord;
 
-PROCEDURE makeNonExportedRecord(cons: JsString.Type; scope: Context.PScope; base: PRecord): PNonExportedRecord;
+PROCEDURE makeNonExportedRecord(cons: JsString.Type; scope: ScopeBase.PType; base: PRecord): PNonExportedRecord;
 VAR
     result: PNonExportedRecord;
 BEGIN
@@ -366,7 +366,7 @@ PROCEDURE moduleName*(m: Module): JsString.Type;
     RETURN m.name
 END moduleName;
 
-PROCEDURE makeBasic(name: ARRAY OF CHAR; initializer: ARRAY OF CHAR): PBasicType;
+PROCEDURE makeBasic*(name: ARRAY OF CHAR; initializer: ARRAY OF CHAR): PBasicType;
 VAR
     result: PBasicType;
 BEGIN
@@ -439,7 +439,7 @@ BEGIN
     r.base := type;
 END setRecordBase;
 
-PROCEDURE recordScope*(r: Record): Context.PScope;
+PROCEDURE recordScope*(r: Record): ScopeBase.PType;
     RETURN r.scope
 END recordScope;
 
@@ -627,7 +627,7 @@ BEGIN
     RETURN result
 END makePointer;
 
-PROCEDURE makeRecord*(name: JsString.Type; cons: JsString.Type; scope: Context.PScope): PRecord;
+PROCEDURE makeRecord*(name: JsString.Type; cons: JsString.Type; scope: ScopeBase.PType): PRecord;
 VAR
     result: PRecord;
 BEGIN

+ 1 - 1
src/oberon/oberon_context.js

@@ -22,4 +22,4 @@ var VariableDeclaration = Context.VariableDeclaration.extend({
 });
 
 exports.RecordDecl = RecordDecl;
-exports.VariableDeclaration = VariableDeclaration;
+exports.VariableDeclaration = VariableDeclaration;

+ 21 - 9
src/oberon/oberon_grammar.js

@@ -1,9 +1,11 @@
 "use strict";
 
+var Cast = require("js/Cast.js");
 var Context = require("context.js");
 var Grammar = require("grammar.js");
 var ObContext = require("oberon/oberon_context.js");
 var Parser = require("parser.js");
+var Scope = require("js/Scope.js");
 
 var and = Parser.and;
 var context = Parser.context;
@@ -31,12 +33,22 @@ function makeFieldList(identdef, identList, type){
     return context(and(identList, ":", type), Context.FieldListDeclaration);
 }
 
-exports.grammar = Grammar.make(
-    makeDesignator,
-    makeProcedureHeading,
-    makeProcedureDeclaration,
-    makeFieldList,
-    ObContext.RecordDecl,
-    ObContext.VariableDeclaration,
-    Grammar.reservedWords
-    );
+exports.language = {
+    grammar: Grammar.make(
+            makeDesignator,
+            makeProcedureHeading,
+            makeProcedureDeclaration,
+            makeFieldList,
+            ObContext.RecordDecl,
+            ObContext.VariableDeclaration,
+            Context.AddOperator,
+            Context.Expression,
+            Grammar.reservedWords
+            ),
+    stdSymbols: Scope.makeStdSymbols(),
+    types: {
+        implicitCast: Cast.implicit
+    }
+};
+
+

+ 29 - 10
src/oc.js

@@ -63,18 +63,32 @@ function compileModulesFromText(
 }
 
 var ModuleResolver = Class.extend({
-    init: function Oc$ModuleResolver(compile, handleCompiledModule, moduleReader){
+    init: function Oc$ModuleResolver(compile, handleCompiledModule, moduleReader, handleErrors){
         this.__modules = {};
         this.__compile = compile;
         this.__moduleReader = moduleReader;
         this.__handleCompiledModule = handleCompiledModule;
+        this.__handleErrors = handleErrors;
+        this.__detectRecursion = [];
     },
     compile: function(text){
         this.__compile(text, this.__resolveModule.bind(this), this.__handleModule.bind(this));
     },
     __resolveModule: function(name){
-        if (this.__moduleReader && !this.__modules[name])
-            this.compile(this.__moduleReader(name));
+        if (this.__moduleReader && !this.__modules[name]){
+            if (this.__detectRecursion.indexOf(name) != -1){
+                this.__handleErrors("recursive import: " + this.__detectRecursion.join(" -> "));
+                return undefined;
+            }
+            this.__detectRecursion.push(name);
+
+            try {
+                this.compile(this.__moduleReader(name));
+            }
+            finally {
+                this.__detectRecursion.pop();
+            }
+        }
         return this.__modules[name];
     },
     __handleModule: function(module){
@@ -96,7 +110,8 @@ function makeResolver(grammar, contextFactory, handleCompiledModule, handleError
                                    handleErrors);
         },
         handleCompiledModule,
-        moduleReader
+        moduleReader,
+        handleErrors
         );
 }
 
@@ -105,17 +120,21 @@ function compileModules(names, moduleReader, grammar, contextFactory, handleErro
     names.forEach(function(name){ resolver.compile(moduleReader(name)); });
 }
 
-function compile(text, grammar, handleErrors){
+function compile(text, language, handleErrors){
     var result = "";
     var rtl = new RTL();
     var moduleCode = function(name, imports){return Code.makeModuleGenerator(name, imports);};
     var resolver = makeResolver(
-            grammar,
+            language.grammar,
             function(moduleResolver){
-                return new Context.Context(Code.makeGenerator(),
-                                           moduleCode,
-                                           rtl,
-                                           moduleResolver);
+                return new Context.Context(
+                    { codeGenerator: Code.makeGenerator(),
+                      moduleGenerator: moduleCode,
+                      rtl: rtl,
+                      types: language.types,
+                      stdSymbols: language.stdSymbols,
+                      moduleResolver: moduleResolver
+                    });
             },
             function(name, code){result += code;},
             handleErrors

+ 9 - 3
src/oc_nodejs.js

@@ -4,6 +4,7 @@ var grammar = require("eberon/eberon_grammar.js").grammar;
 var nodejs = require("nodejs.js");
 
 var options = {
+    "--include": "includeDirs",
     "--out-dir": "outDir",
     "--import-dir": "importDir"
 };
@@ -31,13 +32,14 @@ function main(){
     var sources = args.notParsed;
     if (!sources.length){
         console.info("Usage: <oc_nodejs> [options] <input oberon module file(s)>");
-        console.info("options:\n--out-dir=<out dir>\n--import-dir=<import dir>");
+        console.info("options:\n--include=<search directories separated by ';'>\n--out-dir=<out dir>\n--import-dir=<import dir>");
         return -1;
     }
+    var includeDirs = (args.includeDirs && args.includeDirs.split(";")) || [];
     var outDir = args.outDir || ".";
 
     var errors = "";
-    nodejs.compile(sources, grammar, function(e){errors += e;}, outDir, args.importDir);
+    nodejs.compile(sources, grammar, function(e){errors += e + "\n";}, includeDirs, outDir, args.importDir);
     if (errors.length){
         console.error(errors);
         return -2;
@@ -47,4 +49,8 @@ function main(){
     return 0;
 }
 
-process.exit(main());
+// process.exit(main());
+// hack to avoid problem with not flushed stdout on exit: https://github.com/joyent/node/issues/1669
+var rc = main();
+process.stdout.once("drain", function(){process.exit(rc);});
+process.stdout.write("");

+ 15 - 0
test/expected/eberon/string.js

@@ -0,0 +1,15 @@
+var m = function (){
+var s = '';var s1 = '';var s2 = '';
+var b = false;
+
+function p1(a/*ARRAY OF CHAR*/){
+}
+s = s1 + s2;
+b = s1 == s2;
+b = s1 != s2;
+b = s1 < s2;
+b = s1 > s2;
+b = s1 <= s2;
+b = s1 >= s2;
+p1(s);
+}();

+ 19 - 0
test/input/eberon/string.ob

@@ -0,0 +1,19 @@
+MODULE m;
+VAR 
+    s, s1, s2: STRING;
+    b: BOOLEAN;
+
+PROCEDURE p1(a: ARRAY OF CHAR);
+END p1;
+
+BEGIN
+    s := s1 + s2;
+    b := s1 = s2;
+    b := s1 # s2;
+    b := s1 < s2;
+    b := s1 > s2;
+    b := s1 <= s2;
+    b := s1 >= s2;
+
+    p1(s);
+END m.

+ 1 - 1
test/test_compile.cmd

@@ -1,2 +1,2 @@
-SET NODE_PATH=.;%~dp0../src;%~dp0../src/js
+SET NODE_PATH=.;%~dp0../src;%~dp0../src/js;%~dp0../src/eberon
 "C:\Program Files\nodejs\node.exe" test_compile.js %*

+ 20 - 20
test/test_compile.js

@@ -1,9 +1,9 @@
 "use strict";
 
-var nodejs = require("nodejs");
+var nodejs = require("nodejs.js");
 var oc = require("oc");
-var oberonGrammar = require("oberon/oberon_grammar.js").grammar;
-var eberonGrammar = require("eberon/eberon_grammar.js").grammar;
+var oberon = require("oberon/oberon_grammar.js").language;
+var eberon = require("eberon/eberon_grammar.js").language;
 var fs = require("fs");
 var path = require("path");
 var Test = require("test.js");
@@ -20,16 +20,16 @@ function compareResults(result, name, dirs){
         throw new Test.TestError("Failed");
 }
 
-function compile(src, grammar){
+function compile(src, language){
     var text = fs.readFileSync(src, "utf8");
     var errors = "";
-    var result = oc.compile(text, grammar, function(e){errors += e;});
+    var result = oc.compile(text, language, function(e){errors += e;});
     if (errors)
         throw new Test.TestError(errors);
     return result;
 }
 
-function compileNodejs(src, dirs, grammar){
+function compileNodejs(src, dirs, language){
     var subdir = path.basename(src);
     subdir = subdir.substr(0, subdir.length - path.extname(subdir).length);
 
@@ -37,7 +37,7 @@ function compileNodejs(src, dirs, grammar){
     fs.mkdirSync(outDir);
 
     var errors = "";
-    nodejs.compile([src], grammar, function(e){errors += e;}, outDir);
+    nodejs.compile([src], language, function(e){errors += e;}, [], outDir);
     if (errors)
         throw new Test.TestError(errors);
 
@@ -50,11 +50,11 @@ function expectOk(src, dirs, grammar){
     compareResults(result, resultName, dirs);
 }
 
-function expectError(src, dirs, grammar){
+function expectError(src, dirs, language){
     var text = fs.readFileSync(src, "utf8");
     var errors = "";
     try {
-        oc.compile(text, grammar, function(e){errors += e + "\n";});
+        oc.compile(text, language, function(e){errors += e + "\n";});
     }
     catch (e){
         errors += e;
@@ -65,8 +65,8 @@ function expectError(src, dirs, grammar){
     compareResults(errors, resultName, dirs);
 }
 
-function run(src, dirs, grammar){
-    var result = compile(src, grammar);
+function run(src, dirs, language){
+    var result = compile(src, language);
     var resultName = path.basename(src).replace(".ob", ".js");
     var resultPath = path.join(dirs.output, resultName);
     fs.writeFileSync(resultPath, result);
@@ -146,20 +146,20 @@ function main(){
     var eberonDirs = makeTestDirs("eberon");
     var eberonRunDirs = makeTestDirs("eberon/run");
 
-    function makeCommonTests(grammar, subdir){
+    function makeCommonTests(language, subdir){
         return {
-            "expect OK": makeTests(expectOk, outputSubdir(okDirs, subdir), grammar),
-            "expect compile error": makeTests(expectError, outputSubdir(errDirs, subdir), grammar),
-            "run": makeTests(run, outputSubdir(runDirs, subdir), grammar),
-            "nodejs": makeTests(compileNodejs, outputSubdir(nodejsDirs, subdir), grammar)
+            "expect OK": makeTests(expectOk, outputSubdir(okDirs, subdir), language),
+            "expect compile error": makeTests(expectError, outputSubdir(errDirs, subdir), language),
+            "run": makeTests(run, outputSubdir(runDirs, subdir), language),
+            "nodejs": makeTests(compileNodejs, outputSubdir(nodejsDirs, subdir), language)
         };
     }
 
-    Test.run({"common": {"oberon": makeCommonTests(oberonGrammar, "oberon"),
-                         "eberon": makeCommonTests(eberonGrammar, "eberon")
+    Test.run({"common": {"oberon": makeCommonTests(oberon, "oberon"),
+                         "eberon": makeCommonTests(eberon, "eberon")
                         },
-              "eberon": {"expect OK": makeTests(expectOk, eberonDirs, eberonGrammar),
-                         "run": makeTests(run, eberonRunDirs, eberonGrammar)
+              "eberon": {"expect OK": makeTests(expectOk, eberonDirs, eberon),
+                         "run": makeTests(run, eberonRunDirs, eberon)
                         }
              });
 }

+ 1 - 1
test/test_unit.cmd

@@ -1,2 +1,2 @@
-SET NODE_PATH=.;%~dp0../src;%~dp0../src/oberon;%~dp0../src/js
+SET NODE_PATH=.;%~dp0../src;%~dp0../src/oberon;%~dp0../src/eberon;%~dp0../src/js
 "C:\Program Files\nodejs\node.exe" test_unit.js %*

+ 36 - 22
test/test_unit.js

@@ -9,24 +9,27 @@ var TestUnitCommon = require("test_unit_common.js");
 var TestUnitEberon = require("test_unit_eberon.js");
 var TestUnitOberon = require("test_unit_oberon.js");
 
-var eberonGrammar = require("eberon/eberon_grammar.js").grammar;
-var oberonGrammar = require("oberon/oberon_grammar.js").grammar;
+var eberon = require("eberon/eberon_grammar.js").language;
+var oberon = require("oberon/oberon_grammar.js").language;
 
 var context = TestUnitCommon.context;
 var pass = TestUnitCommon.pass;
 var fail = TestUnitCommon.fail;
 var setupParser = TestUnitCommon.setupParser;
-var testWithGrammar = TestUnitCommon.testWithGrammar;
 var testWithSetup = TestUnitCommon.testWithSetup;
 
-function makeSuiteForGrammar(grammar){
-
+function makeSuiteForGrammar(language){
+    var grammar = language.grammar;
     function testWithContext(context, pass, fail){
-        return TestUnitCommon.testWithContext(context, grammar.declarationSequence, pass, fail);
+        return TestUnitCommon.testWithContext(context, grammar.declarationSequence, language, pass, fail);
     }
 
     function testWithModule(src, pass, fail){
-        return TestUnitCommon.testWithModule(src, grammar, pass, fail);
+        return TestUnitCommon.testWithModule(src, language, pass, fail);
+    }
+
+    function testWithGrammar(parser, pass, faile){
+        return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
     }
 
 return {
@@ -102,7 +105,7 @@ return {
         });
         function makeContext() {return new IdentDeclarationContext();}
 
-        return setupParser(grammar.ident, makeContext);},
+        return setupParser(grammar.ident, language, makeContext);},
     pass("i", "abc1"),
     fail(["", "not parsed"],
          [";", "not parsed"],
@@ -261,9 +264,9 @@ return {
          "pd # pb"
          ),
     fail(["p1 < p2", "operator '<' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
-         ["p1 <= p2", "operator '<=' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
+         ["p1 <= p2", "operator '<=' type mismatch: numeric type or SET or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
          ["p1 > p2", "operator '>' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
-         ["p1 >= p2", "operator '>=' type mismatch: numeric type or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
+         ["p1 >= p2", "operator '>=' type mismatch: numeric type or SET or CHAR or character array expected, got 'POINTER TO anonymous RECORD'"],
          ["p1 = pb", "type mismatch: expected 'POINTER TO anonymous RECORD', got 'POINTER TO B'"]
          )
     ),
@@ -296,9 +299,11 @@ return {
     ),
 "BYTE": testWithContext(
     context(grammar.statement,
-              "VAR b1, b2: BYTE; i: INTEGER; set: SET; a: ARRAY 3 OF BYTE;"
+              "VAR b1, b2: BYTE; i: INTEGER; set: SET; a: ARRAY 3 OF BYTE; ai: ARRAY 3 OF INTEGER;"
             + "PROCEDURE varIntParam(VAR i: INTEGER); END varIntParam;"
             + "PROCEDURE varByteParam(VAR b: BYTE); END varByteParam;"
+            + "PROCEDURE arrayParam(b: ARRAY OF BYTE); END arrayParam;"
+            + "PROCEDURE arrayIntParam(i: ARRAY OF INTEGER); END arrayIntParam;"
             ),
     pass("b1 := b2",
          "i := b1",
@@ -317,11 +322,15 @@ return {
          "b1 := i - b1",
          "i := b1 * i",
          "i := -b1",
-         "i := +b1"
+         "i := +b1",
+         "arrayParam(a)",
+         "arrayIntParam(ai)"
         ),
     fail(["i := b1 / i", "operator DIV expected for integer division"],
          ["varIntParam(b1)", "type mismatch for argument 1: cannot pass 'BYTE' as VAR parameter of type 'INTEGER'"],
-         ["varByteParam(i)", "type mismatch for argument 1: cannot pass 'INTEGER' as VAR parameter of type 'BYTE'"]
+         ["varByteParam(i)", "type mismatch for argument 1: cannot pass 'INTEGER' as VAR parameter of type 'BYTE'"],
+         ["arrayParam(ai)", "type mismatch for argument 1: 'ARRAY 3 OF INTEGER' cannot be converted to 'ARRAY OF BYTE'"],
+         ["arrayIntParam(a)", "type mismatch for argument 1: 'ARRAY 3 OF BYTE' cannot be converted to 'ARRAY OF INTEGER'"]
         )
     ),
 "NEW": testWithContext(
@@ -674,12 +683,11 @@ return {
          "r1 := r1 / r2"),
     fail(["i1 := i1 / i2", "operator DIV expected for integer division"],
          ["r1 := r1 DIV r1", "operator 'DIV' type mismatch: 'INTEGER' or 'BYTE' expected, got 'REAL'"],
-         ["b1 := b1 + b1", "operator '+' type mismatch: numeric type expected, got 'BOOLEAN'"],
-         ["c1 := c1 - c1", "operator '-' type mismatch: numeric type expected, got 'CHAR'"],
-         ["p1 := p1 * p1", "operator '*' type mismatch: numeric type expected, got 'PROCEDURE'"],
-         ["ptr1 := ptr1 / ptr1", "operator '/' type mismatch: numeric type expected, got 'POINTER TO anonymous RECORD'"],
+         ["c1 := c1 - c1", "operator '-' type mismatch: numeric type or SET expected, got 'CHAR'"],
+         ["p1 := p1 * p1", "operator '*' type mismatch: numeric type or SET expected, got 'PROCEDURE'"],
+         ["ptr1 := ptr1 / ptr1", "operator '/' type mismatch: numeric type or SET expected, got 'POINTER TO anonymous RECORD'"],
          ["s1 := +s1", "operator '+' type mismatch: numeric type expected, got 'SET'"],
-         ["b1 := -b1", "operator '-' type mismatch: numeric type expected, got 'BOOLEAN'"],
+         ["b1 := -b1", "operator '-' type mismatch: numeric type or SET expected, got 'BOOLEAN'"],
          ["s1 := +b1", "operator '+' type mismatch: numeric type expected, got 'BOOLEAN'"])
     ),
 "relations are BOOLEAN": testWithContext(
@@ -701,15 +709,21 @@ return {
     ),
 "SET relations": testWithContext(
     context(grammar.expression,
-            "VAR set1, set2: SET; b: BOOLEAN; i: INTEGER;"),
+            "CONST constSet1 = {}; constSet2 = {};"
+            + "VAR set1, set2: SET; b: BOOLEAN; i: INTEGER;"),
     pass("set1 <= set2",
          "set1 >= set2",
          "set1 = set2",
          "set1 # set2",
+         "constSet1 = constSet2",
+         "constSet1 # constSet2",
          "i IN set1"),
     fail(["set1 <= i", "type mismatch: expected 'SET', got 'INTEGER'"],
          ["b IN set1", "'INTEGER' or 'BYTE' expected as an element of SET, got 'BOOLEAN'"],
-         ["i IN b", "type mismatch: expected 'SET', got 'BOOLEAN'"])
+         ["i IN b", "type mismatch: expected 'SET', got 'BOOLEAN'"],
+         ["set1 < set2", "operator '<' type mismatch: numeric type or CHAR or character array expected, got 'SET'"],
+         ["set1 > set2", "operator '>' type mismatch: numeric type or CHAR or character array expected, got 'SET'"]
+         )
     ),
 "SET operators": testWithContext(
     context(grammar.expression,
@@ -1365,8 +1379,8 @@ return {
 
 Test.run({
     "common": {
-        "oberon": makeSuiteForGrammar(oberonGrammar),
-        "eberon": makeSuiteForGrammar(eberonGrammar)
+        "oberon": makeSuiteForGrammar(oberon),
+        "eberon": makeSuiteForGrammar(eberon)
     },
     "eberon": TestUnitEberon.suite,
     "oberon": TestUnitOberon.suite

+ 27 - 24
test/test_unit_common.js

@@ -27,18 +27,21 @@ var TestModuleGenerator = Class.extend({
 });
 
 var TestContext = Context.Context.extend({
-    init: function TestContext(){
+    init: function TestContext(language){
         Context.Context.prototype.init.call(
                 this,
-                Code.nullGenerator(),
-                function(){return new TestModuleGenerator();},
-                new RTL());
-        this.pushScope(Scope.makeModule("test"));
+                { codeGenerator: Code.nullGenerator(),
+                  moduleGenerator: function(){return new TestModuleGenerator();},
+                  rtl: new RTL(),
+                  types: language.types,
+                  stdSymbols: language.stdSymbols
+                });
+        this.pushScope(Scope.makeModule("test", language.stdSymbols));
     },
     qualifyScope: function(){return "";}
 });
 
-function makeContext(){return new TestContext();}
+function makeContext(language){return new TestContext(language);}
 
 function testWithSetup(setup, pass, fail){
     return function(){
@@ -101,23 +104,23 @@ function setup(run){
     };
 }
 
-function parseUsingGrammar(grammar, s, cxFactory){
-    var baseContext = makeContext();
+function parseUsingGrammar(parser, language, s, cxFactory){
+    var baseContext = makeContext(language);
     var context = cxFactory ? cxFactory(baseContext) : baseContext;
-    parseInContext(grammar, s, context);
+    parseInContext(parser, s, context);
     context.currentScope().close();
 }
 
-function setupParser(parser, contextFactory){
+function setupParser(parser, language, contextFactory){
     function parseImpl(s){
-        return parseUsingGrammar(parser, s, contextFactory);
+        return parseUsingGrammar(parser, language, s, contextFactory);
     }
     return setup(parseImpl);
 }
 
-function setupWithContext(grammar, contextGrammar, source){
+function setupWithContext(grammar, contextGrammar, language, source){
     function innerMakeContext(){
-        var context = makeContext();
+        var context = makeContext(language);
         try {
             parseInContext(contextGrammar, source, context);
         }
@@ -129,40 +132,41 @@ function setupWithContext(grammar, contextGrammar, source){
         return context;
     }
 
-    return setupParser(grammar, innerMakeContext);
+    return setupParser(grammar, language, innerMakeContext);
 }
 
-function testWithContext(context, contextGrammar, pass, fail){
+function testWithContext(context, contextGrammar, language, pass, fail){
     return testWithSetup(
-        function(){return setupWithContext(context.grammar, contextGrammar, context.source);},
+        function(){return setupWithContext(context.grammar, contextGrammar, language, context.source);},
         pass,
         fail);
 }
 
-function testWithGrammar(grammar, pass, fail){
+function testWithGrammar(parser, language, pass, fail){
     return testWithSetup(
-        function(){return setupParser(grammar);},
+        function(){return setupParser(parser, language);},
         pass,
         fail);
 }
 
 var TestContextWithModule = TestContext.extend({
-    init: function(module){
-        TestContext.prototype.init.call(this);
+    init: function(module, language){
+        TestContext.prototype.init.call(this, language);
         this.__module = module;
     },
     findModule: function(){return this.__module;}
 });
 
-function testWithModule(src, grammar, pass, fail){
+function testWithModule(src, language, pass, fail){
+    var grammar = language.grammar;
     return testWithSetup(
         function(){
-            var imported = oc.compileModule(grammar, Stream.make(src), makeContext());
+            var imported = oc.compileModule(grammar, Stream.make(src), makeContext(language));
             var module = imported.symbol().info();
             return setup(function(s){
                 oc.compileModule(grammar,
                                  Stream.make(s),
-                                 new TestContextWithModule(module));
+                                 new TestContextWithModule(module, language));
             });},
         pass,
         fail);
@@ -171,7 +175,6 @@ function testWithModule(src, grammar, pass, fail){
 exports.context = context;
 exports.pass = pass;
 exports.fail = fail;
-exports.makeContext = makeContext;
 exports.setupParser = setupParser;
 exports.testWithContext = testWithContext;
 exports.testWithGrammar = testWithGrammar;

+ 46 - 5
test/test_unit_eberon.js

@@ -1,26 +1,39 @@
 "use strict";
 
-var grammar = require("eberon/eberon_grammar.js").grammar;
+var language = require("eberon/eberon_grammar.js").language;
 var TestUnitCommon = require("test_unit_common.js");
 
 var pass = TestUnitCommon.pass;
 var fail = TestUnitCommon.fail;
 var context = TestUnitCommon.context;
 
+var grammar = language.grammar;
+
 function testWithContext(context, pass, fail){
-    return TestUnitCommon.testWithContext(context, grammar.declarationSequence, pass, fail);
+    return TestUnitCommon.testWithContext(context, grammar.declarationSequence, language, pass, fail);
 }
 
 function testWithModule(src, pass, fail){
-    return TestUnitCommon.testWithModule(src, grammar, pass, fail);
+    return TestUnitCommon.testWithModule(src, language, pass, fail);
+}
+
+function testWithGrammar(parser, pass, faile){
+    return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
 }
 
 exports.suite = {
-"key words": TestUnitCommon.testWithGrammar(
+"arithmetic operators": testWithContext(
+    context(grammar.statement, "VAR b1: BOOLEAN;"),
+    pass(),
+    fail(["b1 := b1 + b1", "operator '+' type mismatch: numeric type or SET or STRING expected, got 'BOOLEAN'"])
+    ),
+"key words": testWithGrammar(
     grammar.variableDeclaration,
     pass(),
     fail(["SELF: INTEGER", "not parsed"],
-         ["SUPER: INTEGER", "not parsed"])
+         ["SUPER: INTEGER", "not parsed"],
+         ["STRING: INTEGER", "not parsed"]
+         )
     ),
 "abstract method declaration": testWithContext(
     context(grammar.declarationSequence, 
@@ -157,4 +170,32 @@ exports.suite = {
          "VAR a*: A;"),
     fail()
     ),
+"STRING variable": testWithGrammar(
+    grammar.variableDeclaration,
+    pass("s: STRING")
+    ),
+"STRING expression": testWithContext(
+    context(grammar.expression,
+            "VAR s1, s2: STRING; a: ARRAY 10 OF CHAR;"),
+    pass("s1 + s2",
+         "s1 = s2",
+         "s1 # s2",
+         "s1 < s2",
+         "s1 > s2",
+         "s1 <= s2",
+         "s1 >= s2"
+         ),
+    fail(["s1 = NIL", "type mismatch: expected 'STRING', got 'NIL'"],
+         ["s1 = a", "type mismatch: expected 'STRING', got 'ARRAY 10 OF CHAR'"],
+         ["a = s1", "type mismatch: expected 'ARRAY 10 OF CHAR', got 'STRING'"]
+        )
+    ),
+"STRING can be implicitely converted to open ARRAY OF CHAR": testWithContext(
+    context(grammar.expression,
+            "VAR s: STRING;"
+            + "PROCEDURE p(a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END p;"
+            + "PROCEDURE pVar(VAR a: ARRAY OF CHAR): BOOLEAN; RETURN FALSE END pVar;"),
+    pass("p(s)"),
+    fail(["pVar(s)", "type mismatch for argument 1: cannot pass 'STRING' as VAR parameter of type 'ARRAY OF CHAR'"])
+    )
 };

+ 28 - 2
test/test_unit_oberon.js

@@ -1,13 +1,28 @@
 "use strict";
 
-var grammar = require("oberon/oberon_grammar.js").grammar;
+var language = require("oberon/oberon_grammar.js").language;
 var TestUnitCommon = require("test_unit_common.js");
 
 var pass = TestUnitCommon.pass;
 var fail = TestUnitCommon.fail;
-var testWithGrammar = TestUnitCommon.testWithGrammar;
+var context = TestUnitCommon.context;
+
+function testWithContext(context, pass, fail){
+    return TestUnitCommon.testWithContext(context, grammar.declarationSequence, language, pass, fail);
+}
+
+function testWithGrammar(parser, pass, fail){
+    return TestUnitCommon.testWithGrammar(parser, language, pass, fail);
+}
+
+var grammar = language.grammar;
 
 exports.suite = {
+"arithmetic operators": testWithContext(
+    context(grammar.statement, "VAR b1: BOOLEAN;"),
+    pass(),
+    fail(["b1 := b1 + b1", "operator '+' type mismatch: numeric type or SET expected, got 'BOOLEAN'"])
+    ),
 "scalar variables cannot be exported": testWithGrammar(
     grammar.declarationSequence,
     pass(),
@@ -16,5 +31,16 @@ exports.suite = {
          ["VAR a*: ARRAY 5 OF INTEGER;",
           "variable 'a' cannot be exported: only scalar variables can be exported"]
         )
+    ),
+"eberon key words can be identifiers": testWithGrammar(
+    grammar.variableDeclaration,
+    pass("SELF: INTEGER",
+         "SUPER: INTEGER"
+         )
+    ),
+"eberon types are missing": testWithGrammar(
+    grammar.variableDeclaration,
+    pass(),
+    fail(["s: STRING", "undeclared identifier: 'STRING'"])
     )
 };